python PIL 影象處理 (二)
第 1 章 基本的影象操作和處理
本章講解操作和處理影象的基礎知識,將通過大量示例介紹處理影象所需的 Python 工具包,並介紹用於讀取影象、影象轉換和縮放、計算導數、畫圖和儲存結果等的基本工具。這些工具的使用將貫穿本書的剩餘章節。
1.1 PIL:Python影象處理類庫
PIL(Python Imaging Library Python,影象處理類庫)提供了通用的影象處理功能,以及大量有用的基本影象操作,比如影象縮放、裁剪、旋轉、顏色轉換等。PIL 是免費的,可以從 http://www.pythonware.com/products/pil/ 下載。
利用 PIL 中的函式,我們可以從大多數影象格式的檔案中讀取資料,然後寫入最常見的影象格式檔案中。PIL 中最重要的模組為 Image
from PIL import Image
pil_im = Image.open('empire.jpg')
上述程式碼的返回值 pil_im
是一個 PIL 影象物件。
影象的顏色轉換可以使用 convert()
方法來實現。要讀取一幅影象,並將其轉換成灰度影象,只需要加上 convert('L')
,如下所示:
pil_im = Image.open('empire.jpg').convert('L')
圖 1-1:用 PIL 處理影象的例子
1.1.1 轉換影象格式
通過 save()
方法,PIL 可以將影象儲存成多種格式的檔案。下面的例子從檔名列表( filelist
from PIL import Image
import os
for infile in filelist:
outfile = os.path.splitext(infile)[0] + ".jpg"
if infile != outfile:
try:
Image.open(infile).save(outfile)
except IOError:
print "cannot convert", infile
PIL 的 open()
函式用於建立 PIL 影象物件, save()
本書會處理大量影象列表。下面將建立一個包含資料夾中所有影象檔案的檔名列表。首先新建一個檔案,命名為 imtools.py,來儲存一些經常使用的影象操作,然後將下面的函式新增進去:
import os
def get_imlist(path):
""" 返回目錄中所有JPG 影象的檔名列表"""
return [os.path.join(path,f) for f in os.listdir(path) if f.endswith('.jpg')]
現在,回到 PIL。
1.1.2 建立縮圖
使用 PIL 可以很方便地建立影象的縮圖。 thumbnail()
方法接受一個元組引數(該引數指定生成縮圖的大小),然後將影象轉換成符合元組引數指定大小的縮圖。例如,建立最長邊為
128 畫素的縮圖,可以使用下列命令:
pil_im.thumbnail((128,128))
1.1.3 複製和貼上影象區域
使用 crop()
方法可以從一幅影象中裁剪指定區域:
box = (100,100,400,400)
region = pil_im.crop(box)
該區域使用四元組來指定。四元組的座標依次是(左,上,右,下)。PIL 中指定座標系的左上角座標為(0,0)。我們可以旋轉上面程式碼中獲取的區域,然後使用 paste()
方法將該區域放回去,具體實現如下:
region = region.transpose(Image.ROTATE_180)
pil_im.paste(region,box)
1.1.4 調整尺寸和旋轉
要調整一幅影象的尺寸,我們可以呼叫 resize()
方法。該方法的引數是一個元組,用來指定新影象的大小:
out = pil_im.resize((128,128))
要旋轉一幅影象,可以使用逆時針方式表示旋轉角度,然後呼叫 rotate()
方法:
out = pil_im.rotate(45)
上述例子的輸出結果如圖 1-1 所示。最左端是原始影象,然後是灰度影象、貼上有旋轉後裁剪影象的原始影象,最後是縮圖。
1.2 Matplotlib
我們處理數學運算、繪製圖表,或者在影象上繪製點、直線和曲線時, Matplotlib
是個很好的類庫,具有比 PIL 更強大的繪圖功能。 Matplotlib
可以繪製出高質量的圖表,就像本書中的許多插圖一樣。 Matplotlib
中的 PyLab
介面包含很多方便使用者建立影象的函式。 Matplotlib
是開源工具,可以從 http://matplotlib.sourceforge.net/ 免費下載。該連結中包含非常詳盡的使用說明和教程。下面的例子展示了本書中需要使用的大部分函式。
1.2.1 繪製圖像、點和線
儘管 Matplotlib
可以繪製出較好的條形圖、餅狀圖、散點圖等,但是對於大多數計算機視覺應用來說,僅僅需要用到幾個繪圖命令。最重要的是,我們想用點和線來表示一些事物,比如興趣點、對應點以及檢測出的物體。下面是用幾個點和一條線繪製圖像的例子:
from PIL import Image
from pylab import *
# 讀取影象到陣列中
im = array(Image.open('empire.jpg'))
# 繪製圖像
imshow(im)
# 一些點
x = [100,100,400,400]
y = [200,500,200,500]
# 使用紅色星狀標記繪製點
plot(x,y,'r*')
# 繪製連線前兩個點的線
plot(x[:2],y[:2])
# 新增標題,顯示繪製的影象
title('Plotting: "empire.jpg"')
show()
上面的程式碼首先繪製出原始影象,然後在 x 和 y 列表中給定點的 x 座標和 y 座標上繪製出紅色星狀標記點,最後在兩個列表表示的前兩個點之間繪製一條線段(預設為藍色)。該例子的繪製結果如圖 1-2 所示。 show()
命令首先開啟圖形使用者介面(GUI),然後新建一個影象視窗。該圖形使用者介面會迴圈阻斷指令碼,然後暫停,直到最後一個影象視窗關閉。在每個腳本里,你只能呼叫一次 show()
命令,而且通常是在指令碼的結尾呼叫。注意,在 PyLab
庫中,我們約定影象的左上角為座標原點。
影象的座標軸是一個很有用的除錯工具;但是,如果你想繪製出較美觀的影象,加上下列命令可以使座標軸不顯示:
axis('off')
上面的命令將繪製出如圖 1-2 右邊所示的影象。
圖 1-2: Matplotlib
繪圖示例。帶有座標軸和不帶座標軸的包含點和一條線段的影象
在繪圖時,有很多選項可以控制影象的顏色和樣式。最有用的一些短命令如表 1-1、表 1-2 和表 1-3 所示。使用方法見下面的例子:
plot(x,y) # 預設為藍色實線
plot(x,y,'r*') # 紅色星狀標記
plot(x,y,'go-') # 帶有圓圈標記的綠線
plot(x,y,'ks:') # 帶有正方形標記的黑色虛線
表1-1:用 PyLab
庫繪圖的基本顏色格式命令
顏色 |
|
---|---|
'b' |
藍色 |
'g' |
綠色 |
'r' |
紅色 |
'c' |
青色 |
'm' |
品紅 |
'y' |
黃色 |
'k' |
黑色 |
'w' |
白色 |
表1-2:用 PyLab
庫繪圖的基本線型格式命令
線型 |
|
---|---|
'-' |
實線 |
'--' |
虛線 |
':' |
點線 |
表1-3:用 PyLab
庫繪圖的基本繪製標記格式命令
標記 |
|
---|---|
'.' |
點 |
'o' |
圓圈 |
's' |
正方形 |
'*' |
星形 |
'+' |
加號 |
'x' |
叉號 |
1.2.2 影象輪廓和直方圖
下面來看兩個特別的繪圖示例:影象的輪廓和直方圖。繪製圖像的輪廓(或者其他二維函式的等輪廓線)在工作中非常有用。因為繪製輪廓需要對每個座標 [ x, y ] 的畫素值施加同一個閾值,所以首先需要將影象灰度化:
from PIL import Image
from pylab import *
# 讀取影象到陣列中
im = array(Image.open('empire.jpg').convert('L'))
# 新建一個影象
figure()
# 不使用顏色資訊
gray()
# 在原點的左上角顯示輪廓影象
contour(im, origin='image')
axis('equal')
axis('off')
像之前的例子一樣,這裡用 PIL 的 convert()
方法將影象轉換成灰度影象。
影象的直方圖用來表徵該影象畫素值的分佈情況。用一定數目的小區間(bin)來指定表徵畫素值的範圍,每個小區間會得到落入該小區間表示範圍的畫素數目。該(灰度)影象的直方圖可以使用 hist()
函式繪製:
figure()
hist(im.flatten(),128)
show()
hist()
函式的第二個引數指定小區間的數目。需要注意的是,因為 hist()
只接受一維陣列作為輸入,所以我們在繪製圖像直方圖之前,必須先對影象進行壓平處理。 flatten()
方法將任意陣列按照行優先準則轉換成一維陣列。圖
1-3 為等輪廓線和直方圖影象。
圖 1-3:用 Matplotlib
繪製圖像等輪廓線和直方圖
1.2.3 互動式標註
有時使用者需要和某些應用互動,例如在一幅影象中標記一些點,或者標註一些訓練資料。 PyLab
庫中的 ginput()
函式就可以實現互動式標註。下面是一個簡短的例子:
from PIL import Image
from pylab import *
im = array(Image.open('empire.jpg'))
imshow(im)
print 'Please click 3 points'
x = ginput(3)
print 'you clicked:',x
show()
上面的指令碼首先繪製一幅影象,然後等待使用者在繪圖視窗的影象區域點選三次。程式將這些點選的座標 [ x, y ] 自動儲存在 x 列表裡。
1.3 NumPy
NumPy
( http://www.scipy.org/NumPy/ )是非常有名的
Python 科學計算工具包,其中包含了大量有用的思想,比如陣列物件(用來表示向量、矩陣、影象等)以及線性代數函式。 NumPy
中的陣列物件幾乎貫穿用於本書的所有例子中 1 陣列物件可以幫助你實現陣列中重要的操作,比如矩陣乘積、轉置、解方程系統、向量乘積和歸一化,這為影象變形、對變化進行建模、影象分類、影象聚類等提供了基礎。
1 PyLab
實際上包含 NumPy
的一些內容,如陣列型別。這也是我們能夠在
1.2 節使用陣列型別的原因。
1.3.1 影象陣列表示
在先前的例子中,當載入影象時,我們通過呼叫 array()
方法將影象轉換成 NumPy
的陣列物件,但當時並沒有進行詳細介紹。 NumPy
中的陣列物件是多維的,可以用來表示向量、矩陣和影象。一個數組物件很像一個列表(或者是列表的列表),但是陣列中所有的元素必須具有相同的資料型別。除非建立陣列物件時指定資料型別,否則資料型別會按照資料的型別自動確定。
對於影象資料,下面的例子闡述了這一點:
im = array(Image.open('empire.jpg'))
print im.shape, im.dtype
im = array(Image.open('empire.jpg').convert('L'),'f')
print im.shape, im.dtype
控制檯輸出結果如下所示:
(800, 569, 3) uint8
(800, 569) float32
每行的第一個元組表示影象陣列的大小(行、列、顏色通道),緊接著的字串表示陣列元素的資料型別。因為影象通常被編碼成無符號八位整數(uint8),所以在第一種情況下,載入影象並將其轉換到陣列中,陣列的資料型別為“uint8”。在第二種情況下,對影象進行灰度化處理,並且在建立陣列時使用額外的引數“f”;該引數將資料型別轉換為浮點型。關於更多資料型別選項,可以參考圖書 [24]。注意,由於灰度影象沒有顏色資訊,所以在形狀元組中,它只有兩個數值。
陣列中的元素可以使用下標訪問。位於座標 i 、 j ,以及顏色通道 k 的畫素值可以像下面這樣訪問:
value = im[i,j,k]
多個數組元素可以使用陣列切片方式訪問。 切片方式 返回的是以指定間隔下標訪問該陣列的元素值。下面是有關灰度影象的一些例子:
im[i,:] = im[j,:] # 將第 j 行的數值賦值給第 i 行
im[:,i] = 100 # 將第 i 列的所有數值設為100
im[:100,:50].sum() # 計算前100 行、前 50 列所有數值的和
im[50:100,50:100] # 50~100 行,50~100 列(不包括第 100 行和第 100 列)
im[i].mean() # 第 i 行所有數值的平均值
im[:,-1] # 最後一列
im[-2,:] (or im[-2]) # 倒數第二行