簡介
PIL (Python Imaging Library)
Python圖像處理庫径玖,該庫支持多種文件格式赁咙,提供強大的圖像處理功能拢军。
使用Image類
PIL中最重要的類是Image類尉剩,該類在Image模塊中定義。
從文件加載圖像:
import Image
im = Image.open("lena.ppm")
如果成功菌瘫,這個函數(shù)返回一個Image對象∧纹現(xiàn)在你可以使用該對象的屬性來探索文件的內(nèi)容飞几。
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()
的標準實現(xiàn)不是很有效率刮吧,因為它將圖像保存到一個臨時文件湖饱,然后調(diào)用外部工具(比如系統(tǒng)的默認圖片查看軟件)顯示圖像。該函數(shù)將是一個非常方便的調(diào)試和測試工具杀捻。)
接下來的部分展示了該庫提供的不同功能井厌。
讀寫圖像
PIL支持多種圖像格式。從磁盤中讀取文件,只需使用Image
模塊中的open
函數(shù)旗笔。不需要提供文件的圖像格式。PIL庫將根據(jù)文件內(nèi)容自動檢測拄踪。
如果要保存到文件蝇恶,使用Image
模塊中的save
函數(shù)。當保存文件時惶桐,文件名很重要撮弧,除非指定格式,否則PIL庫將根據(jù)文件的擴展名來決定使用哪種格式保存姚糊。
** 轉(zhuǎn)換文件到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
函數(shù)的第二個參數(shù)可以指定使用的文件格式贿衍。如果文件名中使用了一個非標準的擴展名,則必須通過第二個參數(shù)來指定文件格式救恨。
** 創(chuàng)建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只有在需要的時候才加載像素數(shù)據(jù)。當你打開一個文件時肠槽,PIL只是讀取文件頭獲得文件格式擎淤、圖像模式、圖像大小等屬性秸仙,而像素數(shù)據(jù)只有在需要的時候才會加載嘴拢。
這意味著打開一個圖像文件是一個非常快的操作寂纪,不會受文件大小和壓縮算法類型的影響席吴。
** 獲得圖像信息 **
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
類提供了某些方法孝冒,可以操作圖像的子區(qū)域。提取圖像的某個子區(qū)域拟杉,使用crop()
函數(shù)迈倍。
** 復制圖像的子區(qū)域 **
box = (100, 100, 400, 400)
region = im.crop(box)
定義區(qū)域使用一個包含4個元素的元組,(left, upper, right, lower)捣域。坐標原點位于左上角啼染。上面的例子提取的子區(qū)域包含300x300個像素。
該區(qū)域可以做接下來的處理然后再粘貼回去焕梅。
** 處理子區(qū)域然后粘貼回去 **
region = region.transpose(Image.ROTATE_180)
im.paste(region, box)
當往回粘貼時迹鹅,區(qū)域的大小必須和參數(shù)匹配。另外區(qū)域不能超出圖像的邊界贞言。然而原圖像和區(qū)域的顏色模式無需匹配斜棚。區(qū)域會自動轉(zhuǎn)換。
** 滾動圖像 **
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()
函數(shù)有個可選參數(shù),接受一個掩碼圖像弟蚀。掩碼中255表示指定位置為不透明蚤霞,0表示粘貼的圖像完全透明,中間的值表示不同級別的透明度义钉。
PIL允許分別操作多通道圖像的每個通道昧绣,比如RGB圖像。split()
函數(shù)創(chuàng)建一個圖像集合捶闸,每個圖像包含一個通道夜畴。merge()
函數(shù)接受一個顏色模式和一個圖像元組,然后將它們合并為一個新的圖像删壮。接下來的例子交換了一個RGB圖像的三個通道贪绘。
** 分離和合并圖像通道 **
r, g, b = im.split()
im = Image.merge("RGB", (b, g, r));
對于單通道圖像,split()
函數(shù)返回圖像本身央碟。如果想處理各個顏色通道税灌,你可能需要先將圖像轉(zhuǎn)為RGB模式。
幾何變換
resize()
函數(shù)接受一個元組亿虽,指定圖像的新大小垄琐。
rotate()
函數(shù)接受一個角度值,逆時針旋轉(zhuǎn)经柴。
** 基本幾何變換 **
out = im.resize((128, 128))
out = im.rotate(45) # degrees counter-clockwise
圖像旋轉(zhuǎn)90度也可以使用transpose()
函數(shù)狸窘。transpose()
函數(shù)也可以水平或垂直翻轉(zhuǎn)圖像。
** 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()
函數(shù)在性能和結(jié)果上沒有區(qū)別坯认。
更通用的圖像變換函數(shù)為transform()
翻擒。
顏色模式變換
PIL可以轉(zhuǎn)換圖像的像素模式。
** 轉(zhuǎn)換顏色模式 **
im = Image.open("lena.ppm").convert("L")
PIL庫支持從其他模式轉(zhuǎn)為“L”或“RGB”模式牛哺,其他模式之間轉(zhuǎn)換陋气,則需要使用一個中間圖像,通常是“RGB”圖像引润。
圖像增強(Image Enhancement)
過濾器
ImageFilter
模塊包含多個預定義的圖像增強過濾器用于filter()
函數(shù)巩趁。
** 應用過濾器 **
import ImageFilter
out = im.filter(ImageFilter.DETAIL)
點操作
point()
函數(shù)用于操作圖像的像素值。該函數(shù)通常需要傳入一個函數(shù)對象淳附,用于操作圖像的每個像素:
** 應用點操作 **
# 每個像素值乘以1.2
out = im.point(lambda i: i * 1.2)
使用以上技術(shù)可以快速地對圖像像素應用任何簡單的表達式议慰。可以結(jié)合point()
函數(shù)和paste
函數(shù)修改圖像奴曙。
** 處理圖像的各個通道 **
# 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)
注意用于創(chuàng)建掩碼圖像的語法:
imout = im.point(lambda i: expression and 255)
Python計算邏輯表達式采用短路方式别凹,即:如果and運算符左側(cè)為false,就不再計算and右側(cè)的表達式洽糟,而且返回結(jié)果是表達式的結(jié)果炉菲。比如a and b
如果a為false則返回a堕战,如果a為true則返回b,詳見Python語法拍霜。
增強
對于更多高級的圖像增強功能嘱丢,可以使用ImageEnhance
模塊中的類。
可以調(diào)整圖像對比度祠饺、亮度越驻、色彩平衡、銳度等吠裆。
** 增強圖像 **
import ImageEnhance
enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")
圖像序列
PIL庫包含對圖像序列(動畫格式)的基本支持。支持的序列格式包括FLI/FLC
烂完、GIF
和一些實驗性的格式试疙。TIFF
文件也可以包含多個幀。
當打開一個序列文件時抠蚣,PIL庫自動加載第一幀祝旷。你可以使用seek()
函數(shù)tell()
函數(shù)在不同幀之間移動。
** 讀取序列 **
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
如例子中展示的嘶窄,當序列到達結(jié)尾時怀跛,將拋出EOFError異常。
注意當前版本的庫中多數(shù)底層驅(qū)動只允許seek到下一幀柄冲。如果想回到前面的幀吻谋,只能重新打開圖像。
以下迭代器類允許在for語句中循環(huán)遍歷序列:
** 一個序列迭代器類 **
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庫包含一些函數(shù)用于將圖像现横、文本打印到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()
函數(shù)打開圖像文件骇两,通常傳入一個文件名作為參數(shù):
im = Image.open("lena.ppm")
如果打開成功,返回一個Image對象姜盈,否則拋出IOError異常低千。
也可以使用一個file-like object代替文件名(暫可以理解為文件句柄)。該對象必須實現(xiàn)read馏颂,seek示血,tell函數(shù),必須以二進制模式打開救拉。
** 從文件句柄打開圖像 **
fp = open("lena.ppm", "rb")
im = Image.open(fp)
如果從字符串數(shù)據(jù)中讀取圖像矾芙,使用StringIO類:
** 從字符串中讀取 **
import StringIO
im = Image.open(StringIO.StringIO(buffer))
如果圖像文件內(nèi)嵌在一個大文件里,比如tar
文件中近上√尴埽可以使用ContainerIO或TarIO模塊來訪問。
** 從tar文檔中讀取 **
import TarIO
fp = TarIO.TarIO("Imaging.tar", "Imaging/test/lena.ppm")
im = Image.open(fp)
控制解碼器
** 該小節(jié)不太理解,請參考原文 **
有些解碼器允許當讀取文件時操作圖像葱绒。通常用于在創(chuàng)建縮略圖時加速解碼(當速度比質(zhì)量重要時)和輸出一個灰度圖到激光打印機時感帅。
draft()
函數(shù)。
** 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
輸出類似以下內(nèi)容:
original = RGB (512, 512)
draft = L (128, 128)
注意結(jié)果圖像可能不會和請求的模式和大小匹配地淀。如果要確保圖像不大于指定的大小失球,請使用thumbnail
函數(shù)。
擴展閱讀
Python2.7 教程 PIL
http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/00140767171357714f87a053a824ffd811d98a83b58ec13000
Python 之 使用 PIL 庫做圖像處理
http://www.cnblogs.com/way_testlife/archive/2011/04/17/2019013.html