## Image Arithmetic

PIL provides a set of arithmetic operations via the module ImageChops (Chops is short for channel operations). In the night sky example, some people would prefer working on a white background (which could save quite a bit of ink if you're printing the images). Per the previous section, you could transfer the image to a NumPy array and then convert it, but in such a simple case, it makes more sense to use the ImageChops invert() function:

# display an image and its inverse from PIL import Image, ImageChops im = Image.open('../images/nightsky.png')

new_img = Image.new('RGB', (im.size[o]*2, im.size[l])) new_img.paste(im, (0, 0))

new_img.paste(ImageChops.invert(im), (im.size[o], 0)) new_img.show()

In Figure 9-8, I've used the image generated by the script nightsky.py (Listing 9-6) with num_stars=10, min_star_radius=10, and max_star_radius=30 to show a more pronounced effect of the image inversion.

Figure 9-8. Inverting an image: the original is on the left, and the inverted image is on the right.

Table 9-3 lists some additional ImageChops operations. Notice that ImageChops operations operate only one channel (L) or RGB images.

Table 9-3. Some ImageChops Operations

Function

constant(imgl, value) darker(imgl, img2)

difference(imgl, img2)

lighter(imgl, img2)

subtract(imgl, img2, scale=1.0, offset=0)

Adds two images as follows: (imgl+img2)/ scale+offset. The default values of scale and offset mean a simple addition.

Returns an image of size imgl filled with color value.

Returns an image with the darker pixel from both images. This a minimum of the two images, on a pixel-by-pixel level.

Returns the absolute difference of two images. This is abs(imgl-img2), on a pixel-by-pixel level.

Returns an image with the lighter pixel from both images. This a maximum of the two images, on a pixel-by-pixel level.

Subtracts two images as follows: (imgl-img2)/ scale+offset. The default values of scale and offset mean a simple subtraction.

There are additional functions available in ImageChops; check out either help(ImageChops) or the PIL web site (http://www.pythonware.com/library/pil/handbook/ imagechops.htm).

You can create some interesting effects using these simple operations. And these effects can in turn be used for some fast image processing algorithms. Listing 9-13 presents a script that makes use of the lighter() method on two night sky images. To follow along, run the nightsky.py script and rename the generated file images/nightsky.png to images/ nightskyl.png; do it again, this time renaming the generated image to images/nightsky2.png.

Listing 9-13. Usinglighter() on Two Images from PIL import Image, ImageDraw, ImageFont, ImageChops from matplotlib import font_manager

# read the images imgl = Image.open('../images/nightskyl.png') img2 = Image.open('../images/nightsky2.png')

# create a new image, made of the lighter of the two imgB = ImageChops.lighter(imgl, img2)

# create a collage of three images width, height = imgl.size delta = 10

img = Image.new('RGB', (width*2+delta, height*2+delta), (255, 255, 255)) img.paste(imgl, (0, 0)) img.paste(img2, (width+delta, 0)) img.paste(img3, ((width+delta)/2, height+delta))

# annotate the images with text font_str = font_manager.findfont('Vera') ttf = ImageFont.truetype(font_str, 54)

draw = ImageDraw.Draw(img)

draw.text((delta, delta), 'Night Sky (l)', fill='white', font=ttf) draw.text((delta*2+width, delta), 'Night Sky (2)', fill='white', font=ttf) draw.text(((width+delta)/2+delta, height+delta*2), \ 'Combined', fill='white', font=ttf)

# display the final image img.showQ

I've made a collage and separated the images with a white band. Figure 9-9 shows the result.

Figure 9-9. Using lighter()

It's interesting to note that in this specific case, using the function add() would have resulted in a similar image.