PIL (Python Imaging Library) 教程

來自 http://effbot.org/imagingbook/introduction.htm

簡介

PIL (Python Imaging Library)

Python圖像處理庫,該庫支持多種文件格式,提供強大的圖像處理功能。

使用Image類

PIL中最重要的類是Image類,該類在Image模塊中定義。

從文件加載圖像:

import Image
im = Image.open("lena.ppm")

如果成功,這個函數返回一個Image對象。現在你可以使用該對象的屬性來探索文件的內容。

print im.format, im.size, im.mode
# PPM (512, 512) RGB

format屬性指定了圖像文件的格式,如果圖像不是從文件中加載的則爲None
size屬性是一個2個元素的元組,包含圖像寬度和高度(像素)。
mode屬性定義了像素格式,常用的像素格式爲:“L” (luminance) - 灰度圖, “RGB” , “CMYK”。

如果文件打開失敗, 將拋出IOError異常。

一旦你擁有一個Image類的實例,你就可以用該類定義的方法操作圖像。比如:顯示

im.show()

(show()的標準實現不是很有效率,因爲它將圖像保存到一個臨時文件,然後調用外部工具(比如系統的默認圖片查看軟件)顯示圖像。該函數將是一個非常方便的調試和測試工具。)

接下來的部分展示了該庫提供的不同功能。

讀寫圖像

PIL支持多種圖像格式。從磁盤中讀取文件,只需使用Image模塊中的open函數。不需要提供文件的圖像格式。PIL庫將根據文件內容自動檢測。

如果要保存到文件,使用Image模塊中的save函數。當保存文件時,文件名很重要,除非指定格式,否則PIL庫將根據文件的擴展名來決定使用哪種格式保存。

* 轉換文件到JPEG *

import os, sys
import Image

for infile in sys.argv[1:]:
    f, e = os.path.splitext(infile)
    outfile = f + ".jpg"
    if infile != outfile:
        try:
            Image.open(infile).save(outfile)
        except IOError:
            print "cannot convert", infile

save函數的第二個參數可以指定使用的文件格式。如果文件名中使用了一個非標準的擴展名,則必須通過第二個參數來指定文件格式。

* 創建JPEG縮略圖 *

import os, sys
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)
            im.save(outfile, "JPEG")
        except IOError:
            print "cannot create thumbnail for", infile

需要注意的是,PIL只有在需要的時候才加載像素數據。當你打開一個文件時,PIL只是讀取文件頭獲得文件格式、圖像模式、圖像大小等屬性,而像素數據只有在需要的時候纔會加載。

這意味着打開一個圖像文件是一個非常快的操作,不會受文件大小和壓縮算法類型的影響。

* 獲得圖像信息 *

import sys
import Image

for infile in sys.argv[1:]:
    try:
        im = Image.open(infile)
        print infile, im.format, "%dx%d" % im.size, im.mode
    except IOError:
        pass

剪切、粘貼、合併圖像

Image類提供了某些方法,可以操作圖像的子區域。提取圖像的某個子區域,使用crop()函數。

* 複製圖像的子區域 *

box = (100, 100, 400, 400)
region = im.crop(box)

定義區域使用一個包含4個元素的元組,(left, upper, right, lower)。座標原點位於左上角。上面的例子提取的子區域包含300x300個像素。

該區域可以做接下來的處理然後再粘貼回去。

* 處理子區域然後粘貼回去 *

region = region.transpose(Image.ROTATE_180)
im.paste(region, box)

當往回粘貼時,區域的大小必須和參數匹配。另外區域不能超出圖像的邊界。然而原圖像和區域的顏色模式無需匹配。區域會自動轉換。

* 滾動圖像 *

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

paste()函數有個可選參數,接受一個掩碼圖像。掩碼中255表示指定位置爲不透明,0表示粘貼的圖像完全透明,中間的值表示不同級別的透明度。

PIL允許分別操作多通道圖像的每個通道,比如RGB圖像。split()函數創建一個圖像集合,每個圖像包含一個通道。merge()函數接受一個顏色模式和一個圖像元組,然後將它們合併爲一個新的圖像。接下來的例子交換了一個RGB圖像的三個通道。

* 分離和合並圖像通道 *

r, g, b = im.split()
im = Image.merge("RGB", (b, g, r));

對於單通道圖像,split()函數返回圖像本身。如果想處理各個顏色通道,你可能需要先將圖像轉爲RGB模式。

幾何變換

resize()函數接受一個元組,指定圖像的新大小。
rotate()函數接受一個角度值,逆時針旋轉。

* 基本幾何變換 *

out = im.resize((128, 128))
out = im.rotate(45) # degrees counter-clockwise

圖像旋轉90度也可以使用transpose()函數。transpose()函數也可以水平或垂直翻轉圖像。

* transpose *

out = im.transpose(Image.FLIP_LEFT_RIGHT)
out = im.transpose(Image.FLIP_TOP_BOTTOM)
out = im.transpose(Image.ROTATE_90)
out = im.transpose(Image.ROTATE_180)
out = im.transpose(Image.ROTATE_270)

transpose()rotate()函數在性能和結果上沒有區別。

更通用的圖像變換函數爲transform()

顏色模式變換

PIL可以轉換圖像的像素模式。

* 轉換顏色模式 *

im = Image.open("lena.ppm").convert("L")

PIL庫支持從其他模式轉爲“L”或“RGB”模式,其他模式之間轉換,則需要使用一箇中間圖像,通常是“RGB”圖像。

圖像增強(Image Enhancement)

過濾器

ImageFilter模塊包含多個預定義的圖像增強過濾器用於filter()函數。

* 應用過濾器 *

import ImageFilter
out = im.filter(ImageFilter.DETAIL)

點操作

point()函數用於操作圖像的像素值。該函數通常需要傳入一個函數對象,用於操作圖像的每個像素:

* 應用點操作 *

# 每個像素值乘以1.2
out = im.point(lambda i: i * 1.2)

使用以上技術可以快速地對圖像像素應用任何簡單的表達式。可以結合point()函數和paste函數修改圖像。

* 處理圖像的各個通道 *

# 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)

注意用於創建掩碼圖像的語法:

imout = im.point(lambda i: expression and 255)

Python計算邏輯表達式採用短路方式,即:如果and運算符左側爲false,就不再計算and右側的表達式,而且返回結果是表達式的結果。比如a and b如果a爲false則返回a,如果a爲true則返回b,詳見Python語法。

增強

對於更多高級的圖像增強功能,可以使用ImageEnhance模塊中的類。

可以調整圖像對比度、亮度、色彩平衡、銳度等。

* 增強圖像 *

import ImageEnhance

enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")

圖像序列

PIL庫包含對圖像序列(動畫格式)的基本支持。支持的序列格式包括FLI/FLCGIF和一些實驗性的格式。TIFF文件也可以包含多個幀。

當打開一個序列文件時,PIL庫自動加載第一幀。你可以使用seek()函數tell()函數在不同幀之間移動。

* 讀取序列 *

import Image

im = Image.open("animation.gif")
im.seek(1) # skip to the second frame

try:
    while 1:
        im.seek(im.tell() + 1)
        # do something to im
except EOFError:
    pass # end of sequence

如例子中展示的,當序列到達結尾時,將拋出EOFError異常。

注意當前版本的庫中多數底層驅動只允許seek到下一幀。如果想回到前面的幀,只能重新打開圖像。

以下迭代器類允許在for語句中循環遍歷序列:

* 一個序列迭代器類 *

class ImageSequence:
    def __init__(self, im):
        self.im = im
    def __getitem__(self, ix):
        try:
            if ix:
                self.im.seek(ix)
            return self.im
        except EOFError:
            raise IndexError # end of sequence

for frame in ImageSequence(im):
    # ...do something to frame...

Postscript打印

PIL庫包含一些函數用於將圖像、文本打印到Postscript打印機。以下是一個簡單的例子。

* 打印到Postscript *

import Image
import PSDraw

im = Image.open("lena.ppm")
title = "lena"
box = (1*72, 2*72, 7*72, 10*72) # in points

ps = PSDraw.PSDraw() # default is sys.stdout
ps.begin_document(title)

# draw the image (75 dpi)
ps.image(box, im, 75)
ps.rectangle(box)

# draw centered title
ps.setfont("HelveticaNarrow-Bold", 36)
w, h, b = ps.textsize(title)
ps.text((4*72-w/2, 1*72-h), title)

ps.end_document()

讀取圖像進階

如前所述,可以使用open()函數打開圖像文件,通常傳入一個文件名作爲參數:

im = Image.open("lena.ppm")

如果打開成功,返回一個Image對象,否則拋出IOError異常。

也可以使用一個file-like object代替文件名(暫可以理解爲文件句柄)。該對象必須實現read,seek,tell函數,必須以二進制模式打開。

* 從文件句柄打開圖像 *

fp = open("lena.ppm", "rb")
im = Image.open(fp)

如果從字符串數據中讀取圖像,使用StringIO類:

* 從字符串中讀取 *

import StringIO

im = Image.open(StringIO.StringIO(buffer))

如果圖像文件內嵌在一個大文件裏,比如tar文件中。可以使用ContainerIO或TarIO模塊來訪問。

* 從tar文檔中讀取 *

import TarIO

fp = TarIO.TarIO("Imaging.tar", "Imaging/test/lena.ppm")
im = Image.open(fp)

控制解碼器

* 該小節不太理解,請參考原文 *

有些解碼器允許當讀取文件時操作圖像。通常用於在創建縮略圖時加速解碼(當速度比質量重要時)和輸出一個灰度圖到激光打印機時。

draft()函數。

* Reading in draft mode *

im = Image.open(file)
print "original = ", im.mode, im.size

im.draft("L", (100, 100))
print "draft = ", im.mode, im.size

輸出類似以下內容:

original = RGB (512, 512)
draft = L (128, 128) 

注意結果圖像可能不會和請求的模式和大小匹配。如果要確保圖像不大於指定的大小,請使用thumbnail函數。

擴展閱讀

Python2.7 教程 PIL
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00140767171357714f87a053a824ffd811d98a83b58ec13000

Python 之 使用 PIL 庫做圖像處理
http://www.cnblogs.com/way_testlife/archive/2011/04/17/2019013.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章