You know there are ways to edit images online. Maybe you just want to caption it, or crop or rescale it. Up until now you only knew of online tools and standalone programs that were able to do this. Maybe you were relying on Photoshop for a long time to edit an image.
But anyone who has a bunch of images can tell you how difficult it is to find a program to edit a bunch of images at once, the so-called "batch editing". So instead of trying to find a tool that does that, it may be worthwhile to write a script in a programming language that you know.
In Python there is a package called Pillow that can manipulate several image types programmatically. Pillow is a fork of a now-abandoned package called PIL. Pillow runs on both Python 3 and 2, but given that Python 2 is unsupported now you probably should consider migrating.
Installation
Usualy, pip3 install Pillow
should do the trick. Although I have experienced some cases where you can't open any image file with Pillow because it throws an exception. If this happens it's probably because Pillow wasn't compiled with support for that image format, and you should compile pillow yourself. But this problem doesn't appear on Windows.
Operating system distribution-provided installations of Pillow should work out of the box, but I have not verified this.
I should also mention that you can't have PIL and Pillow installed in the same python environment, which could be a venv or a literal python installation. They are incompatible with each other. But you won't run into this problem on Python 3 because PIL was never ported to Python 3.
Usage
Quick and dirty now, to open an image, you use the Image.open()
function.
>>> from PIL import Image
>>> im = Image.open("lena.ppm")
This returns a handle to an image. Inside im.format
is a human-readable extension name for the file type, something like PPM
. im.size
is a tuple with width and height elements, like (512, 512)
. im.mode
contains the color system used in the image, such as RGB
or CMYK
.
To display the image, you could use im.show()
. This uses the xv
program to display the image on the screen so if you don't have that installed, then this method won't work. Admittingly, I haven't even heard of xv
before this so this is as far as I will cover it in this article.
show()
is mainly useful for testing and debugging purposes, but you should use the im.save("pathfilename")
method to save the read image somewhere so that you can open it with any image viewer.
Effects
A non-exhaustive list of operations that you can perform on Pillow images is:
- Creating thumbnails
- Cropping
- Pasting regions of image elsewhere
- Rotating regions of images
- Resizing
- Flipping regions of images
- General transformations
- Color mode conversion
- Per-pixel color manipulations
- Processing individual color bands (such as red)
- Enhancing image contrast and brightness
- Operating on each GIF frame independently
So you see, it's very powerful, if you can get the images to open that is. And as I said earlier, compiling from source usually solves these problems. Pillow has to link with a few C libraries and so that's where the compilation kicks in - it links the libraries with the main Pillow code written in C that has Python bindings.
I will go over the most common operations one by one. The Pillow documentation is an excellent place to learn the more advanced operations.
Also I'm more of the "learn by example" kind of person so you'll be seeing more examples here than explanations of what they do. Where things are unclear I will clarify them though.
All of these snippets assume you import Image first: from PIL import Image
. If you are getting errors such as Image is not defined
, you need to run the aforementioned command to import it.
Creating thumbnails
Thumbnails are stored inside the image file, not separately. They are the small pictures you see in file managers.
import os, sys
from PIL import Image
size = (128, 128)
for infile in sys.argv[1:]:
outfile = os.path.splitext(infile)[0] + ".thumbnail"
if infile != outfile:
try:
im = Image.open(infile)
im.thumbnail(size) # This creates the thumbnal.
im.save(outfile, "JPEG")
except IOError:
print("cannot create thumbnail for", infile)
Cropping
The crop()
method also returns an Image object.
box = (100, 100, 400, 400)
region = im.crop(box)
Pasting
Likewise, it's also possible to paste an Image object on another one using the paste()
method.
im.paste(region, box)
Rotating and flipping
As a general rule, methods do not modify the input image. They return the modified image as the output. And, rotating and flipping is accomplished by using the transpose()
function. It may sound like an odd name considering the purpose of it, but you can pass a value such as Image.ROTATE_180
to direct it to do the corresponding operation.
region = region.transpose(Image.ROTATE_180)
# Transforms it again
region = region.transpose(Image.FLIP_LEFT_RIGHT)
You can also use rotate()
to rotate the image at an arbitrary amount of degrees.
out = im.rotate(45) # degrees counter-clockwise
Resizing
Tuple is specified as (width, height).
out = im.resize((128, 128))
Getting the image bands
All images are composed of red, green and blue values. You can retrieve each of them separately from an image by calling the split()
function. When you're done modifying them individually, you can call the merge()
function to put them back together.
r, g, b = im.split()
# ...
# Adjust the red values, the green values and blue values
# ...
im = Image.merge("RGB", (b, g, r))
Color mode conversion
You can convert any color mode to RGB and vice versa. You can also convert any color mode to L (the grayscale mode) and vice versa.
# PPM is not a color mode, it's an image format (way the image is stored on disk)
im = Image.open("lena.ppm")
# Convert this PPM-file-format image to grayscale
im.convert("L")
Applying filters to images
A filter is a post-processing effect that is added to the image to make it look different. There are several filters in Pillow. All of them are in the ImageFilter module, not the Image module, so you need to import ImageFilter from PIL to use them.
BLUR
CONTOUR
DETAIL
EDGE_ENHANCE
EDGE_ENHANCE_MORE
EMBOSS
FIND_EDGES
SMOOTH
SMOOTH_MORE
SHARPEN
Use the filters like this:
from PIL import ImageFilter
im1 = im.filter(ImageFilter.BLUR) # Blurs the image
Per-point manipulation
You can pass a function to the point()
method that takes a single number as input and applies a math expression to it.
# multiply each pixel by 1.2
# This example indiscriminately adjusts the red, green and blue values. See below to modify them individually.
out = im.point(lambda i: i * 1.2)
One of the benefits of having split()
and merge()
is they can be used in situations like this, where an image is expected by the point()
function, but you can pass three different images with each color band so you can run those images/color-bands through three different functions. A function that does that would look something like this:
# split the image into individual bands
source = im.split()
R, G, B = 0, 1, 2
# select regions where red is less than 100
mask = source[R].point(lambda i: i < 100 and 255)
# process the green band
out = source[G].point(lambda i: i * 0.7)
# paste the processed band back, but only where red was < 100
source[G].paste(out, None, mask)
# build a new multiband image
im = Image.merge(im.mode, source)
Image enhancement
You can use the ImageEnhancement
module to give your images more contrast, brightness, or sharpness, or change the color balance. It is not a substitute for careful and accurate drawing, however.
Each of the items in ImageEnhancement
is a class that takes an image in the constructor, and contains a method to return a modified version of the image. Currently the classes are:
- PIL.ImageEnhance.Color(image)
- PIL.ImageEnhance.Contrast(image)
- PIL.ImageEnhance.Brightness(image)
- PIL.ImageEnhance.Sharpness(image)
They all have an enhance()
method that takes a single number as its argument.
from PIL import ImageEnhance
enh = ImageEnhance.Contrast(image)
enh.enhance(1.3).show("30% more contrast")
enhancer = ImageEnhance.Sharpness(image)
for i in range(8):
factor = i / 4.0
enhancer.enhance(factor).show("Sharpness %f" % factor)
Neat example
This examples were taken from the documentation. It moves an image to the left, putting the end of the image on the right.
def roll(image, delta):
"Roll an image sideways"
xsize, ysize = image.size
delta = delta % xsize
if delta == 0: return image
part1 = image.crop((0, 0, delta, ysize))
part2 = image.crop((delta, 0, xsize, ysize))
image.paste(part2, (0, 0, xsize-delta, ysize))
image.paste(part1, (xsize-delta, 0, xsize, ysize))
return image
And we're done
I'm never perfect, but in this particular article I'm dependent on what I see in the documentation, having failed to get Pillow running, which means there is a higher chance that errors have cropped up (no pun intended) in this article. so if you spot errors, let me know in the comments so I can correct them.
Image by PublicDomainPictures from Pixabay
Top comments (1)
I played with this Pillow library to do some automatic photo comparison hobby project, it can also help with preparing the image for OCR, by making it black-and-white and more sharp.