##Pillow概況
PIL是Python的一種圖像處理工具。
PIL支援大部分的圖像格式,高效并強大。
核心庫設計用來高速通路基于基于像素的資料存儲,給這個通用的圖像處理工具提供了堅實的基礎。
來看下這個庫的一般用途:
###圖像歸檔
PIL是較為理想的圖檔歸檔和批處理應用。你可以使用這個庫去生成縮略圖、轉換圖檔格式、列印圖像等。
目前版本可以識别和讀取大量的格式。寫操作被限制用于大多數通用的轉換處理和顯示格式上。
###圖像展示
目前發行版本包含Tk
PhotoImage
和
BitmapImage
接口,這個和
Windows DIB interface
一樣,可以用于
PythonWin
和其他基于視窗的程式。許多其他的GUI程式都是基于PIL。
為了調試友善,PIL提供了
show()
,用來儲存圖檔到硬碟和調用外部圖檔檢視器。
###圖形處理
PIL包含基本的圖像處理功能,包括像素操作、一套内置卷積核的濾鏡、色空間轉換。
PIL支援圖像縮放、旋轉、任意角度的轉換。
PIL支援用于統計圖像的直方圖。這個可以用來自動自動對比增強、全局統計分析。
##安裝
使用PIP安裝
pip install Pillow
##使用
###使用Image類
Image類是PIL最核心的類,定義在同名的子產品中。你可以建立這個類執行個體,可以通過加載圖檔檔案、處理其他Image執行個體、或建立一個空的Image執行個體。
為了從圖檔檔案加載Image類,需要使用
Image
子產品中的
open()
方法。
>>> from PIL import Image
>>> im = Image.open("hopper.ppm")
如果加載成功,這個函數會傳回一個Image對象。你可以使用執行個體屬性去檢視檔案内容。
>>> from __future__ import print_function
>>> print(im.format, im.size, im.mode)
PPM (512, 512) RGB
format
屬性定義了圖檔來源格式。如果執行個體沒有讀取檔案,
format
就是
None
。size屬性是一個兩個元素的元組,包含像素計的寬高。mode屬性定義了圖像通道的編号和名字,以及像素類型和深度。常用模式有用于灰階圖的"L"(luminance)、用于真彩圖的"RGB"、用于預印刷圖的"CMYK"模式。
如果你沒有打開檔案,會觸發一個
IOError
異常。
一旦你有了一個
Image
執行個體,你就可以使用類裡的方法來處理和維護圖像了。例如,讓我們顯示圖像,我們僅僅加載如下:
>>> im.show()
這個版本的 show()
不是非常高效,因為他是儲存圖像到一個臨時檔案,然後調用xv協助去顯示圖像。如果你沒有安裝xv,他将不會工作。盡管他工作了,也會非常難以調試和測試。
下一節介紹PIL提供的幾種不用的方法。
###讀寫圖像
PIL支援廣泛的圖檔格式。為了從硬碟讀取檔案,需要使用
Image
子產品中的
open()
方法。你不必知道檔案格式就可以打開檔案。PIL會根據檔案内容自動判斷檔案格式。
可以使用
Image
子產品裡的
save()
方法來儲存檔案。儲存檔案的時候,命名會很重要。除非你規定了格式,PIL會用檔案擴充名來存儲。
####轉換圖檔為JPEG格式
from __future__ import print_function
import os, sys
from PIL 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縮略圖
from __future__ import print_function
import os, sys
from PIL 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不解碼加載光栅資料,除非必須這樣。當你打開一個檔案,在檔案頭裡就可以判斷出檔案格式,并抽取出像模式、大小和其他需要解碼檔案的屬性,但是檔案的其他部分不會被處理,除非以後需要。
這個意思就是打開圖像檔案是一個很快速的操作,這個跟檔案大小和壓縮方式無關。這裡舉幾個簡單的例子,快速識别一批圖像檔案。
####識别圖像檔案
from __future__ import print_function
import sys
from PIL import Image
for infile in sys.argv[1:]:
try:
with Image.open(infile) as im:
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個元素的元組來定義區塊,相比對的坐标是(左上右下)。PIL使用一個坐标系統,原點位于左上角。坐标系統使用像素來引用位置,是以上例中的區塊大小是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))
part1.load()
part2.load()
image.paste(part2, (0, 0, xsize-delta, ysize))
image.paste(part1, (xsize-delta, 0, xsize, ysize))
return image
當從
crop()
粘貼回的時候,要先調用
load()
。這是因為剪切是一個惰性操作。如果
load()
沒有被調用,剪切操作就不會執行,直到有粘貼指令執行。這就意味着
part1
會被首先修改的
image
剪切。
對于更進階的技巧,粘貼指令可以将透明遮罩作為可選參數。在這個遮罩裡面,255表明在這個位置上粘貼的圖像是不透明的。0表明粘貼的圖像是完全透明的。在這兩個值之間的數值表明不同級别的透明度。例如,粘貼一張RGBA圖像,并使用它作為遮罩,将會粘貼圖像不透明的部分,而不是他的透明背景。
PIL也可以讓你處理單個或多個通道圖像,例如RGB圖像。
split
方法建立了一套新的圖像,每一個包含一個來源于原始多通道圖像裡的單個通道。
merge
方法采用一種模式和一組圖像,然後合并他們魏一張新圖像。下面的樣本代碼交換了RGB圖像的三種通道。
####分割合并通道
r, g, b = im.split()
im = Image.merge("RGB", (b, g, r))
對于單通道圖像 ,
split()
會傳回圖像本身。為作用于單色通道,你可以先轉換圖像為"RGB"模式。
###幾何變換
PIL.Image.Image
類包含一些變形和旋轉圖像方法
resize()
、
rotate()
。前者以一個元組為新尺寸,後者以逆時針的角度為角度。
####簡單的幾何變換
out = im.resize((128, 128))
out = im.rotate(45) # degrees counter-clockwise
旋轉圖像90度的步驟裡,你可以使用
rotate()
方法或
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)
也可以執行相同于
rotate()
的功能,如果
expand
标簽為真,則圖像尺寸有相同的更改。
更詳細的圖像轉換,可以查閱
transform()
方法說明。
顔色轉換
PIL可以使用
convert()
方法在不同的像素表示之間轉換。
轉換圖像模式
im = Image.open("hopper.ppm").convert("L")
該庫支援每個受支援的模式、“L”和“RGB”模式之間的轉換。
要在其他模式之間轉換,您可能需要使用中間圖像(通常是“RGB”圖像)。
# 将不同模式的圖檔列印出shape 和 [0, 0]像素點的值
from PIL import Image
import matplotlib.pyplot as plt
image = Image.open('images/tower.jpg') # 本地一個檔案
mode_list = ['1', 'L', 'I', 'F', 'P', 'RGB', 'RGBA', 'CMYK', 'YCbCr' ]
for mode in mode_list:
img = image.convert(mode)
img_data = np.array(img)
print('img_{:>1}.shape: {}' .format(mode, img_data.shape))
print('img_{:>}_data[0, 0]: {}'.format(mode, img_data[0, 0]))
print('---')
RGB 為真色彩模式, 可組合為 256 x 256 x256 種, 列印需要更改為 CMYK模式, 需要注意數值溢出的問題。
HSB 模式(本篇沒有涉及),建立基于人類感覺顔色的方式,将顔色分為色相(Hue),飽和度(Saturation),明亮度(Brightness),這裡不詳細展開。
CMYK模式,應用在印刷領域,4個字母意思是青、洋紅、黃、黑,因為不能保證純度,是以需要黑。
位圖模式,見1, 顔色由黑和白表示(True, False)。
灰階模式,隻有灰階, 所有顔色轉化為灰階值,見L,I,F。
雙色調模式(未有涉及),節約成本将可使用雙色調。
Lab模式(未涉及,ps内置),由3通道組成(亮度,a,b)組成,作為RGB到CMYK的過渡。
多通道模式,删除RGB,CMYK,Lab中某一個通道後,會轉變為多通道,多通道用于處理特殊列印,它的每個通道都為256級灰階通道。
索引顔色模式,用在多媒體和網頁,通過顔色表查取,沒有則就近取,僅支援單通道,(8位/像素)。
P模式利用了重映射的思想,事先準備一個調色闆,然後圖檔資料裡儲存的是調色闆的位置,而不是具體的色值,色值都儲存在調色闆裡。
###圖象增強
PIL提供了許多方法和子產品,可用于增強圖像。
####濾鏡
ImageFilter
子產品包含一些預定義的增強濾鏡,可以與
filter()
方法一起使用。
####應用濾鏡
from PIL import ImageFilter
out = im.filter(ImageFilter.DETAIL)
####點運算
point()
方法可以用來轉換圖像的像素值(例如圖像對比操作)。在大多數情況下,期望一個參數的函數對象可以傳遞給這個方法。根據這個函數處理每個像素:
####應用點轉換
# multiply each pixel by 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隻對邏輯表達式的部分進行評估,以确定結果,并傳回所檢查的最後一個值作為表達式的結果。是以,如果上面的表達式是假(0),那麼Python不會檢視第二個操作數,進而傳回0。否則,它将傳回255。
####增強
對于更進階的圖像增強,您可以使用
ImageEnhance
子產品中的類。一旦從圖像中建立了,就可以使用增強對象快速嘗試不同的設定。
你可以通過這種方式調整對比度、亮度、色彩平衡和清晰度。
####增強圖像
from PIL import ImageEnhance
enh = ImageEnhance.Contrast(im)
enh.enhance(1.3).show("30% more contrast")
###圖像序列
PIL包含一些對圖像序列的基本支援(也可以叫做動畫格式)。支援的序列格式包括FLI/FLC、GIF和幾個實驗格式。TIFF檔案還可以包含多個幀。
當您打開一個序列檔案時,它會自動加載序列中的第一個幀。您可以使用seek和tell方法在不同的幀之間移動:
####讀取學列
from PIL 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異常。
注意,目前版本庫中的大多數驅動程式隻允許您尋找下一幀(如上面示例中所示)。要重新修改檔案,您可能需要重新打開它。
下面的類讓您使用for語句來對序列進行循環:
####使用ImageSequence疊代器類
from PIL import ImageSequence
for frame in ImageSequence.Iterator(im):
# ...do something to frame...
###Postscript列印
PIL包括列印圖像、文字和在Postscript列印機上繪圖的功能。這是一個簡單的例子:
####畫Postscript
from PIL import Image
from PIL import PSDraw
im = Image.open("hopper.ppm")
title = "hopper"
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 title
ps.setfont("HelveticaNarrow-Bold", 36)
ps.text((3*72, 4*72), title)
ps.end_document()
###更多關于讀取圖像
如前所述,
Image
子產品的
open()
函數用于打開圖像檔案。在大多數情況下,您隻需将檔案名作為參數傳過去:
im = Image.open("hopper.ppm")
如果一切順利,結果就是一個
PIL.Image.Image
對象。否則,将抛出IOError異常。
您可以使用類似檔案的對象而不是檔案名。對象必須實作
read()
、
seek()
和
tell()
方法,并以二進制模式打開。
####從一個打開的檔案讀取
fp = open("hopper.ppm", "rb")
im = Image.open(fp)
要從字元串資料中讀取圖像,使用
StringIO
類:
####從字元串讀取
import StringIO
im = Image.open(StringIO.StringIO(buffer))
請注意,在讀取圖像頭之前,庫會重新調整檔案(使用
seek(0)
)。此外,還可以在讀取圖像資料時使用seek(通過load方法)。
如果圖像檔案嵌入到較大的檔案中,如tar檔案,則可以使用
ContainerIO
或
TarIO
子產品來通路它。
####從tar壓縮包裡讀取
from PIL import Image, TarIO
fp = TarIO.TarIO("Tests/images/hopper.tar", "hopper.jpg")
im = Image.open(fp)
###控制解碼器
一些解碼器允許您在從檔案中讀取圖像時對圖像進行操作。在建立縮略圖(通常速度比品質更重要)和列印到一個單色雷射列印機(當需要隻需要一個灰階版本的圖像時),這通常可以用于加速解碼。
draft()
方法操作一個已打開但尚未加載的圖像,以便盡可能地比對給定的模式和大小。這是通過重新配置圖像解碼器來完成的。
####以草稿模式讀取
這隻适用于JPEG和MPO檔案。
from PIL import Image
from __future__ import print_function
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)
注意,生成的圖像可能與請求的模式和大小不完全比對。為了確定圖像不大于給定的大小,可以使用縮略圖方法。
im=Image.open('dog.jpg')
draw=ImageDraw.Draw(im)
newfont=ImageFont.truetype('simkai.ttf',40) #設定字型,simkai為楷體,字型大小40,truetype相關知識可百度
draw.text((200,100),'you are so good!',(255,255,0),font=newfont) #第一個tuple表示要寫在哪裡,(left,up),之後寫的#文字,顔色為黃色,三通道設定可以百度,第<span style="white-space:pre"> </span> #一個是紅色,第二個綠色,第三個是藍色,從0到255,最後設定字型
im.show()
im.save('target.jpg')