1、 OpenCV的結構
和Python一樣,當前的OpenCV也有兩個大版本,OpenCV2和OpenCV3。相比OpenCV2,OpenCV3提供了更強的功能和更多方便的特性。不過考慮到和深度學習架構的相容性,以及上手安裝的難度,這部分先以2為主進行介紹。
根據功能和需求的不同,OpenCV中的函數介面大體可以分為如下部分:
core:核心模組,主要包含了OpenCV中最基本的結構(矩陣,點線和形狀等),以及相關的基礎運算/操作。
imgproc:影像處理模組,包含和映像相關的基礎功能(濾波,梯度,改變大小等),以及一些衍生的進階功能(映像分割,長條圖,形態分析和邊緣/直線提取等)。
highgui:提供了使用者介面和檔案讀取的基本函數,比如映像顯示視窗的產生和控制,映像/視頻檔案的IO等。
如果不考慮視頻應用,以上三個就是最核心和常用的模組了。針對視頻和一些特別的視覺應用,OpenCV也提供了強勁的支援:
video:用於視頻分析的常用功能,比如光流法(Optical Flow)和目標跟蹤等。
calib3d:三維重建,立體視覺和相機標定等的相關功能。
features2d:二維特徵相關的功能,主要是一些不受專利保護的,商業友好的特徵點檢測和匹配等功能,比如ORB特徵。
object:目標檢測模組,包含級聯分類和Latent SVM
ml:機器學習演算法模組,包含一些視覺中最常用的傳統機器學習演算法。
flann:最近鄰演算法庫,Fast Library for Approximate
Nearest Neighbors,用於在多維空間進行聚類和檢索,經常和關鍵點匹配搭配使用。
gpu:包含了一些gpu加速的介面,底層的加速是CUDA實現。
photo:計算攝像學(Computational Photography)相關的介面,當然這隻是個名字,其實只有映像修複和降噪而已。
stitching:映像拼接模組,有了它可以自己產生全景相片。
nonfree:受到專利保護的一些演算法,其實就是SIFT和SURF。
contrib:一些實驗性質的演算法,考慮在未來版本中加入的。
legacy:字面是遺產,意思就是廢棄的一些介面,保留是考慮到向下相容。
ocl:利用OpenCL並行加速的一些介面。
superres:超解析度模組,其實就是BTV-L1(Biliteral Total Variation – L1
regularization)演算法
viz:基礎的3D渲染模組,其實底層就是著名的3D工具包VTK(Visualization Toolkit)。
從使用的角度來看,和OpenCV2相比,OpenCV3的主要變化是更多的功能和更細化的模組劃分。 2、python——opencv基礎 2.1 映像的表示
已知單通道的灰階映像在電腦中的表示是一個8位無符號整形的矩陣,在oncv的C++代碼中,表示映像有專門的結構cv::Mat,但python中有numpy這種強大的基礎工具,所以該矩陣就用numpy的array表示,多通道就是紅綠藍(RGB)三通道。
2.2 基本處理
(1)讀取映像:cv.imread()
可以按照不同模式讀取,一般最常用到的是讀取單通道灰階圖,或者直接預設讀取多通道
(2)儲存映像:cv.imwrite()
import cv2color_img=cv2.imread('4.jpg')print(color_img.shape)# 讀取單通道gray_img=cv2.imread('4.jpg',cv2.IMREAD_GRAYSCALE)print(gray_img.shape)#把單通道映像儲存後,再讀取,仍然是3通道,相當於將單通道複製到3個通道儲存cv2.imwrite('grayscale_4.jpg',gray_img)reload_grayscale=cv2.imread('grayscale_4.jpg')print(reload_grayscale.shape)# 指定jpg品質,範圍從1~100,預設95,值越高畫質越好,檔案越大cv2.imwrite('anglababy.jpg',color_img,(cv2.IMWRITE_JPEG_QUALITY,20))
(2)縮放、裁剪、補邊
縮放:im.resize()
裁剪:利用array下標截取實現
import cv2img=cv2.imread('dog.jpg')# 縮小為200x200的正方形img_200x200=cv2.resize(img,(200,200))# 不直接指定縮放後的大小,通過fx和fy指定縮放比例,0.5表示長寬各一半# 插值方法預設為cv2.INTER_LINEAR,這裡指定為最近鄰插值img_half=cv2.resize(img,(0,0),fx=0.5,fy=0.5,interpolation=cv2.INTER_NEAREST)# 上下各貼50像素的黑邊img_add=cv2.copyMakeBorder(img,50,50,0,0,cv2.BORDER_CONSTANT,value=(0,0,0))# 裁剪patch_img=img[20:150,-180:-50]cv2.imshow("image",img_200x200)cv2.imshow("img_half",img_half)cv2.imshow("img_add",img_add)cv2.imshow("patch_img",patch_img)cv2.waitKey(0)
(3)色調、明暗、長條圖、Gamma曲線
比如可以通過HSV空間對色調和明暗進行調節。HSV空間是由美國的圖形學專家A. R. Smith提出的一種色彩空間,HSV分別是色調(Hue),飽和度(Saturation)和明度(Value)。在HSV空間中進行調節就避免了直接在RGB空間中調節是還需要考慮三個通道的相關性。OpenCV中H的取值是[0, 180),其他兩個通道的取值都是[0, 256),下面例子接著上面例子代碼,通過HSV空間對映像進行調整:
import cv2img=cv2.imread('mushroom.jpg')# 通過cv2.cvtcolor把映像從RGB轉到HSVimg_hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)# H空間中,綠色比黃色值高,所以給每個像素+15,黃色的就會變綠turn_green_hsv=img_hsv.copy()turn_green_hsv[:,:,0]=(turn_green_hsv[:,:,0]+15)turn_green_img=cv2.cvtColor(turn_green_hsv,cv2.COLOR_HSV2BGR)cv2.imshow("turn_green_img",turn_green_img)# 減小飽和度會讓映像損失鮮豔,變得更灰colorless_hsv=img_hsv.copy()colorless_hsv[:,:,1]=0.5*colorless_hsv[:,:,1]colorless_img=cv2.cvtColor(colorless_hsv,cv2.COLOR_HSV2BGR)cv2.imshow("colorless_img",colorless_img)# 減小為原來的一半darker_hsv=img_hsv.copy()darker_hsv[:,:,2]=0.5*darker_hsv[:,:,2]darker_img=cv2.cvtColor(darker_hsv,cv2.COLOR_HSV2BGR)cv2.imshow("darker_img",darker_img)cv2.waitKey(0)
長條圖:方便對映像的像素值分布瞭解更清晰,低的像素值表示暗的部分,高的值表示亮度大的部分,但是顯示的時候可能就出現了暗部細節不足或者亮部細節丟失的情況。
Gamma變換:提升暗部細節,Gamma變換是矯正相機直接成像和人眼感受映像差別的一種常用手段,簡單來說就是通過非線性變換讓映像從對曝光強度的線性響應變得更接近人眼感受到的響應。
import numpy as npimport cv2import matplotlib.pylab as pltfrom mpl_toolkits.mplot3d import Axes3Dimg=cv2.imread('4.jpg')# 分通道計算每個通道的長條圖hist_b=cv2.calcHist([img],[0],None,[256],[0,256])hist_g=cv2.calcHist([img],[1],None,[256],[0,256])hist_r=cv2.calcHist([img],[2],None,[256],[0,256])# Gamma變換的函數def gamma_trans(img,gamma): # 先歸一化到1,之後利用gamma作為指數求出新值,再還原 gamma_table=[np.power(x/255.0,gamma)*255.0 for x in range(256)] gamma_table=np.round(np.array(gamma_table)).astype(np.uint8) # 用opencv的查表函數實現該映射 return cv2.LUT(img,gamma_table)# 執行Gamma變換,小於1的值讓暗細節大量提升,同時亮部細節少量提升img_corrected=gamma_trans(img,0.5)cv2.imshow("img",img)cv2.imshow("img_corrected",img_corrected)# cv2.waitKey(0)# 分通道計算Gamma矯正後的長條圖hist_b_corrected=cv2.calcHist([img_corrected],[0],None,[256],[0,256])hist_g_corrected=cv2.calcHist([img_corrected],[1],None,[256],[0,256])hist_r_corrected=cv2.calcHist([img_corrected],[2],None,[256],[0,256])# 長條圖可視化fig=plt.figure()pix_hists=[ [hist_b,hist_g,hist_r], [hist_b_corrected,hist_g_corrected,hist_r_corrected]]pix_vals=range(256)for sub_plt,pix_hist in zip([121,122],pix_hists): ax=fig.add_subplot(sub_plt,projection='3d') for c,z,channel_hist in zip(['b','g','r'],[20,10,0],pix_hist): cs=[c]*256 ax.bar(pix_vals,channel_hist,zs=z,zdir='y',color=cs,alpha=0.618, edgecolor='none',lw=0) ax.set_xlabel('Pixel Values') ax.set_xlim([0,256]) ax.set_ylabel('Channels') ax.set_zlabel('Counts')plt.show()
(4)仿射變換
映像的仿射變換涉及到映像的形狀位置角度的變化,是深度學習預先處理中常到的功能,在此簡單回顧一下。仿射變換具體到映像中的應用,主要是對映像的縮放,旋轉,剪下,翻轉和平移的組合。在OpenCV中,仿射變換的矩陣是一個2×3的矩陣,其中左邊的2×2子矩陣是線性變換矩陣,右邊的2×1的兩項是平移項:
對於映像上的任一位置(x,y),仿射變換執行的是如下的操作:
需要注意的是,對於映像而言,寬度方向是x,高度方向是y,座標的順序和映像像素對應下標一致。所以原點的位置不是左下角而是右上方,y的方向也不是向上,而是向下。在OpenCV中實現仿射變換是通過仿射變換矩陣和cv2.warpAffine()這個函數:
import numpy as npimport cv2img = cv2.imread('dog.jpg')# 沿著橫縱軸放大1.6倍,之後平移(-150,-240),最後沿原圖大小截取,等效於剪裁併放大M_crop_dog = np.array([ [1.6, 0, -150], [0, 1.6, -240]], dtype=np.float32)# cv2.warpAffine(原始映像, 變換矩陣,變換後的映像大小)img_dog = cv2.warpAffine(img, M_crop_dog,(400, 400))cv2.imshow("img_dog", img_dog)# x軸的剪下變換,逆時針旋轉角度15°theta=15*np.pi/180M_shear=np.array([ [1,np.tan(theta),0], [0,1,0]],dtype=np.float32)img_sheared=cv2.warpAffine(img,M_shear,(400,600))cv2.imshow("img_sheared",img_sheared)# 順時針旋轉,角度15°M_rotate=np.array([ [np.cos(theta),-np.sin(theta),0], [np.sin(theta),np.cos(theta),0]],dtype=np.float32)im_rotate=cv2.warpAffine(img,M_rotate,(400,600))cv2.imshow("im_rotate",im_rotate)# 旋轉+縮放+旋轉組合,可以通過SVD分解理解M=np.array([ [1,1.5,-400], [0.5,2,-100]],dtype=np.float32)img_transformed=cv2.warpAffine(img,M,(400,600))cv2.imshow("img_transformed",img_transformed)cv2.waitKey(0)