From my last post where I made a slideshow promo video, I realised how powerful Pillow is. I still have a fair bit of automation to do, so another thing I do a lot of is motivational quotes for Twitter (I am aware they are fucking stupid, but they share well)
The breakdown of the components are a background with a filter, the quote, who said it and our logo.
The breakdown of the components are a background with a filter, the quote, who said it and our logo.
Background and Filter
To start off I am going to create a folder called quote and add in an image and just name it background.jpg (sourced from Pixabay). Next I import PIL and open the file.
from PIL import Image import os, errno cwd = os.getcwd() image_dir = os.path.join(cwd, 'quote') def filterImage(imageFile): im = Image.open(imageFile) im.show() filterImage(os.path.join(image_dir, 'background.jpg'))
Next I need to crop the image. I am going to take the centre portion out of the image to make square. Like so:
So I start off with getting the dimensions of the image with:
im.size
I get a tuple of (1920, 1080), so my square is going to by the height of my image squared, so I will add that as a variable.
cropDim = im.size[1]
Crop is a rectangular region from this image. The box is a 4-tuple defining the left, upper, right, and lower pixel coordinate. Upper is 0, and lower is my cropDim. To find the left and right I half the width (im.size[0] / 2) to give me the centre and then I minus half the cropDim for the left and add for the right.
def filterImage(imageFile): ... cropDim = im.size[1] left = (im.size[0] / 2) - (cropDim / 2) right = (im.size[0] / 2) + (cropDim / 2) im = im.crop((left, 0, right, cropDim)) im.show()
Awesome, now to create the filter I am going to create a new image with an alpha mask 'RGBA', fill it with a colour and set a transparency. then paste onto the image. The image, the filter and then the paste in show in the screenshot below from left to right.
def filterImage(imageFile): ... filter = Image.new("RGBA", im.size, (62, 39, 35, 127)) im.paste(filter, None, filter)
Apart from fiddling about with the filter, this looks good.
Quote
From here going to some text now. First going to import ImageDraw and ImageFont modules
from PIL import Image, ImageFont, ImageDraw
Next I am going pass a quote and author tuple as an argument in the function. Now going to import my font, which I have just downloaded from Google Fonts and saved into the directory, and create a draw object from the image. This will allow me to draw things on the image, in this case the quote. I position it 10px from the left and half way up, i.e. cropDim / 2, this is as x, y coordinates. Finally I use draw.text and pass my x, y, the text, the colour (in this case white) and then the font.
def filterImage(imageFile, quote): ... quoteText = quote[0] quoteAuthor = quote[1] font = ImageFont.truetype('Lato-Regular.ttf', 48) draw = ImageDraw.Draw(im) x, y = 10, cropDim / 2 draw.text((x, y), quoteText, 'white', font) im.show() filterImage(os.path.join(image_dir, 'background.jpg'), ('The world is a book and those who do not travel read only one page', 'Augustine of Hippo'))
So, problem. The text is obviously falling of the edge of the image therefore will have to wrap the string. For this I have import the python module textwrap. This will just give me the text as a list of string as X length passed in the variable. I change quoteText to the wrap function and then loop through run draw.text each time and adding to the y co-ordinate each time. In addition have made the font bigger and move the text up.
quoteText = textwrap.wrap(quote[0], width=20) quoteAuthor = quote[1] font = ImageFont.truetype('Lato-Regular.ttf', 96) draw = ImageDraw.Draw(im) x, y = 20, cropDim / 8 for text in quoteText: draw.text((x, y), text, 'white', font) y = y + 90
Still some niggles but will alter a bit later. Next I am going to add the same font but smaller and use the draw.text on the Author a bit further down and slightly indented.
fontAuthor = ImageFont.truetype('Lato-Regular.ttf', 30) y = y + 100x = x + 30draw.text((x, y), quoteAuthor, 'white', fontAuthor) im.show()
Logo
For the last bit I am going to add the logo, for this I will use the paste again. I open my image which is in the same directory, then resize to 144 square. I then position as the height - height of the image - margin for vertical, and then half the width - half the logo width for the horizontal.
def filterImage(imageFile, quote): ... iconImage = Image.open('icon.png') iconDim = 100 iconImage.thumbnail((iconDim, iconDim)) iconPositionTop = cropDim - iconDim - 100 iconPositionLeft = (cropDim / 2) - (iconDim / 2) im.paste(iconImage, (iconPositionLeft, iconPositionTop))
Finally one more draw.text for the username.
Alterations
A good first attempt but needs some alterations. First of is the fact the font size is explicit, so I am gonna change the larger font to 1/12 of the image size and the small to 1/30. Also the addition of the y axis in the textwrap loop is at a proportion of 1/10 also. This way when different size images come in then it will adjust accordingly.
Second, the filter I have changed to (82, 72, 63, 170). A slightly more sepia type tone.
Also I have added an if statement that if the textwrap is more that 8 lines then to go for a font half the size.
I changed the font to permanent marker in Google Fonts.
And most important added the save function.
from PIL import Image, ImageFont, ImageDraw import os, errno import textwrap cwd = os.getcwd() image_dir = os.path.join(cwd, 'quote') def filterImage(imageFile, quote): # Open Image im = Image.open(imageFile) # cropDim will be the standard unit over the process cropDim = im.size[1] # Crop image left = (im.size[0] / 2) - (cropDim / 2) right = (im.size[0] / 2) + (cropDim / 2) im = im.crop((left, 0, right, cropDim)) # Add a filter of a transparent brown filter = Image.new("RGBA", im.size, (82, 72, 63, 170)) im.paste(filter, None, filter) # Get text as a list 20 characters long quoteText = textwrap.wrap(quote[0], width=20) quoteAuthor = "-" + quote[1] # If quote is particularly long then use half the size font if len(quoteText) > 8: font = ImageFont.truetype('PermanentMarker-Regular.ttf', cropDim / 24) else: font = ImageFont.truetype('PermanentMarker-Regular.ttf', cropDim / 12) # Draw Quote and author draw = ImageDraw.Draw(im) x, y = cropDim / 54 , cropDim / 10 for text in quoteText: draw.text((x, y), text, 'white', font) y = y + cropDim / 10 fontAuthor = ImageFont.truetype('Lato-Regular.ttf', cropDim / 30) y = y + 100 x = x + 30 draw.text((x, y), quoteAuthor, 'white', fontAuthor) # Place icon and username iconImage = Image.open('icon.png') iconDim = 100 iconImage.thumbnail((iconDim, iconDim)) iconPositionTop = cropDim - iconDim - 100 iconPositionLeft = (cropDim / 2) - (iconDim / 2) im.paste(iconImage, (iconPositionLeft, iconPositionTop)) fontUser = ImageFont.truetype('Lato-Regular.ttf', 30) draw.text((iconPositionLeft - 40, cropDim - 70), '@travistaapp', 'white', fontUser) im.show() im.save(os.path.join(image_dir, 'quote_image.png'), 'PNG') filterImage(os.path.join(image_dir, 'background.jpg'),('The world is a book and those who do not travel read only one page', 'Augustine of Hippo'))
Great !!!
ReplyDelete