3.5、Canny邊緣檢測
OpenCV提供了Canny邊緣檢測函數來識別邊緣。它有5個步驟:使用高斯濾波器對圖像進行去噪、計算梯度、在邊緣上使用最大抑制(NMS)、在檢測到的邊緣上使用雙閥值去除
假陽性(false positive),最后還會分析出所有的邊緣及其之間的連接,以保留真正的邊緣並消除不明顯的邊緣。
import cv2 import numpy as np img = cv2.imread("flower.jpg") cv2.imwrite("canny.jpg", cv2.Canny(img, 200, 300)) cv2.imshow("image", img) cv2.imshow("canny", cv2.imread("canny.jpg")) cv2.waitKey() cv2.destroyAllWindows()
運行效果:
3.6、輪廓檢測
在計算機視覺中,輪廓檢測是另一個比較重要的任務,不單是用來檢測圖像或視頻中物體的輪廓,而且還有其他操作與輪廓檢測有關。這些操作有:計算多邊形邊界、
形狀逼近和計算感興趣區域。這是與圖像數據交互時的簡單操作,因為NumPy中的矩形區域可以使用數組切片(slice)來定義。在介紹物體檢測(包括人臉)和物體跟蹤的概念時會大量使用這種技術。
import cv2 import numpy as np import os img = np.zeros((200, 200), dtype = np.uint8) # 創建一個200x200大小的黑色空白圖像, img[50:150, 50:150] = 255 # 在圖像的中央放置一個白色方塊 ret, thresh = cv2.threshold(img, 127, 255, 0) #對圖像進行二值化操作 contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # 尋找輪廓 color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) # 顏色空間轉換 img = cv2.drawContours(color, contours, -1, (0, 0, 255), 5) # 畫出輪廓,-1,表示所有輪廓,畫筆顏色為(0, 255, 255),即Red,粗細為5 cv2.imshow("contours",color) cv2.waitKey() cv2.destroyAllWindows()
findContours()函數有三個參數:輸入圖像、層次類型和輪廓逼近方法。
這個函數會修改輸入圖像,因此建議使用原始圖像的一份拷貝(如:通過img.copy()來作為輸入圖像)。
由函數返回的層次樹相當重要:cv2.RETR_TREE參數會得到圖像中輪廓的整體層次結構,以此來建立輪廓之間的“關系”。如果只想得到最外面的輪廓,可使用cv2.RETR_EXTERNAL。
這對消除包含在其他輪廓中的輪廓很有用(如在大多數情形下,不需要檢測一個目標包含在另一個與之相同的目標里面)
findContours()函數有三個返回值:修改后的圖像、圖像的輪廓以及它們的層次。使用輪廓來畫出圖像的彩色版本(即把輪廓畫成綠色),並顯示出來。
注意:返回三個值報錯,實際上只返回兩個值(圖像的輪廓以及它們的層次)
cv2.threshold()簡單閾值
這個函數有四個參數,第一個原圖像,第二個進行分類的閾值,第三個是高於(低於)閾值時賦予的新值,第四個是一個方法選擇參數,常用的有:
cv2.THRESH_BINARY(黑白二值)
cv2.THRESH_BINARY_INV(黑白二值反轉)
cv2.THRESH_TRUNC (得到的圖像為多像素值)
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INV
該函數有兩個返回值,第一個retVal(得到的閾值(在后面一個方法中會用到)),第二個就是閾值化后的圖像。
cvCvtColor(...)是Opencv里的顏色空間轉換函數,可以實現RGB顏色向HSV,HSI等顏色空間的轉換,也可以轉換為灰度圖像。參數CV_RGB2GRAY是RGB到gray。參數 CV_GRAY2RGB是gray到RGB.處理結果是彩色的,則轉灰色就是了
運行效果如下:
3.7、邊界框、最小矩形區域和最小閉圓的輪廓
找到一個正方形輪廓很簡單,要找到不規則的、歪斜的以及旋轉的形狀可用 OpenCV的cv2.findContours函數,它能得到最好的結果,下面來看一幅圖像:
現實的應用對目標的邊界框、最小矩形面積、最小閉圓特別感興趣。將cv2.findContours函數與少量的OpenCV的功能想結合就能非常容易地實現這些功能。
繪制矩形
函數:cv2.rectangle(img,(380,0),(511,111),(255,0,0),3),需要確定的就是矩形的兩個點(左上角與右下角),顏色,線的類型(不設置就默認)
import cv2 import numpy as np import os from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8) #生成一個空彩色圖像 cv2.rectangle(img,(20,20),(411,411),(0,255,0),5) plt.imshow(img,'brg') plt.show()
import cv2 import numpy as np import os img = cv2.pyrDown(cv2.imread("hammer.png", cv2.IMREAD_UNCHANGED)) ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY) contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: # find bounding box coordinates # 現計算出一個簡單的邊界框 x, y, w, h = cv2.boundingRect(c) # 將輪廓信息轉換成(x, y)坐標,並加上矩形的高度和寬度 cv2.rectangle(img, (x,y), (x+w, y+h), (0, 255, 0), 2) # 畫出矩形 # find minimum area # 計算包圍目標的最小矩形區域 rect = cv2.minAreaRect(c) # calculate coordinate of the minimum area rectangle box = cv2.boxPoints(rect) # normalize coordinates to integers box =np.int0(box) # 注:OpenCV沒有函數能直接從輪廓信息中計算出最小矩形頂點的坐標。所以需要計算出最小矩形區域, # 然后計算這個矩形的頂點。由於計算出來的頂點坐標是浮點型,但是所得像素的坐標值是整數(不能獲取像素的一部分), # 所以需要做一個轉換 # draw contours cv2.drawContours(img, [box], 0, (0, 0, 255), 3) # 畫出該矩形 # calculate center and radius of minimum enclosing circle (x, y), radius = cv2.minEnclosingCircle(c) # 會返回一個二元組,第一個元素為圓心的坐標組成的元組,第二個元素為圓的半徑值。 # cast to integers center = (int(x), int(y)) radius = int(radius) # draw the circle img = cv2.circle(img, center, radius, (0, 255, 0), 2) cv2.drawContours(img, contours, -1, (255, 0, 0), 1) cv2.imshow("contours", img) cv2.waitKey() cv2.destroyAllWindows()
在導入模塊后,加載圖像,然后在源圖像的灰度圖像上執行一個二值化操作。這樣做之后,可在這個灰度圖像上執行所有計算輪廓的操作,但在源圖像上可利用色彩信息來畫這些輪廓。
cv2.drawContours(img, [box], 0, (0, 0, 255), 3):首先,該函數與所有繪圖函數一樣,它會修改源圖像。其次,該函數的第二個參數接收一個保存着輪廓的數組,
從而可以在一次操作中繪制一系列的輪廓。因此如果只有一組點來表示多邊形輪廓,就需要把這組點放到一個數組里。該函數的第三個參數是要繪制的輪廓數組的索引:-1表示繪制所有的輪廓,
否則只會繪制輪廓數組里指定的輪廓。大多數繪圖函數把繪圖的顏色和密度(thickness)放在最后兩個參數里。
函數介紹:
cv2.imread():讀入圖片,共兩個參數,第一個參數為要讀入的圖片文件名,第二個參數為如何讀取圖片,包括cv2.IMREAD_COLOR:讀入一副彩色圖片;
cv2.IMREAD_GRAYSCALE:以灰度模式讀入圖片;cv2.IMREAD_UNCHANGED:讀入一幅圖片,並包括其alpha通道。cv2.imread('flower.jpg',0)表示已灰度模式讀入
cv2.imshow():創建一個窗口顯示圖片,共兩個參數,第一個參數表示窗口名字,可以創建多個窗口中,但是每個窗口不能重名;第二個參數是讀入的圖片。
cv2.waitKey():鍵盤綁定函數,共一個參數,表示等待毫秒數,將等待特定的幾毫秒,看鍵盤是否有輸入,返回值為ASCII值。如果其參數為0,則表示無限期的等待鍵盤輸入。
cv2.destroyAllWindows():刪除建立的全部窗口
cv2.destroyWindows():刪除指定的窗口。
cv2.imwrite():保存圖片,共兩個參數,第一個為保存文件名,第二個為讀入圖片
Opencv中可以通過函數cv2.pyrDown()和cv2.pyrUp()來構建金字塔。
函數cv2.pyrDown()是從一個高分辨率圖像變成低分辨率圖像的。cv2.pyrDown()函數接受3個參數:
tmp: 當前圖像,初始化為原圖像 src 。
dst: 目的圖像( 顯示圖像,為輸入圖像的一半)
Size( tmp.cols/2, tmp.rows/2 ) :目的圖像大小, 既然我們是向下采樣
凸輪廓與Douglas-Peucker算法
大多數處理輪廓的時候,物體的形狀(包括凸形狀)都是變化多樣的。凸形狀內部的任意兩點之間的連線都在該形狀里面。
cv2.approxPloyDP是一個OpenCV函數,它用來計算近似的多邊形框。該函數有三個參數:
第一個參數為“輪廓”;
第二個參數為“ε值”,它表示源輪廓與近似多邊形的最大差值(這個值越小,近似多邊形與源輪廓越接近);
第三個參數為“布爾標記”,它表示這個多邊形是否閉合。
ε值對獲取有用的輪廓非常重要。是為所得到的近似多邊形周長與源輪廓周長之間的最大差值,這個差值越小,近似多邊形與源輪廓就越相似。
可通過OpenCV的cv2.arcLength函數來得到輪廓的周長信息。
epsilon = 0.01 * cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
可通過OpenCV來有效地計算一個近似多邊形,多邊形周長與源輪廓周長之比就為ε。
為了計算凸形狀,需要用OpenCV的cv2.convexHull函數來獲取處理過的輪廓信息,代碼為:
hull = cv2.convexHull(cnt)
3.8、直線和圓檢測
Hough變換是直線和形狀檢測背后的理論基礎。
繪制直線
函數為:cv2.line(img,Point pt1,Point pt2,color,thickness=1,line_type=8 shift=0)
有值的代表有默認值,不用給也行。可以看到這個函數主要接受參數為兩個點的坐標,線的顏色(彩色圖像的話顏色就是一個1*3的數組)
import cv2 import numpy as np import os from matplotlib import pyplot as plt img = np.zeros((512,512),np.uint8)#生成一個空灰度圖像 cv2.line(img,(0,200),(511,511),255,5) plt.imshow(img,'gray') plt.show()
import cv2 import numpy as np import os from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8)#生成一個空彩色圖像 cv2.line(img,(200,0),(511,511),(0,255,0),5) plt.imshow(img,'brg') plt.show()
3.9、直線檢測
直線檢測可通過HoughLines和HoughLinesP函數來完成,它們僅有的差別是:第一個函數使用標准的Hough變換,第二個函數使用概率Hough變換。HoughLinesP函數之所以成為
概率版本的Hough變換是因為它只通過分析點的子集並估計這些點都屬於一條直線的概率,這是標准Hough變換的優化版本,該函數的計算代價會少一些,執行會變得更快。
import cv2 import numpy as np img = cv2.imread("lines.png") gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 120) minLineLength = 20 maxLineGap = 5 lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength, maxLineGap) for x1, y1, x2, y2 in lines[0]: cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imshow("edges", edges) cv2.imshow("lines", img) cv2.waitKey() cv2.destroyAllWindows()
HoughLines函數會接收一個由Canny邊緣檢測濾波器處理過的單通道二值圖像,不一定需要Canny濾波器,但是一個經過去噪並只有邊緣的圖像當作Hough變換的輸入會很不錯。
HoughLinesP的參數:
需要處理的參數;
線段的幾何表示rho和theta,一般分別取1和np.pi/180;
閾值,低於該閾值的直線會被忽略,Hough變換可以理解為投票箱和投票數之間的關系,每一個投票箱代表一個直線,投票數達到閾值的直線會被保留,其他的會被刪除。
minLineLength和maxLineGap
3.10、繪制圓
繪制圓形只需要確定圓心與半徑,函數: cv2.circle (img,(380,0),63,(255,0,0),3),
import cv2 import numpy as np from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8)#生成一個空彩色圖像 cv2.circle(img,(100,100),50,(55,255,155),5) plt.imshow(img,'brg') plt.show(
3.11、圓檢測
OpenCV的HoughCircles函數可用來檢測圓,它與使用HoughLines函數類似。像用來決定刪除或保留直線的兩個參數minLineLength和maxLineGap一樣,
HoughCircles有一個圓心間的最小距離和圓的最小及最大半徑。
import cv2 import numpy as np planets = cv2.imread("planet_glow.png") gray_img = cv2.cvtColor(planets, cv2.COLOR_BGR2GRAY) img = cv2.medianBlur(gray_img, 5) cimg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) circles = cv2.HoughCircles(img, cv2.HOUGH_GRADIENT, 1, 120, param1=100, param2 = 30, minRadius = 0, maxRadius = 0) circles = np.uint16(np.around(circles)) for i in circles[0,:]: # draw the outer circle cv2.circle(planets, (i[0], i[1]), i[2],(0, 255, 0),2) # draw the center of the circle cv2.circle(planets, (i[0], i[1]), 2, (0, 0,255), 3) cv2.imwrite("planets_circles.jpg",planets) cv2.imshow("HoughCircles", planets) cv2.waitKey() cv2.destroyAllWindows()
3.12、繪制橢圓
橢圓涉及到長軸短軸,橢圓圓心,旋轉角度等。
import cv2 import numpy as np from matplotlib import pyplot as plt img = np.zeros((512,512,3),np.uint8)#生成一個空彩色圖像 cv2.ellipse(img,(256,256),(150,100),0,0,180,250,-1) #注意最后一個參數-1,表示對圖像進行填充,默認是不填充的,如果去掉,只有橢圓輪廓了 plt.imshow(img,'brg') plt.show()
3.13、檢測其他形狀
Hough變換能檢測的形狀僅限於圓,前面提到過檢測任何形狀的方法,特別是用approxPloyDP函數來檢測,該函數提供多邊形的近似,所以如果你的圖像有多邊形,再結合cv2.findContours函數和
cv2.approxPloyDP函數,就能相當准確地檢測出來。