Date: 2021.07.11 | art | labeled-intentions | pil | python |
DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)
Labeled Intentions is my newest art project which explores the meaning of labels. It does this by creating text labels (which we'll be talking about here) and then producing shirts with those labels on them (which we won't talk about here though you can read how I do this in: How I make and sell clothes online).
I chose Python over a traditional image editor (like GIMP, Inkscape, Photoshop, etc.) for this project because I saw that it would be way more efficient. Labeled Intentions is a set of several labels, each requiring several outputs. Doing this by hand, with an image editor, would require me to do the same operation multiple times. Plus if I had to change something (the font type, the size, the background) etc. it would require lots of changes to all the different designs.
With code, I can just set new rules and make the computer regenerate everything to the new specs. I wanted to reduce the cost of iteration so I chose code over a traditional image editor.
For those that don't know PIL is an image manipulation library for Python. There are many image manipulation libraries out there but I like PIL because:
Essentially the code flows like this:
Here's the source code.
Note: I run this snippet of code in a generation framework I created so there are a few imports that won't make sense by itself but this still has all the important logic for how I created this. generate_async returns a list of images which I then save using PIL.
File: LabeledIntentionsGenerationRunner.py
rom typing import Any, List
from PIL import Image
from Domain.GenerationRunners.IGenerationRunner import IGenerationRunner
from Domain.GenerationRunners.IGenerationResult import IGenerationResult
from Domain.GenerationRunners.GenerationResult import GenerationResult
from Domain.Models.Point import Point
from Domain.Models.RGBA import RGBA
from Domain.Utilities.TextUtilities import draw_centered_text_on_image
class LabeledIntentionsGenerationRunner(IGenerationRunner):
image_size_px: int = 2000
font_path: str = './resources/HelveticaNeue-Bold.ttf'
font_size: int = 200
def __init__(self):
self.labeled_intentions: List[str] = [
'HAMBLOH',
'LABEL',
'INTENTION',
# NY
'HEALING',
'!DEAD',
'THRIVING',
# Purpose
'ART',
'BUSINESS',
'PLEASURE',
# Color
'BLACK',
'RED',
'WHITE'
]
async def generate_async(
self,
) -> List[IGenerationResult]:
all_results: List[IGenerationResult] = []
transparent = RGBA((0,0,0), 0)
black = RGBA((0,0,0))
white = RGBA((255, 255, 255))
center_point = Point(self.image_size_px / 2, self.image_size_px /2)
for label in self.labeled_intentions:
transparent_image = Image.new(
'RGBA',
(LabeledIntentionsGenerationRunner.image_size_px, LabeledIntentionsGenerationRunner.image_size_px),
transparent.to_tuple_alpha())
black_image = Image.new(
'RGBA',
(LabeledIntentionsGenerationRunner.image_size_px, LabeledIntentionsGenerationRunner.image_size_px),
black.to_tuple_alpha())
white_image = Image.new(
'RGBA',
(LabeledIntentionsGenerationRunner.image_size_px, LabeledIntentionsGenerationRunner.image_size_px),
white.to_tuple_alpha())
transparent_words_image = draw_centered_text_on_image(
transparent_image,
center_point,
LabeledIntentionsGenerationRunner._get_hambloh_string_from_label(label),
LabeledIntentionsGenerationRunner.font_path,
LabeledIntentionsGenerationRunner.font_size,
white
)
black_words_image = draw_centered_text_on_image(
black_image,
center_point,
LabeledIntentionsGenerationRunner._get_hambloh_string_from_label(label),
LabeledIntentionsGenerationRunner.font_path,
LabeledIntentionsGenerationRunner.font_size,
white
)
white_words_image = draw_centered_text_on_image(
white_image,
center_point,
LabeledIntentionsGenerationRunner._get_hambloh_string_from_label(label),
LabeledIntentionsGenerationRunner.font_path,
LabeledIntentionsGenerationRunner.font_size,
black
)
all_results.append(
GenerationResult(
LabeledIntentionsGenerationRunner._get_file_name(label, 'transparent'),
transparent_words_image
)
)
all_results.append(
GenerationResult(
LabeledIntentionsGenerationRunner._get_file_name(label, 'white'),
black_words_image
)
)
all_results.append(
GenerationResult(
LabeledIntentionsGenerationRunner._get_file_name(label, 'black'),
white_words_image
)
)
return all_results
@staticmethod
def _get_file_name(
label: str,
descriptor: str
) -> str:
return f'{label}-{descriptor}'
@staticmethod
def _get_hambloh_string_from_label(
label: str
) -> str:
return f'\"{label}\"'
File: TextUtilities.py
from Domain.Models.Point import Point
from Domain.Models.RGBA import RGBA
from PIL import Image, ImageDraw, ImageFont
def draw_centered_text_on_image(
image: Image,
center_point: Point,
text: str,
font_path: str,
font_size: int,
color_rgb: RGBA
) -> Image:
"""
Draws centered text on image
"""
text_image = Image.new('RGBA', image.size, (255,255,255,0))
font = ImageFont.truetype(
font=font_path,
size=font_size,
)
image_draw = ImageDraw.Draw(text_image)
text_width, text_height = image_draw.textsize(text, font)
draw_point = Point(
(center_point.x - (text_width / 2)),
(center_point.y - (text_height / 2))
)
image_draw.text(
draw_point.to_tuple_int(),
text,
font=font,
fill=color_rgb.to_bgr_tuple_alpha()
)
out_image = Image.alpha_composite(image, text_image)
return out_image
How I save images:
async def export_image_async(
self,
original_image: Image,
image_name: str
) -> None:
print('exporting image')
if not os.path.isdir(self.folder_path):
os.mkdir(self.folder_path)
save_file_path = FileImageExporter._construct_output_file_path(
self.folder_path,
image_name,
self._get_file_ending_for_file_image_type(self.file_image_type)
)
original_image.save(save_file_path)
print(f'image saved to: {save_file_path}')
[] Let me know what you create in the comments below!
Happy generating!
-HAMY.OUT
The best way to support my work is to like / comment / share for the algorithm and subscribe for future updates.