1 影像與圖形資料的處理
討論圖形(影像) 本身的處理,而討論的內容將會集中在Python Imaging Library (PIL) 這一套程式庫上。
PIL 是Python 下最有名的影像處理套件,由許多不同的模組所組成,並且提供了許多的處理功能,允許我們在簡單的Python 程式裡進行影像的處理。 使用像PIL 許樣的程式庫套件可以協助我們把精力集中在影像處理的工作本身,避免迷失在底層的演算法裡面。
由於影像處理牽涉到了大量的數學運算,因此PIL 中有許多的模組是用C 語言所寫成的,以提升處理的效率。 不過,在使用的時候,我們當然不必在意這樣的問題,只管放心地用就是了。
1.1 PIL 能為你作的事
PIL 具備(但不限於) 以下的能力:
- 數十種圖檔格式的讀寫能力。 常見的JPEG, PNG, BMP, GIF, TIFF 等格式,都在PIL 的支援之列。 另外,PIL 也支援黑白、灰階、自訂調色盤、RGB true color、帶有透明屬性的RBG true color、CMYK 及其它數種的影像模式。相當齊全。
- 基本的影像資料操作:裁切、平移、旋轉、改變尺寸、調置(transpose)、剪下與貼上等等。
- 強化圖形:亮度、色調、對比、銳利度。
- 色彩處理。
- PIL 提供十數種濾鏡(filter)。 當然,這個數目遠遠不能與Photoshop 或GIMP 這樣的專業特效處理軟體相比;但PIL 提供的這些濾鏡可以用在Python 程式裡面,提供批次化處理的能力。
- PIL 可以在影像中繪圖制點、線、面、幾何形狀、填滿、文字等等。
接下來,我們開始一步一步地對Python/PIL 的影像處理程式設計進行討論。
2 轉換圖檔格式
市面上有許多影像處理程式,一般人最常用它們來處理的工作大概就是圖檔格式轉換了;這是影像處理軟體最基本的功能,PIL 當然也要支援。
假設我們有一個JPG檔案,名字叫作 sample01.jpg,那麼,以下的程式會把這個檔案載入Python:
“”>“匯入映像”>“”(在= Image.open“sample01.jpg”)
im 這個物件是由 Image.open() 方法所產生出來的 Image 物件。 我們可以用 Image 物件內的屬性來查詢關於此檔案的資訊:
“”>“列印im.format,im.size,im.mode的JPEG(2288,1712)的RGB
格式字串放在 format 屬性裡,尺寸放在 size 屬性裡,而(調色盤)模式放在 mode 屬性裡。 從以上的執行結果,可以看出來我們讀的確實是一個JPEG檔案,檔案的尺寸是2288像素寬、1712像素高,調色盤是RGB全彩模式。
既然我們已經把圖檔讀入了Python,要處理它就簡單了。 利用Image類別的 save() 方法,可以把檔案儲存成PIL支援的格式:
“”“im.save(”fileout.png“)
如果圖檔很大,這會花上一點時間。Image.save() 方法會根據欲存檔的副檔名,自動判斷要存圖檔的格式(剛剛我們用的 open() 函式也會這樣作)。
save() 可以指定存檔格式。 在以下的例子裡,我們把存檔格式指定為JPEG:
“”“im.save(”fileout.png“,”JPEG格式“)
這時候副檔名是無所謂的。
只處理一兩個檔案的時候,使用Python 直譯器就相當合適。 然而若要處理一大群檔案,譬如把一整個目錄的JPEG 檔轉換為PNG 檔,那麼寫成一個程式檔會比較方便,例如:
#!/ usr /斌/從來自世界os.path進口映像匯入splitext世界jpglist =全球環境保護的python進口(“python_imaging_pix / *. [JJ] [頁] [千兆]”)在jpglist JPG格式:即時=映像。開放(JPG格式)巴布亞新畿內亞= splitext(JPG格式)[0] +“。巴布亞新畿內亞”im.save(巴新)印刷巴新
只要在一個放了 *.jpg 或 *.JPG 檔案的目錄裡面執行這個指令稿,它就會把所有的JPEG檔轉成PNG檔案:
$。/ convertdir.py file0001.png file0002.png。。file9999.png
既然PIL 會從檔名偵測常用的檔案格式,存檔時我們通常都不會指定存檔格式。
然而,依據檔案格式的不同,save() 方法提供了不同的選項參數。 以JPEG而言,它可以接受 quality (從1到100的整數,預設為75)、optimize (真假值)及 progression (真假值)。 在以下的例子裡,我們以100的 quality 來儲存JPEG檔案:
“”“im.save(”quality100.jpg“,品質= 100)
要訣
PIL 也支援EPS (Encapsulate PostScript) 格式的寫入。 TeX 的使用者可以利用PIL 來簡單地把圖檔轉成EPS 以供TeX compiler 使用。
3 改變影像與製作縮圖
在瞭解了基本的圖檔轉換之後,我們來看看如何對影像進行尺寸方面的修改。 PIL對 Image 物件提供了 resize 方法,以執行影像的縮放工作。 用我們的 sample01.jpg 檔案來當例子:
“”“進出口= Image.open(sample01.jpg ")>>>印刷im.size(2288,1712)”“”寬度= 400“”“浮動的比例=(寬)/ im.size [0]” “”高度=廉政(im.size [1] *比率)“”“它im.resize =((寬,高),Image.BILINEAR)”“>”列印nim.size(400,299)“”“它。儲存(“resized.jpg)
然後我們就會得到比較小的 resized.jpg:
resize() 這個方法會傳回一個新的Image物件,所以舊的Image不會被更動。resize() 接受兩個參數,第一個用來指定變更後的大小,是一個雙元素tuple,分別用以指定影像的寬與高;第二個參數可以省略,是用來指定變更時使用的插入法,預設是 Image.NEAREST (取最近點),這裡我們指定為品質比較好的 Image.BILINEAR。
resize() 可以把影像放大縮小,在使用時一定要傳入寬與高。 上面的程式會先限定新影像的寬,再根據舊影像的長寬比例來算出新影像的高應該是多少,最後把尺寸值傳入 resize() 去。 由此可知,resize() 是允許我們不等比例縮放的:
“”“寬度= 400”“”高度= 100“”“nim2 = im.resize((寬,高),Image.BILINEAR)”“”nim2.save(“resize2wide.jpg”)
會得到形狀奇怪的縮圖:
我們可以任意改變新影像的尺寸值。
另一個常用的操作是旋轉;rotate() 方法可以用來旋轉影像。 它取兩個參數,第一個參數是一個逆時針的度數,第二個參數則也是影像處理時的插入法,可省略:
“”“nim3 = nim.rotate(45歲,Image.BILINEAR)”“”nim3.save(“rotated.jpg”)
rotate() 並不會改變影像的尺寸(dimension),所以你會看到:
出現了黑邊。 如果我們想要連影像尺寸一起變動,得要改用 transpose() 方法:
“”“nim4 = nim.transpose(Image.ROTATE_90)”“”nim4.save(“transposed90.jpg”)
永恒的結果:
transpose() 方法接受 Image.FLIP_LEFT_RIGHT, Image.FLIP_TOP_DOWN, ROTATE_90, ROTATE_180, ROTATE_270 等五種參數;其中後三種的旋轉均為逆時針。rotate() 方法會對像素資料進行插入;而 transpose() 則只是轉置像素資料,所以沒有插入參數可以設定,也不會影響影像的品質。
縮放與旋轉是最常用的兩個操作,而在其中,縮圖的製作可能是特別常用的;PIL對縮圖提供了一個方便的thumbnail() 方法。thumbnail() 會直接修改Image物件本身,所以速度能比 resize() 更快,也消耗更少的記憶體。 它不接受指定插入法的參數,而且只能縮小影像,不能放大影像;用法是:
(“”“進出口= Image.open”sample01.jpg ")>>> im.thumbnail((400.100 ))>>> im.save(“thumbnail.jpg ")>>>列印im.size(133,100 )
thumbnail() 在接受尺寸參數的時候,行為與 resize() 不同;resize() 允許我們不等比例進行縮放,但 thumbnail()只能進行等比例縮小,並且是以長、寬中比較小的那一個值為基準。 因此,上面的程式所作出的 thumbnail.jpg 變成了133*100的小圖片:
有了這些操作,我們可以很輕易地執行影像管理的任務。
4 修改圖形內容
除了可以針對圖形的尺寸作變更之外,PIL 更提供我們變更影像內容的能力。 這樣,我們就不只能對影像進行管理,而能更進一步地利用程式來把影像的內容改成我們想要的樣子。
我們從「貼圖」開始:
“”“Baseim = Image.open(”resized.jpg ")>>> floatim = Image.open(“thumbnail.jpg ")>>> baseim.paste(floatim,(150,50 ))>>> baseim。儲存(“pasted.jpg”)
利用 paste() 方法,把之前作的 thumbnail.jpg 貼到 resized.jpg 裡面去:
此種用法的 paste() 方法要求兩個參數,第一是要貼上的Image,第二是要貼上的位置。 第二個參數有三種指定的方式:
- None:不指定位置與尺寸,那麼 pasted() 會假設要貼上的Image與被貼上的Image的尺寸完全相同。
- (left, upper):雙元素tuple。pasted() 會把要貼上的Image的左上方對齊在指定的位置。
- (left, upper, right, lower):四元素tuple。paste()` 除了會把Image的左上方對齊外,也會對齊右下角。 不過基本上這種寫法和上面那一種一樣,因為 paste() 要求要貼上的影像與這裡指定的尺寸一致,所以不可能出現不同的兩組right, lower。
除了「貼圖」之外,我們還可以對影像的內容進行裁切:
“”“進出口= Image.open(sample01.jpg ")>>>它im.crop =((700,300,1500,1300 ))>>> nim.thumbnail((400.400 ))>>>它。儲存(“croped.jpg)
(因為裁切之後的圖形還是大了點,所以再縮圖一次) 得到的結果是:
crop() 接受的 box 參數指定要裁切的左、上、右、下四個邊界值,形成一個矩形。
除了剪貼之外,PIL還可以使用內建的濾鏡(filter)作一些特效處理。 這些濾鏡都放在 ImageFilter 模組裡面,使用前要先匯入這個模組:
>>> import ImageFilter
我們用個例子,對剛剛裁切的"No Riding" 禁止牌作20 次blur (糊化),來看看PIL 濾鏡的效果:
“”“進出口= Image.open(croped.jpg ")>>>它=他們”“”在範圍I(20):它= nim.filter(ImageFilter.BLUR)...>>>它。儲存(“blured.jpg)
你應該看不出來它是"No Riding" 了吧:
使用濾鏡的基本文法是:
newim = im.filter(ImageFilter.FILTERNAME)
其中 FILTERNAME 是PIL中支援的濾鏡名稱,目前有:BLUR, CONTOUR, DETAIL, EDGE_ENHANCE, EDGE_ENHANCE_MORE, EMBOSS, FIND_EDGES, SMOOTH, SMOOTH_MORE, SHARPEN,此處就不一一介紹了,但建議你可以自己來把每一個濾鏡都試試看。
利用濾鏡,我們可以對同一類的影像進行相同的特效處理。 當然,影像特效需要很精細的調整,在自動化作業中通常只能達到很粗略的效果;但PIL 既然提供了,我們的Bot就擁有更多的工具可以使用。
5 生產的新畫滾動太平
除了對已存在的影像進行編修之外,從零開始建立新影像也是很重要的工作。 PIL中的 ImageDraw 模組提供給我們繪製影像內容的能力。 在使用 ImageDraw 之前,要先建立好空白的新影像:
“”>“匯入ImageDraw”“在Image.new”=(為“RGB”,(400.300 ))>>>提請= ImageDraw.Draw(中)
最後建出來的 draw 是一個 ImageDraw 物件會提供各種繪製影像的方法。 針對幾何圖形,draw 物件提供 arc() (弧線)、chord() (弦)、line() (線段)、ellipse() (橢圓)、point() (點)、rectangle() (矩形)與 polygon () (多邊形)。 不過,我們不準備討論幾何圖形的繪製;相信這些方法的使用對一般人來說應該都很直覺才是。
要訣
你可以在指令行輸入 pydoc ImageDraw.ImageDraw.<<methodname>> 來查詢上述方法(<<methodname>>)的說明,譬如 pydoc ImageDraw.ImageDraw.line。
這裡要介紹的不是幾何圖形,而是文字的繪製。 我們要再介紹一個模組:ImageFont,並且以執行個體來說明如何用PIL 「寫字」:
“”>“匯入映像,ImageDraw,ImageFont”“”字型= ImageFont.truetype(/ ...“/ usr /共用/ fonts /中的TrueType / freefont /”,24)“”“在Image.new FreeMono.ttf =(為“RGB”,(400.300 ))>>>提請= ImageDraw.Draw(中)“”“draw.text((20.20),”文本“,字型=字型)”“”im.save(文本“。 JPG格式“)
這樣就在一個黑色底圖上用白筆寫了"TEXT" 四個大字:
接著一一說明剛剛作的動作。 首先我們用 ImageFont 的 truetype() 函式建立了一個TrueType字型,大小設定為16點。truetype() 函式的第一個參數必須是字型檔的搜尋路徑,第二個參數是字型的點數。 然後依序建立影像物件與draw 物件。 寫字的動作用 draw 物件的 text() 方法來完成,它接受兩個參數,分別是文字的左上方點、字串,另外可以用 font 選項來指定所使用的字型(若不指定,便使用預設字型)。
在1.1.4 版之前,PIL 是只能使用點陣字型的。 現在PIL 加入了TrueType 向量字型的支援,對於要「寫字」的人來說實在是一大福音。 對點陣字來說,想改變字型的大小得要更換字型才作得到,但TrueType 就沒有這個限制。 如果我們想要寫出兩串不同大小的文字,這樣作就可以了:
“”“Largefont = ImageFont.truetype(/ ...”/ usr /共用/ fonts /中的TrueType / freefont / FreeMono.ttf“,48)”“”smallfont ImageFont.truetype =(/ ...“/ usr /共用/ fonts /中的TrueType / freefont /“,24)”“”在Image.new =(“皇家植物園”(400.300 FreeMono.ttf ))>>>提請= ImageDraw.Draw(中)“”“draw.text(( 20:20),“小文”字型= smallfont)“”“draw.text((20.120),”大文字“,字型= largefont)”“”im.save(“multitext.jpg”)
秋之結果:
以上就是在PIL 裡建立文字圖形的方法。
最後,我們要說明如何改變繪製圖形(文字)時的顏色;繪圖時畫筆的顏色是透過 draw 物件的 ink 屬性來改變的:
“”“draw.ink = 0 + 255 * 256 + 0 * 256 * 256
以上會把畫筆設成綠色。ink 值必須要是一個整數,其值由色彩的RGB值算出。 舉幾個 ink 值的例子:
- 紅色的 ink 值應設為 255(R) + 0(G)*256 + 0(B)*256*256,
- 藍色的 ink 值應設為 0(R) + 0(G)*256 + 255(B)*256*256,
- 靛色的 ink 值應設為 0(R) + 255(G)*256 + 255(B)*256*256
所設定的 ink 會影響所有後續的繪圖動作。
6結論
本文介紹了方便好用的PIL 套件,可以讓我們用Python 撰寫影像處理的程式。 我們對圖檔的格式處理、尺寸處理以及內容的編修都作了討論,最後也說明如何從零開始創作一個影像。
對網頁程式來說,動態產生簡單的影像是特別有用的功能,可以用來補足HTML 與CSS 的不足之處。 利用PIL 來執行批次影像處理的工作,更能省去我們許多的操作時間。 相信讀者能從其中發現它所提供的生產力。
在下一期的內容裡,我們要開始介紹Python 的網頁程式設計。