77
400
Chapter 17
Changing Individual Pixels
The color of an individual pixel can be retrieved or set with the
getpixel()
and
putpixel()
methods. These methods both take a tuple representing the
x- and y-coordinates of the pixel. The
putpixel()
method also takes an addi-
tional tuple argument for the color of the pixel. This color argument is a
four-integer RGBA tuple or a three-integer RGB tuple. Enter the following
into the interactive shell:
u >>> im = Image.new('RGBA', (100, 100))
v >>> im.getpixel((0, 0))
(0, 0, 0, 0)
w >>> for x in range(100):
for y in range(50):
x im.putpixel((x, y), (210, 210, 210))
>>> from PIL import ImageColor
y >>> for x in range(100):
for y in range(50, 100):
z im.putpixel((x, y), ImageColor.getcolor('darkgray', 'RGBA'))
>>> im.getpixel((0, 0))
(210, 210, 210, 255)
>>> im.getpixel((0, 50))
(169, 169, 169, 255)
>>> im.save('putPixel.png')
At u we make a new image that is a 100×100 transparent square.
Calling
getpixel()
on some coordinates in this image returns
(0, 0, 0, 0)
because the image is transparent v. To color pixels in this image, we can
use nested
for
loops to go through all the pixels in the top half of the
image w and color each pixel using
putpixel()
x. Here we pass
putpixel()
the RGB tuple
(210, 210, 210)
, a light gray.
Say we want to color the bottom half of the image dark gray but don’t
know the RGB tuple for dark gray. The
putpixel()
method doesn’t accept a
standard color name like
'darkgray'
, so you have to use
ImageColor.getcolor()
to get a color tuple from
'darkgray'
. Loop through
the pixels in the bottom half of the image y and pass
putpixel()
the return value of
ImageColor.getcolor()
z,
and you should now have an image that is light gray
in its top half and dark gray in the bottom half, as
shown in Figure 17-10. You can call
getpixel()
on some
coordinates to confirm that the color at any given
pixel is what you expect. Finally, save the image to
putPixel.png.
Of course, drawing one pixel at a time onto
an image isn’t very convenient. If you need to draw
shapes, use the
ImageDraw
functions explained later
in this chapter.
Figure 17-10: The
putPixelpng image
52
Manipulating Images
401
Project: Adding a logo
Say you have the boring job of resizing thousands
of images and adding a small logo watermark to
the corner of each. Doing this with a basic graph-
ics program such as Paintbrush or Paint would
take forever. A fancier graphics application such
as Photoshop can do batch processing, but that
software costs hundreds of dollars. Let’s write a
script to do it instead.
Say that Figure 17-11 is the logo you want to
add to the bottom-right corner of each image: a
black cat icon with a white border, with the rest
of the image transparent.
At a high level, here’s what the program
should do:
• Load the logo image.
• Loop over all .png and.jpg files in the working directory.
• Check whether the image is wider or taller than 300 pixels.
• If so, reduce the width or height (whichever is larger) to 300 pixels and
scale down the other dimension proportionally.
• Paste the logo image into the corner.
• Save the altered images to another folder.
This means the code will need to do the following:
• Open the catlogo.png file as an
Image
object.
• Loop over the strings returned from
os.listdir('.')
.
• Get the width and height of the image from the
size
attribute.
• Calculate the new width and height of the resized image.
• Call the
resize()
method to resize the image.
• Call the
paste()
method to paste the logo.
• Call the
save()
method to save the changes, using the original filename.
Step 1: Open the Logo Image
For this project, open a new file editor window, enter the following code,
and save it as resizeAndAddLogo.py:
#! python3
# resizeAndAddLogo.py - Resizes all images in current working directory to fit
# in a 300x300 square, and adds catlogo.png to the lower-right corner.
Figure 17-11: The logo to be
added to the image.
VB.NET PDF- View PDF Online with VB.NET HTML5 PDF Viewer Highlight Text. Add Text. Add Text Box. Drawing Markups. PDF Print. C#.NET edit PDF sticky note, C#.NET print PDF, C#.NET go to any page by using page go to button
changing font size in a pdf form; adding a text field to a pdf
58
402
Chapter 17
import os
from PIL import Image
u SQUARE_FIT_SIZE = 300
v LOGO_FILENAME = 'catlogo.png'
w logoIm = Image.open(LOGO_FILENAME)
x logoWidth, logoHeight = logoIm.size
# TODO: Loop over all files in the working directory.
# TODO: Check if image needs to be resized.
# TODO: Calculate the new width and height to resize to.
# TODO: Resize the image.
# TODO: Add the logo.
# TODO: Save changes.
By setting up the
SQUARE_FIT_SIZE
u and
LOGO_FILENAME
v constants at the
start of the program, we’ve made it easy to change the program later. Say
the logo that you’re adding isn’t the cat icon, or say you’re reducing the out-
put images’ largest dimension to something other than 300 pixels. With these
constants at the start of the program, you can just open the code, change
those values once, and you’re done. (Or you can make it so that the values
for these constants are taken from the command line arguments.) Without
these constants, you’d instead have to search the code for all instances of
300
and
'catlogo.png'
and replace them with the values for your new project.
In short, using constants makes your program more generalized.
The logo
Image
object is returned from
Image.open()
w. For readability,
logoWidth
and
logoHeight
are assigned to the values from
logoIm.size
x.
The rest of the program is a skeleton of
TODO
comments for now.
Step 2: Loop Over All Files and Open Images
Now you need to find every .png file and .jpg file in the current working
directory. Note that you don’t want to add the logo image to the logo image
itself, so the program should skip any image with a filename that’s the same
as
LOGO_FILENAME
. Add the following to your code:
#! python3
# resizeAndAddLogo.py - Resizes all images in current working directory to fit
# in a 300x300 square, and adds catlogo.png to the lower-right corner.
import os
from PIL import Image
--snip--
76
Manipulating Images
403
os.makedirs('withLogo', exist_ok=True)
# Loop over all files in the working directory.
u for filename in os.listdir('.'):
v if not (filename.endswith('.png') or filename.endswith('.jpg')) \
or filename == LOGO_FILENAME:
w continue # skip non-image files and the logo file itself
x im = Image.open(filename)
width, height = im.size
--snip--
First, the
os.makedirs()
call creates a withLogo folder to store the fin-
ished images with logos, instead of overwriting the original image files.
The
exist_ok=True
keyword argument will keep
os.makedirs()
from raising
an exception if withLogo already exists. While looping through all the files
in the working directory with
os.listdir('.')
u, the long
if
statement v
checks whether each filename doesn’t end with .png or .jpg. If so—or if the
file is the logo image itself—then the loop should skip it and use
continue
w
to go to the next file. If
filename
does end with
'.png'
or
'.jpg'
(and isn’t the
logo file), you can open it as an
Image
object x and set
width
and
height
.
Step 3: Resize the Images
The program should resize the image only if the width or height is larger
than
SQUARE_FIT_SIZE
(300 pixels, in this case), so put all of the resizing code
inside an
if
statement that checks the
width
and
height
variables. Add the
following code to your program:
#! python3
# resizeAndAddLogo.py - Resizes all images in current working directory to fit
# in a 300x300 square, and adds catlogo.png to the lower-right corner.
import os
from PIL import Image
--snip--
# Check if image needs to be resized.
if width > SQUARE_FIT_SIZE and height > SQUARE_FIT_SIZE:
# Calculate the new width and height to resize to.
if width > height:
u height = int((SQUARE_FIT_SIZE / width) * height)
width = SQUARE_FIT_SIZE
else:
v width = int((SQUARE_FIT_SIZE / height) * width)
height = SQUARE_FIT_SIZE
# Resize the image.
print('Resizing %s...' % (filename))
w im = im.resize((width, height))
--snip--
77
404
Chapter 17
If the image does need to be resized, you need to find out whether it is
a wide or tall image. If
width
is greater than
height
, then the height should
be reduced by the same proportion that the width would be reduced u.
This proportion is the
SQUARE_FIT_SIZE
value divided by the current width.
The new
height
value is this proportion multiplied by the current
height
value. Since the division operator returns a float value and
resize()
requires
the dimensions to be integers, remember to convert the result to an integer
with the
int()
function. Finally, the new
width
value will simply be set to
SQUARE_FIT_SIZE
.
If the
height
is greater than or equal to the
width
(both cases are handled
in the
else
clause), then the same calculation is done, except with the
height
and
width
variables swapped v.
Once
width
and
height
contain the new image dimensions, pass them to
the
resize()
method and store the returned
Image
object in
im
w.
Step 4: Add the Logo and Save the Changes
Whether or not the image was resized, the logo should still be pasted to the
bottom-right corner. Where exactly the logo should be pasted depends on
both the size of the image and the size of the logo. Figure 17-12 shows how
to calculate the pasting position. The left coordinate for where to paste the
logo will be the image width minus the logo width; the top coordinate for
where to paste the logo will be the image height minus the logo height.
Image
Logo width
Logo height
Logo
Image width
Image height
Figure 17-12: The left and top coordinates for
placing the logo in the bottom-right corner
should be the image width/height minus the
logo width/height.
After your code pastes the logo into the image, it should save the modi-
fied
Image
object. Add the following to your program:
#! python3
# resizeAndAddLogo.py - Resizes all images in current working directory to fit
# in a 300x300 square, and adds catlogo.png to the lower-right corner.
import os
from PIL import Image
32
Manipulating Images
405
--snip--
# Check if image needs to be resized.
--snip--
# Add the logo.
u print('Adding logo to %s...' % (filename))
v im.paste(logoIm, (width - logoWidth, height - logoHeight), logoIm)
# Save changes.
w im.save(os.path.join('withLogo', filename))
The new code prints a message telling the user that the logo is being
added u, pastes
logoIm
onto
im
at the calculated coordinates v, and saves
the changes to a filename in the withLogo directory w. When you run this
program with the zophie.png file as the only image in the working directory,
the output will look like this:
Resizing zophie.png...
Adding logo to zophie.png...
The image zophie.png will be changed to a 225×300-pixel image that
looks like Figure 17-13. Remember that the
paste()
method will not paste
the transparency pixels if you do not pass the
logoIm
for the third argument
as well. This program can automatically resize and “logo-ify” hundreds of
images in just a couple minutes.
Figure 17-13: The image zophiepng resized and the logo added (left). If you forget the
third argument, the transparent pixels in the logo will be copied as solid white pixels (right).
84
406
Chapter 17
Ideas for Similar Programs
Being able to composite images or modify image sizes in a batch can be
useful in many applications. You could write similar programs to do the
following:
• Add text or a website URL to images.
• Add timestamps to images.
• Copy or move images into different folders based on their sizes.
• Add a mostly transparent watermark to an image to prevent others
from copying it.
drawing on images
If you need to draw lines, rectangles, circles, or other simple shapes on
an image, use Pillow’s
ImageDraw
module. Enter the following into the inter-
active shell:
>>> from PIL import Image, ImageDraw
>>> im = Image.new('RGBA', (200, 200), 'white')
>>> draw = ImageDraw.Draw(im)
First, we import
Image
and
ImageDraw
. Then we create a new image, in this
case, a 200×200 white image, and store the
Image
object in
im
. We pass the
Image
object to the
ImageDraw.Draw()
function to receive an
ImageDraw
object.
This object has several methods for drawing shapes and text onto an
Image
object. Store the
ImageDraw
object in a variable like
draw
so you can use it eas-
ily in the following example.
Drawing Shapes
The following ImageDraw methods draw various kinds of shapes on the
image. The
fill
and
outline
parameters for these methods are optional and
will default to white if left unspecified.
Points
The
point(xy, fill)
method draws individual pixels. The
xy
argument
represents a list of the points you want to draw. The list can be a list of
x- and y-coordinate tuples, such as
[(x, y), (x, y), ...]
, or a list of x- and
y- coordinates without tuples, such as
[x1, y1, x2, y2, ...]
. The
fill
argu-
ment is the color of the points and is either an RGBA tuple or a string of
a color name, such as
'red'
. The
fill
argument is optional.
Lines
The
line(xy, fill, width)
method draws a line or series of lines.
xy
is either
a list of tuples, such as
[(x, y), (x, y), ...]
, or a list of integers, such as
[x1, y1, x2, y2, ...]
. Each point is one of the connecting points on the
Documents you may be interested
Documents you may be interested