Python Imaging Library/Printable version
This is the print version of Python Imaging Library You won't see this message or any elements not part of the book's content when you print or preview this page. |
The current, editable version of this book is available in Wikibooks, the open-content textbooks collection, at
https://en.wikibooks.org/wiki/Python_Imaging_Library
Overview
Modules in PIL
edit- Image
- ImageChops
- ImageColor
- ImageDraw
- ImageEnhance
- ImageFile
- ImageFileIO
- ImageFilter
- ImageFont
- ImageGrab
- ImageMath
- ImageOps
- ImagePalette
- ImagePath
- ImageQt
- ImageSequence
- ImageStat
- ImageTk
- ImageWin
- PSDraw
Getting PIL
Python is obviously a prerequisite for using PIL. The current version of PIL is 1.1.7, and this supports Python up to v.2.6.
PIL is available from PythonWare at this page. Source code can be built for any platform, and Windows binaries are available.
Installing PIL
editWindows
editTo install on Windows machines, go to the page given above and download the appropriate binary executable for the version of Python that you have. Run this executable and follow the instructions given by the installer.
Linux
editOn Linux, you can either compile the source yourself, or install using your package manager. For Debian-based systems, apt-get can be used:
sudo apt-get install python-imaging
In Gentoo:
emerge imaging
Mac OS X
editTo install on a Mac OS X system , visit http://pythonmac.org and download the relevant .dmg file and install as any other application.
Using PIL
editOnce install you need to import the PIL modules you want to use. Basic functions are found in the Image module, so the following will work:
import Image
You can then access functions as usual, e.g. Image.load(filename). Uses of the other modules available are given in the overview section, and these are imported in exactly the same way.
File IO
File operations in PIL are very simple, and reflect normal Python methods.
This page or section is an undeveloped draft or outline. You can help to develop the work, or you can ask for assistance in the project room. |
open
editimport Image
img = Image.open(filepath)
load
editfrom PIL import Image
img = Image.open("path/to/image.ext")
pixels = img.load() # Load the pixels of the image into a matrix
show
editDisplays a copy of the specified image in a window.
from PIL import Image
img = Image.open("path/to/image.ext")
img.show() # Shows the image in a new window
save
editimport Image
img = Image.open(filepath)
img.save("example.png")
#img.save(outfile, options...)
#img.save(outfile, format, options...)
Editing Pixels
With PIL you can easily access and change the data stored in the pixels of an image. To get the pixel map, call load() on an image. The pixel data can then be retrieved by indexing the pixel map as an array.
pixelMap = img.load() #create the pixel map
pixel = pixelMap[0,0] #get the first pixel's value
When you change the pixel data, it is changed in the image it came from (since the pixel map is just a reference to the data rather than a copy).
Example
editThe following snippet shows how to change the pixel values in an image based on the index of the pixel:
from PIL import Image
# PIL accesses images in Cartesian co-ordinates, so it is Image[columns, rows]
img = Image.new( 'RGB', (250,250), "black") # create a new black image
pixels = img.load() # create the pixel map
for i in range(img.size[0]): # for every col:
for j in range(img.size[1]): # For every row
pixels[i,j] = (i, j, 100) # set the colour accordingly
img.show()
Drop Shadows
Drop shadows are a common way to emphasise an image.
Creating the shadow
editThe shadow can be created by taking a simple solid rectangle (usually black or grey, but you can also have coloured shadows) and applying the ImageFilter BLUR filter to it repeatedly. This filter uses a 5×5 kernel, so a single iteration will not be smoothly blurred. You can experiment to find the optimum number of iterations for your purpose.
def makeShadow(image, iterations, border, offset, backgroundColour, shadowColour):
# image: base image to give a drop shadow
# iterations: number of times to apply the blur filter to the shadow
# border: border to give the image to leave space for the shadow
# offset: offset of the shadow as [x,y]
# backgroundCOlour: colour of the background
# shadowColour: colour of the drop shadow
#Calculate the size of the shadow's image
fullWidth = image.size[0] + abs(offset[0]) + 2*border
fullHeight = image.size[1] + abs(offset[1]) + 2*border
#Create the shadow's image. Match the parent image's mode.
shadow = Image.new(image.mode, (fullWidth, fullHeight), backgroundColour)
# Place the shadow, with the required offset
shadowLeft = border + max(offset[0], 0) #if <0, push the rest of the image right
shadowTop = border + max(offset[1], 0) #if <0, push the rest of the image down
#Paste in the constant colour
shadow.paste(shadowColour,
[shadowLeft, shadowTop,
shadowLeft + image.size[0],
shadowTop + image.size[1] ])
# Apply the BLUR filter repeatedly
for i in range(iterations):
shadow = shadow.filter(ImageFilter.BLUR)
# Paste the original image on top of the shadow
imgLeft = border - min(offset[0], 0) #if the shadow offset was <0, push right
imgTop = border - min(offset[1], 0) #if the shadow offset was <0, push down
shadow.paste(image, (imgLeft, imgTop))
return shadow
First, let us ignore the last step and concentrate on the shadow. Let's see what we get for various numbers of iterations. The border was set to 8, the background was white and the shadow is 0x444444 grey. The initial image was 30×30pixels.
1 iteration | 2 iterations | 3 iterations | 5 iterations | 10 iterations |
Notice that the shadow is always contained in the image boundary - this is caused by the blue filter "hitting" the image boundaries. If the border were to be made larger, you would see the blur spreading out at large numbers of iterations.
Now, we can add the last section and try it. The result is below. This image used an offset of [3,3] with 3 iterations and the same colours as before.
Original image (the SVG was made into a rectangular PNG) | Resulting image with drop shadow |
Efficiency
editThe calculations needed to produce the blurred shadow is computationally expensive, especially for large images or many iterations, but the same shadow can be reused for any image of the same size. So, if you are going to creare lot of identically-sized image tiles, it will be beneficial to compute the shadow just once, and reuse it for each image.
You can also speed up the calculation of the shadow by recognising that the centre of the shadow will be flat fill of the shadow colour - only the edges need to be computed. How far in the lighter border extends depends on the number of iterations. Making this change reduces the problem from quadratic in the size of the image (i.e. doubling the width quadruples the time taken) to linear (doubling the width doubles the time) for large images, but will be unlikely to have a large effect for icons.
Dealing with non-rectangular images
editAdding transparency
edit
Image Tiling
from PIL import Image
img = Image.new( 'RGB', (255,255), "black") # create a new black image
pixels = img.load() # create the pixel map
for i in range(img.size[0]): # for every pixel:
for j in range(img.size[1]):
pixels[i,j] = (i, j, 100) # set the colour accordingly
img.show()