cv2.add()——將兩張圖片的像素疊加起來,與兩張圖片直接相加結(jié)果不同
>>> x = np.uint8([250])
>>> y = np.uint8([10])
>>> print cv2.add(x,y) # 250+10 = 260 => 255 最高為255
[[255]]
>>> print x+y # 250+10 = 260 % 256 = 4
[4]
cv2.addWeighted() —— 以一定權(quán)重相加
img1 = cv2.imread('ml.png')
img2 = cv2.imread('opencv_logo.jpg')
#圖片1 0.7 圖片2 0.3
dst = cv2.addWeighted(img1,0.7,img2,0.3,0)
cv2.imshow('dst',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
- cv2.bitwise() —— 位圖操作,包括AND OR NOT XOR
# 載入兩張圖片
img1 = cv2.imread('messi5.jpg')
img2 = cv2.imread('opencv_logo.png')
# 創(chuàng)建一個(gè)ROI:region of image即圖片的一部分區(qū)域
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols ]
# 轉(zhuǎn)換顏色模式為灰度模式网棍;用cv2.threshold將圖片二值化處理痛侍,
#即將像素值高于或者低于設(shè)定閾值的像素置為0厅各,然后將低于或者高于的值置為其它顏色虫埂,
#需要四個(gè)參數(shù)cv2.threshold(灰度圖贷盲,閾值坏匪,設(shè)定值拟逮,方法),方法有五種:
#cv2.THRESH_BINARY - 二值化處理适滓,低于閾值的像素點(diǎn)灰度值置為0敦迄;高于閾值的值置為參數(shù)3
#cv2.THRESH_BINARY_INV - 大于閾值的像素點(diǎn)灰度值置為0;小于閾值置為參數(shù)3
#cv2.THRESH_TRUNC - 小于閾值的像素點(diǎn)灰度值不變凭迹,大于閾值的像素點(diǎn)置為該閾值
#cv2.THRESH_TOZERO - 小于閾值的像素點(diǎn)灰度值不變罚屋,大于閾值的像素點(diǎn)置為0,其中參數(shù)3任取
#cv2.THRESH_TOZERO_INV - 大于閾值的像素點(diǎn)灰度值不變,小于閾值的像素點(diǎn)置為0,其中參數(shù)3任取
img2gray = cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
ret, mask = cv2.threshold(img2gray, 10, 255, cv2.THRESH_BINARY)
#將圖片進(jìn)行反向掩碼的位操作得到logo圖像
mask_inv = cv2.bitwise_not(mask)
# 保留除logo外的背景
img1_bg = cv2.bitwise_and(roi,roi,mask = mask_inv)
# 去除除logo圖中l(wèi)ogo外的背景
img2_fg = cv2.bitwise_and(img2,img2,mask = mask)
# 進(jìn)行融合
dst = cv2.add(img1_bg,img2_fg)
# 融合后放在原圖上
img1[0:rows, 0:cols ] = dst
cv2.imshow('res',img1)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.blur(), cv2.GaussianBlur(), cv2.medianBlur(), cv2.bilateralFilter() —— 各種濾波進(jìn)行圖像平滑處理
首先要了解卷積是什么嗅绸?
卷積就是循環(huán)對(duì)圖像跟一個(gè)核逐個(gè)元素相乘再求和得到另外一副圖像的操作脾猛,比如結(jié)果圖中第一個(gè)元素5是怎么算的呢?原圖中3×3的區(qū)域與3×3的核逐個(gè)元素相乘再相加:1×1 + 2×0 + 1×0 + 0×0 + 1×0 + 1×0 + 3×0 + 0×0 + 2×2 = 5鱼鸠。算完之后猛拴,整個(gè)框再往右移一步繼續(xù)計(jì)算羹铅,橫向計(jì)算完后,再往下移一步繼續(xù)計(jì)算……網(wǎng)上有一副很經(jīng)典的動(dòng)態(tài)圖愉昆,方便我們理解卷積:
- 濾波有均值濾波职员、方框?yàn)V波、高斯濾波跛溉、中值濾波焊切、雙邊濾波
- 在不知道用什么濾波器好的時(shí)候,優(yōu)先高斯濾波cv2.GaussianBlur()芳室,然后均值濾波cv2.blur()
- 斑點(diǎn)和椒鹽噪聲優(yōu)先使用中值濾波cv2.medianBlur()
- 要去除噪點(diǎn)的同時(shí)盡可能保留更多的邊緣信息蛛蒙,使用雙邊濾波cv2.bilateralFilter()
- 線性濾波方式:均值濾波、方框?yàn)V波渤愁、高斯濾波(速度相對(duì)快)
-
非線性濾波方式:中值濾波牵祟、雙邊濾波(速度相對(duì)慢)
從左到右,從上到下依次為均值濾波抖格、高斯濾波诺苹、中值濾波、雙邊濾波
cv2.threshold, cv2.adaptiveThreshold —— 圖像閾值化
簡單的閾值處理cv2.threshold在基礎(chǔ)操作已經(jīng)講過雹拄,接下來說高級(jí)處理cv2.adaptiveThreshold
有三個(gè)輸入變量和一個(gè)輸出量:
- Adaptive Method - 決定閾值計(jì)算類型.
- cv2.ADAPTIVE_THRESH_MEAN_C : 根據(jù)相鄰區(qū)域
- cv2.ADAPTIVE_THRESH_GAUSSIAN_C : 通過高斯函數(shù)窗口
- Block Size - 決定相鄰區(qū)域的范圍
- C -計(jì)算中需要的一個(gè)常數(shù)
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('dave.jpg',0)
img = cv2.medianBlur(img,5)
ret,th1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
th2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,\
cv2.THRESH_BINARY,11,2)
th3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,11,2)
titles = ['Original Image', 'Global Thresholding (v = 127)',
'Adaptive Mean Thresholding', 'Adaptive Gaussian Thresholding']
images = [img, th1, th2, th3]
for i in range(4):
plt.subplot(2,2,i+1),plt.imshow(images[i],'gray')
plt.title(titles[i])
plt.xticks([]),plt.yticks([])
plt.show()
孰優(yōu)孰劣顯而易見收奔!
幾何變換
縮放
import cv2
import numpy as np
img = cv2.imread('messi5.jpg')
# 縮放圖片,就一行代碼滓玖,很簡單
res = cv2.resize(img,None,fx=2, fy=2, interpolation = cv2.INTER_CUBIC)
#OR
height, width = img.shape[:2]
res = cv2.resize(img,(2*width, 2*height), interpolation = cv2.INTER_CUBIC)
旋轉(zhuǎn)坪哄、平移
平移
import cv2
import numpy as np
img = cv2.imread('messi5.jpg',0)
rows,cols = img.shape
M = np.float32([[1,0,100],[0,1,50]])
# 用cv2.warpAffine平移一段距離M
dst = cv2.warpAffine(img,M,(cols,rows))
cv2.imshow('img',dst)
cv2.waitKey(0)
cv2.destroyAllWindows()
旋轉(zhuǎn)
img = cv2.imread('messi5.jpg',0)
rows,cols = img.shape
M = cv2.getRotationMatrix2D((cols/2,rows/2),90,1)
# 用cv2.warpAffine旋轉(zhuǎn)
dst = cv2.warpAffine(img,M,(cols,rows))
仿射
對(duì)于仿射變換,我們只需要知道變換前的三個(gè)點(diǎn)與其對(duì)應(yīng)的變換后的點(diǎn)势篡,就可以通過cv2.getAffineTransform求得變換矩陣.
img = cv2.imread('sudokusmall.png')
rows,cols,ch = img.shape
pts1 = np.float32([[56,65],[368,52],[28,387],[389,390]])
pts2 = np.float32([[0,0],[300,0],[0,300],[300,300]])
M = cv2.getPerspectiveTransform(pts1,pts2)
dst = cv2.warpPerspective(img,M,(300,300))
plt.subplot(121),plt.imshow(img),plt.title('Input')
plt.subplot(122),plt.imshow(dst),plt.title('Output')
plt.show()
形態(tài)學(xué)操作
形態(tài)學(xué)操作是指改變物體形狀翩肌,一般作用于二值化圖,來連接相鄰的元素或分離成獨(dú)立的元素禁悠。腐蝕和膨脹是針對(duì)圖片中的白色部分念祭!
腐蝕
import cv2
import numpy as np
img = cv2.imread('j.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(img,kernel,iterations = 1)
膨脹
dilation = cv2.dilate(img,kernel,iterations = 1)
開運(yùn)算
先腐蝕后膨脹叫開運(yùn)算(因?yàn)橄雀g會(huì)分開物體,這樣容易記装臁)粱坤,其作用是:分離物體,消除小區(qū)域瓷产。
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
閉運(yùn)算
先膨脹后腐蝕(先膨脹會(huì)使白色的部分?jǐn)U張站玄,以至于消除/“閉合”物體里面的小黑洞,所以叫閉運(yùn)算)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
形態(tài)學(xué)梯度運(yùn)算
膨脹圖減去腐蝕圖濒旦,這樣會(huì)得到物體的輪廓
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
不常用株旷,不做解釋
##### 頂帽
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
##### 黑帽
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
圖像梯度
如果你還記得高數(shù)中用一階導(dǎo)數(shù)來求極值的話,就很容易理解了:把圖片想象成連續(xù)函數(shù)疤估,因?yàn)檫吘壊糠值南袼刂凳桥c旁邊像素明顯有區(qū)別的灾常,所以對(duì)圖片局部求極值,就可以得到整幅圖片的邊緣信息了铃拇。不過圖片是二維的離散函數(shù)钞瀑,導(dǎo)數(shù)就變成了差分,這個(gè)差分就稱為圖像的梯度慷荔。
濾波是應(yīng)用卷積來實(shí)現(xiàn)的雕什,卷積的關(guān)鍵就是卷積核,我們來考察下面這個(gè)卷積核:這個(gè)核是用來提取圖片中的垂直邊緣的显晶,怎么做到的呢贷岸?看下圖:
當(dāng)前列左右兩側(cè)的元素進(jìn)行差分,由于邊緣的值明顯小于(或大于)周邊像素磷雇,所以邊緣的差分結(jié)果會(huì)明顯不同偿警,這樣就提取出了垂直邊緣。同理唯笙,把上面那個(gè)矩陣轉(zhuǎn)置一下螟蒸,就是提取水平邊緣。這種差分操作就叫圖像的梯度計(jì)算:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('dave.jpg',0)
laplacian = cv2.Laplacian(img,cv2.CV_64F)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=5)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()
邊緣檢測
邊緣檢測是圖像處理和計(jì)算機(jī)視覺中的基本問題崩掘,邊緣檢測的目的是標(biāo)識(shí)數(shù)字圖像中亮度變化明顯的點(diǎn)七嫌。圖像屬性中的顯著變化通常反映了屬性的重要事件和變化。
侃尼邊緣檢測原理
侃尼邊緣檢測是一個(gè)流行的邊緣檢測算法苞慢,1986年由 John F. Canny提出诵原。包括四個(gè)步驟:
- 減少噪聲
因?yàn)檫吘墮z測容易受到噪聲的影響,因此使用5×5高斯濾波消除噪聲挽放,前面已經(jīng)提到過绍赛。
-
計(jì)算圖像梯度
首先用上面提到的Sobel方法提取水平垂直兩個(gè)方向的邊緣,然后根據(jù)以下公式計(jì)算出每條邊的梯度及其梯度方向:
-
非極大值抑制
在找到梯度后褪测,應(yīng)該對(duì)整幅圖像做一個(gè)掃描,去除那些非邊界上的點(diǎn)潦刃。對(duì)每一個(gè)像素進(jìn)行檢查侮措,看這個(gè)點(diǎn)的梯度是不是周圍具有相同梯度方向的點(diǎn)中最大的。如下所示:
非極大值抑制
簡而言之,你得到的結(jié)果是一個(gè)二進(jìn)制圖像,“薄邊緣”墨状。
-
滯后閾值法
這個(gè)階段決定誰是邊緣,誰不是邊緣
滯后閾值法
- 像素點(diǎn)的值大于最高閾值菲饼,那肯定是邊緣(上圖A)
- 同理像素值小于最低閾值肾砂,那肯定不是邊緣
- 像素值介于兩者之間,如果與高于最高閾值的點(diǎn)連接宏悦,也算邊緣镐确,所以上圖中C算,B不算
進(jìn)過這一步就得到了清晰地邊界饼煞。
OpenCV中Canny邊緣檢測的使用
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('messi5.jpg',0)
# 第一個(gè)參數(shù)為圖片源葫,第二第三個(gè)參數(shù)為上述原理第四步的最大最小閾值
edges = cv2.Canny(img,100,200)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()
圖像匹配
SIFT算法由D.G.Lowe 1999年提出,2004年完善總結(jié)砖瞧,論文發(fā)表在2004年的IJCV上息堂,主要用于提取具有圖像旋轉(zhuǎn)不變性和伸縮不變性的特征點(diǎn)。這項(xiàng)技術(shù)可以推廣到圖像識(shí)別块促、圖像拼接以及圖像恢復(fù)等储矩。
David G. Lowe, "Distinctive image features from scale-invariant keypoints," International Journal of Computer Vision,60, 2 (2004), pp. 91-110
論文詳細(xì)地址
其中就用到了OpenCV的cv2.pyrDown()
和cv2.pyrUp()
函數(shù)來創(chuàng)建圖像金字塔。
圖像金字塔主要有兩類:高斯金字塔和拉普拉斯金字塔褂乍。
高斯金字塔的頂部是通過將底部圖像的連續(xù)行與列去掉得到的持隧。每一層圖像中的像素值等于下一層圖像中對(duì)應(yīng)位置5個(gè)像素的高斯加權(quán)平均值。這樣操作一個(gè)MN的圖像就變成了(M/2)(N/2)的圖像逃片,圖像的面積就變?yōu)樵瓉淼?/4屡拨,連續(xù)進(jìn)行這樣的操作,就會(huì)得到一些列的金字塔的圖像褥实。
一個(gè)小應(yīng)用
import cv2
import numpy as np,sys
A = cv2.imread('apple.jpg')
B = cv2.imread('orange.jpg')
# 生成蘋果圖像金字塔
G = A.copy()
gpA = [G]
for i in range(6):
G = cv2.pyrDown(G)
gpA.append(G)
# 生成橘子圖像金字塔
G = B.copy()
gpB = [G]
for i in range(6):
G = cv2.pyrDown(G)
gpB.append(G)
# 生成蘋果圖像拉普拉斯金字塔
lpA = [gpA[5]]
for i in range(5,0,-1):
GE = cv2.pyrUp(gpA[i])
L = cv2.subtract(gpA[i-1],GE)
lpA.append(L)
# 生成橘子圖像拉普拉斯金字塔
lpB = [gpB[5]]
for i in range(5,0,-1):
GE = cv2.pyrUp(gpB[i])
L = cv2.subtract(gpB[i-1],GE)
lpB.append(L)
# 添加蘋果呀狼、橘子各一半的圖像
LS = []
for la,lb in zip(lpA,lpB):
rows,cols,dpt = la.shape
ls = np.hstack((la[:,0:cols/2], lb[:,cols/2:]))
LS.append(ls)
# 重新組合
ls_ = LS[0]
for i in range(1,6):
ls_ = cv2.pyrUp(ls_)
ls_ = cv2.add(ls_, LS[i])
# 直接組合
real = np.hstack((A[:,:cols/2],B[:,cols/2:]))
cv2.imwrite('Pyramid_blending2.jpg',ls_)
cv2.imwrite('Direct_blending.jpg',real)
未完待續(xù)
關(guān)于OpenCV的學(xué)習(xí)就先告一段落,接下來我會(huì)更新關(guān)于機(jī)器學(xué)習(xí)的內(nèi)容损离,然后再用到關(guān)于OpenCV的內(nèi)容會(huì)再更新哥艇,大家有什么需要的也可以留言告訴我,我也會(huì)考慮更新的僻澎。