這篇隨筆介紹使用OpenCV進行圖像處理的第四章 幾何變換。
4 幾何變換
圖像的幾何變換是指將一幅圖像映射到另一幅圖像內。有縮放、翻轉、仿射變換、透視、重映射等操作。
4.1 縮放
使用cv2.resize()函數實現對圖像的縮放,但要注意cv2.resize()函數內的dsize參數與原圖像的行列屬性是相反的,也就是:目標圖像的行數是原始圖像的列數,目標圖像的列數是原始圖像的行數。
下面舉例說明cv2.resize()函數的用法:
1 import cv2 2 img=cv2.imread('E:/python_opencv/tupian.jpg') 3 rows,cols=img.shape[0:2] #行數和列數等於img的長度和寬度
4 size=(int(cols*0.9),int(rows*0.5)) #比例:列變為原來0.9倍,行變為0.5倍
5 rst=cv2.resize(img,size) #將img按size比例縮放
6 print('img.shape=',img.shape) 7 print('rst.shape=',rst.shape)
運行程序的結果如下:
img.shape=(600,60,3) rst.shape=(300,54,3)
可以看出,行數變為原來的0.5倍,列數變為原來的0.9倍。代碼中size的行列位置發生了交換。
4.2 翻轉
使用cv2.flip()函數對圖像翻轉,能夠實現水平方向翻轉、垂直方向翻轉、兩個方向同時翻轉。
下面舉例說明cv2.flip()函數的用法:
1 import cv2 2 img=cv2.imread('E:/python_opencv/tupian.jpg') 3 x=cv2.flip(img,0) #圖x對原圖像繞x軸翻轉
4 y=cv2.flip(img,1) #圖y對原圖像繞y軸翻轉
5 xy=cv2.flip(img,-1) #圖xy對原圖像繞x軸y軸同時翻轉
6 cv2.imshow('img',img) 7 cv2.imshow('x',x) 8 cv2.imshow('y',y) 9 cv2.imshow('xy',xy) 10 cv2.waitKey() 11 cv2.destroyAllWindows()
程序運行結果如下四幅圖,第一幅是原圖,第二幅是繞x軸翻轉,第三幅是繞y軸翻轉,第四幅是繞x軸y軸同時翻轉。
4.3 仿射
仿射變換是指圖像實現平移、旋轉等操作。
先設置一個變換矩陣M,然后使用cv2.warpAffine()函數對原圖像和變換矩陣M進行仿射操作。
(一)平移
要實現圖像的平移,我們先自定義一個轉換矩陣,再進行仿射平移變換。例程如下:
1 import cv2 2 import numpy as np 3 img=cv2.imread('E:\python_opencv/tupian.jpg') 4 height,width=img.shape[:2] #讀取原圖像的長和寬
5 x=100 #自定義轉換矩陣M的x軸移動值
6 y=200 #自定義轉換矩陣M的y軸移動值
7 M=np.float32([[1,0,x],[0,1,y]]) #構造轉換矩陣M
8 move=cv2.warpAffine(img,M,(width,height)) #平移映射
9 cv2.imshow('orginal',img) 10 cv2.imshow('move',move) 11 cv2.waitKey() 12 cv2.destroyAllWindows()
程序運行結果如下圖所示,左為原圖,右為平移后的圖。
(二)旋轉
使用函數cv2.getRotationMatrix2D()獲得轉移矩陣M,然后使用函數cv2.warpAffine()進行仿射旋轉變換。例程如下:
1 import cv2 2 img=cv2.imread('E:\python_opencv/tupian.jpg') 3 height,width=img.shape[:2] #讀取原圖像的長和寬
4 M=cv2.getRotationMatrix2D((width/2,height/2),45,0.6) #以中心為原點,逆時針旋轉45°,且縮小為原圖的0.6倍,獲得轉移矩陣M
5 rotate=cv2.warpAffine(img,M,(width,height)) #旋轉映射
6 cv2.imshow('original',img) 7 cv2.imshow('rotation',rotate) 8 cv2.waitKey() 9 cv2.destroyAllWindows()
程序運行結果如下圖所示,左為原圖,右為旋轉后的圖。
4.4 透視
透視變換是指將矩陣圖形投影到另一個視平面,可以映射為任意四邊形,所以透視變換也被稱為投影映射(Projection Mapping),並不是字面意義上的“透視”。透視與上節的仿射不同,仿射可以將矩陣映射為任意平行四邊形。
使用cv2.warpPerspective()函數實現透視變換。例程如下:
1 #完成圖像透視
2 import cv2 3 import numpy as np 4 img=cv2.imread('E:/python_opencv/tupian.jpg') 5 rows,cols=img.shape[:2] #讀取原圖像的長和寬
6 print(rows,cols) 7 #生成旋轉矩陣M
8 pts1=np.float32([[150,50],[400,50],[60,450],[310,450]]) 9 pts2=np.float32([[50,50],[rows-50,50],[50,cols-50],[rows-50,cols-50]]) 10 M=cv2.getPerspectiveTransform(pts1,pts2) 11 #使用函數cv2.warpPerspective()進行透視變換
12 dst=cv2.warpPerspective(img,M,(cols,rows)) 13 cv2.imshow('img',img) 14 cv2.imshow('dst',dst) 15 cv2.waitKey() 16 cv2.destroyAllWindows()
程序運行結果如下圖所示,左為原圖,右為透視變換的圖。
我們可以看到,原圖片經過透視映射后,變成另一個視角下的任意四邊形了。
4.5 重映射
重映射是修改了像素點的位置,從而生成一幅新的圖像,包括:復制、繞x軸y軸翻轉,x軸y軸互換,圖像縮放等。
均使用cv2.remap()重映射函數進行操作。
需要注意cv2.remap()中的兩個參數mapx、mapy。mapx表示對應位置上x軸坐標值,mapy表示對應位置上y軸坐標值。
(一)復制
使用cv2.remap()函數完成圖像復制,需先定義mapx,mapy的值,然后循環映射每個像素點到對應的位置上。
代碼如下:
1 import cv2 2 import numpy as np 3 img=cv2.imread('E:/python_opencv/tupian.jpg') 4 rows,cols=img.shape[:2] #讀取行列數
5 mapx=np.zeros(img.shape[:2],np.float32) #mapx參數設定為對應位置上的x軸坐標值
6 mapy=np.zeros(img.shape[:2],np.float32) #mapy參數設定為對應位置上的y軸坐標值
7 for i in range(rows): #對每個元素復制映射
8 for j in range(cols): 9 mapx.itemset((i,j),j) 10 mapy.itemset((i,j),i) 11 rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) 12 cv2.imshow('original',img) 13 cv2.imshow('result',rst) 14 cv2.waitKey() 15 cv2.destroyAllWindows()
執行后結果如下所示,可以看到,實現了圖像的復制重映射。
(二)繞x軸翻轉
重映射法對圖像繞x軸翻轉,表明mapx的值保持不變,mapy的值調整為總行數-1-當前行號,其余部分代碼不變,所以循環體內代碼變為:
1 for i in range(rows): 2 for j in range(cols): 3 mapx.itemset((i,j),j) #mapx的值保持不變
4 mapy.itemset((i,j),rows-1-i) #mapy的值調整為總行數-1-當前行號
(三)繞y軸翻轉
重映射法對圖像繞y軸翻轉,表明mapx的值調整為總行數-1-當前列號,mapy的值保持不變,所以循環體內代碼變為:
1 for i in range(rows): 2 for j in range(cols): 3 mapx.itemset((i,j),cols-1-j) #mapx的值調整為總列數-1-當前列號
4 mapy.itemset((i,j),i) #mapy的值保持不變
(四)繞x軸y軸翻轉
重映射也能實現圖像繞x軸和y軸的同時翻轉,只需將前兩個部分合並,使mapx的值調整為總行數-1-當前列號,mapy的值調整為總行數-1-當前行號。例程如下:
1 import cv2 2 import numpy as np 3 img=cv2.imread('E:\python_opencv/tupian.jpg') 4 rows,cols=img.shape[:2] 5 mapx=np.zeros(img.shape[:2],np.float32) 6 mapy=np.zeros(img.shape[:2],np.float32) 7 for i in range(rows): 8 for j in range(cols): 9 mapx.itemset((i,j),cols-1-j) #mapx的值調整為總列數-1-當前列號
10 mapy.itemset((i,j),rows-1-i) #mapy的值調整為總行數-1-當前行號
11 rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) 12 cv2.imshow('original',img) 13 cv2.imshow('result',rst) 14 cv2.waitKey() 15 cv2.destroyAllWindows()
執行后結果如下所示,可以看到,實現了圖像的繞x軸和y軸翻轉重映射過程。
(五)x軸、y軸互換
重映射中,x軸、y軸互換表明,mapx的值變為所在行的行號,mapy的值變為所在列的列號。
但當行數和列數不一致時,行或列無法完成映射的部分就被處理為0。示例代碼如下:
1 #使用函數cv2.remap()實現圖像繞x軸和y軸的互換
2 import cv2 3 import numpy as np 4 img=cv2.imread('E:\python_opencv/tupian.jpg') 5 rows,cols=img.shape[:2] 6 mapx=np.zeros(img.shape[:2],np.float32) 7 mapy=np.zeros(img.shape[:2],np.float32) 8 for i in range(rows): 9 for j in range(cols): 10 mapx.itemset((i,j),i) #mapx的值變為所在行的行號
11 mapy.itemset((i,j),j) #mapy的值變為所在列的列號
12 rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) 13 cv2.imshow('original',img) 14 cv2.imshow('result',rst) 15 cv2.waitKey() 16 cv2.destroyAllWindows()
結果如圖:
可以看到,列數多於行數的部分被置為0(黑色)。
(六)圖像的縮放
重映射提供了cv2.remap()函數能夠實現圖像的放大或縮小。處理圖像后,可以將圖像固定在圍繞其中心的某個區域。
下面例程中,x軸和y軸均縮小為原來的0.25-0.75倍之間。
1 import cv2 2 import numpy as np 3 img=cv2.imread('E:\python_opencv/tupian.jpg') 4 rows,cols=img.shape[:2] 5 mapx=np.zeros(img.shape[:2],np.float32) 6 mapy=np.zeros(img.shape[:2],np.float32) 7 for i in range(rows): 8 for j in range(cols): 9 if 0.25*cols < i < 0.75*cols and 0.25*rows < i < 0.75*rows: 10 #在目標圖像的x軸(0.25-0.75)倍之內生成縮小圖像
11 mapx.itemset((i,j),2*(j-0.25*cols)+0.5) 12 #在目標圖像的y軸(0.25-0.75)倍之內生成縮小圖像
13 mapy.itemset((i,j),2*(i-rows*0.25)+0.5) 14 else: 15 #不在上述區域的點都取(0,0)坐標點的值
16 mapx.itemset((i,j),0) 17 mapy.itemset((i,j),0) 18 rst=cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR) #圖像縮放重映射
19 cv2.imshow('original',img) 20 cv2.imshow('result',rst) 21 cv2.waitKey() 22 cv2.destroyAllWindows()
圖像縮放重映射結果如下:
這次內容就分享到這里了,下次繼續更新第5章 圖像閾值處理,希望與各位老師和小伙伴們交流學習~