# Batch Image Manipulation with Python: Famous quotes embedded on free stock images

So recently I’ve been wanting to create a years worth of images with quotes from famous people and then schedule them on various social media for a few different pet projects of mine. Please note that I’m not at all a professional developer, so experienced developers, please brace yourselves.

This post will provide a working example of a very simple, yet powerful enough Python image batch script using Pillow, that iterate through stock images, and combine them with famous quotes and authors from CSV files.

At the end of this tutorial, you can generate hundreds or thousands of unique images with added quotes and their respective authors. I also add support for embedding custom graphics and or optional text for company logos or websites, so you can further customize to your needs.

And last but not least I show off the opacity function for overlays, separating the quote texts from the backgrounds.

What you will end up with, could be something like this.

I might update the code later with an added transparent image overlay containing a few swirls and lines to give the image a bit more panache, but the main purpose is to show you how to generate images in batch, not give a lesson in design.

As of writing, there are some issues with the functionality of the code, such as some text being incorrectly padded like this:

Another quite big issue is the fact that the first line is set to simply just use a fixed width, meaning that for quotes particularly, oftentimes it just looks wrong, such as this one:

As I’m not using this script for anything, I doubt I’ll get around to updating it, but I mention it as I hope someone might comment with a quick solution.

It should also be noted that I by no means encourage you to just use this code as I am neither a professional developer, nor am I read up on any theory regarding best practices. (Read: the code is most likely horrible)

So, first we need an installation of Python, I use the lastest version. So create a folder named after you liking, this folder will contain the entire script, including folders for images and CSV files.

Create your main script, name it whatever you like, I will just refer to this as:

app.py

import PIL
import csv
import textwrap
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw

First few lines are importing and assigning the libraries we need, namely Pillow, csv and textwrap. If you are new to Python and the above lines are throwing an error, look into package managers, and install the needed modules.

The way I have structured things in this script are as follows:

The authors and quotes are split up for my own benefit, as I originally wrote this script for movie quotes where I needed to link various data from 2 separate files. I’ve left it like this, since it might provide some additional functionality that would otherwise be left out. Feel free to change it.

The folder called /in/, are simply containing a list of JPG’s that I’ve downloaded from various CC0 image archive sites, such as Pexels and Pixabay.

The folder called /out/ are used to store the newly generated images.

I then run a simple windows .bat file thats simply located inside the /in/ folder to rename all files to my liking. This way I could even match up quote #4 with image #4 if I wished to.

rename-images.bat

@echo off
ren *.jpeg *.jpg
setlocal EnableDelayedExpansion
set i=0
for %%a in (*.jpg) do (
ren "%%a" "!i!.new"
set /a i+=1
)
ren *.new *.jpg

authors.csv

quotes.csv

As you can see, both CSV files are simply separated by line breaks.

This is the first time I write a tutorial that covers more than a few lines of code, so I hope you can bear with my bad commenting, and even worse quality of code and formatting. Below is the full script.

app.py

#BIMP - Batch Image Manipulating with Python

"""
Author; Mark Pedersen @makerspender
"""
import PIL
import csv
import textwrap
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw

#create lists to store quotes and authors in
lines = [line.rstrip() for line in open('quotes.csv')]
authors = [author.rstrip() for author in open('authors.csv')]

#start the for loop
for idx, val in enumerate(lines):

#echo each quote and author
print(idx, val)

#create main quote value
para = textwrap.wrap(val, width=40)

#set image dimensions
MAX_W, MAX_H = 1920, 1280

#set image location
imageFile = "D:\imgapp\in\%s.jpg" %idx

#assign im to pillow and open the image
im = Image.open(imageFile).convert('RGBA')

#resize the image to our chosen proportions and use antialiasing
im = im.resize((1920, 1280), Image.ANTIALIAS)

#create new layer for adding opacity
poly = Image.new('RGBA', (1920,1280))
polydraw = ImageDraw.Draw(poly)

#fill the image with black and 165/255 opacity
polydraw.rectangle([(0,0),(1920,1280)], fill=(0,0,0,165), outline=None)

#paste in the layer on top of the image im

#command to start merging layers
draw = ImageDraw.Draw(im)

#setting up fonts
font = ImageFont.truetype("C:\Windows\Fonts\ArchivoBlack-Regular.ttf",80)
authorfont = ImageFont.truetype("C:\Windows\Fonts\Roboto-BlackItalic.ttf",60)

#setting up padding and positioning for quote text

#for loop breaking up each quote into lines not exceeding the width of the image dimensions
for line in para:
w, h = draw.textsize(line, font=font)
draw.text(((MAX_W - w) / 2, current_h), line, font=font)

#setting up padding and positioning for author text
currentauthor = authors[idx]
w, h = draw.textsize(currentauthor, font=authorfont)
draw.text(((MAX_W - w) / 2, (current_h + 100)), currentauthor, font=authorfont)

#setting up padding and positining for optional text
sitelink = "Batch Image Manipulating with Python"

logo = Image.open('logo.png').convert('RGBA')
logo_w, logo_h = logo.size
im.paste(logo, (50,1150), logo)

#saving the image to our chosen location
im.save("D:\imgapp\out\image_%d.jpg" %idx)

I’ve used the official pillow documentation extensively, and while I had an idea that there would be plenty of tutorials out there offering a quick and better solution than the one I came up with, I couldn’t find any complete package.

So while the above code is neither pretty nor optimal, it works for the intended purpose, and hopefully can serve as inspiration or a bootstrap for your own projects.

Please let me know in the comments whether I’ve made a giant error, if you have updates, fixes or wishes for new features.