使用OpenCV怎么實現輪廓檢測?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
輪廓概述
輪廓可以簡單認為成將連續的點(連著邊界)連在一起的曲線,具有相同的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。
為了更加準確,要使用二值化圖像。在尋找輪廓之前,要進行閾值化處理或者 Canny 邊界檢測。
查找輪廓的函數會修改原始圖像。如果你在找到輪廓之后還想使用原始圖像的話,你應該將原始圖像存儲到其他變量中。
在 OpenCV 中,查找輪廓就像在黑色背景中超白色物體,要找的物體應該是白色而背景應該是黑色。
輪廓檢測的作用:
1.可以檢測圖圖像或者視頻中物體的輪廓
2.計算多邊形邊界,形狀逼近和計算感興趣區域
先看一個較為簡單的輪廓檢測:
import cv2 import numpy as np # 創建一個200*200的黑色空白圖像 img = np.zeros((200, 200), dtype=np.uint8) # 利用numpy數組在切片上賦值的功能放置一個白色方塊 img[50:150, 50:150] = 255 # 對圖像進行二值化操作 # threshold(src, thresh, maxval, type, dst=None) # src是輸入數組,thresh是閾值的具體值,maxval是type取THRESH_BINARY或者THRESH_BINARY_INV時的最大值 # type有5種類型,這里取0: THRESH_BINARY ,當前點值大于閾值時,取maxval,也就是前一個參數,否則設為0 # 該函數第一個返回值是閾值的值,第二個是閾值化后的圖像 ret, thresh = cv2.threshold(img, 127, 255, 0) # findContours()有三個參數:輸入圖像,層次類型和輪廓逼近方法 # 該函數會修改原圖像,建議使用img.copy()作為輸入 # 由函數返回的層次樹很重要,cv2.RETR_TREE會得到圖像中輪廓的整體層次結構,以此來建立輪廓之間的‘關系'。 # 如果只想得到最外面的輪廓,可以使用cv2.RETE_EXTERNAL。這樣可以消除輪廓中其他的輪廓,也就是最大的集合 # 該函數有三個返回值:修改后的圖像,圖像的輪廓,它們的層次 image, 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, 255, 0), 2) cv2.imshow("contours", color) cv2.waitKey() cv2.destroyAllWindows()
上面是找到一個正方形的輪廓,下面看如何找到不規則的多邊形輪廓:
import cv2 import numpy as np # pyrDown():brief Blurs an image and downsamples it. # 將圖像高斯平滑,然后進行降采樣 img = cv2.pyrDown(cv2.imread("hammer.jpg", cv2.IMREAD_UNCHANGED)) # 依然是二值化操作 ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY) # 計算圖像的輪廓 image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for c in contours: # find bounding box coordinates # 先計算出一個簡單的邊界狂,也就是一個矩形啦 # 就是將輪廓信息轉換為(x,y)坐標,并加上矩形的高度和寬度 x, y, w, h = cv2.boundingRect(c) # 畫出該矩形 cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2) # find minimum area # 然后計算包圍目標的最小矩形區域 # 這里先計算出最小矩形區域,然后計算區域的頂點,此時頂點坐標是浮點型,但是像素坐標是整數 # 需要將浮點型轉換成矩形 rect = cv2.minAreaRect(c) box = cv2.boxPoints(rect) box = np.int0(box) # draw contours # 畫出最小矩形 # drawContours()也會修改源圖像 # 第二個參數保存輪廓的數組,也就是保存著很多輪廓 # 第三個參數是要繪制的輪廓數組的索引:-1是繪制所有的輪廓,否則只繪制[box]中指定的輪廓 # 顏色和thickness(密度,就是粗細)放在最后兩個參數 cv2.drawContours(img, [box], 0, (0, 0, 255), 3) # calculate center and radius of minimum enclosing circle # 最后檢查的邊界輪廓為最小閉圓 # minEnclosingCircle()會返回一個二元數組,第一個是圓心坐標組成的元祖,第二個元素是元的半徑 (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, (255, 0, 0), 3) # 繪制輪廓 cv2.drawContours(img, contours, -1, (255, 0, 0), 1) cv2.imshow("contours", img) cv2.waitKey() cv2.destroyAllWindows()
凸輪廓與Douglas-Peucker算法
import cv2 import numpy as np img = cv2.pyrDown(cv2.imread("hammer.jpg", cv2.IMREAD_UNCHANGED)) ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY), 127, 255, cv2.THRESH_BINARY) # 創建與源圖像一樣大小的矩陣 black = cv2.cvtColor(np.zeros((img.shape[1], img.shape[0]), dtype=np.uint8), cv2.COLOR_GRAY2BGR) image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) for cnt in contours: # 得到輪廓的周長作為參考 epsilon = 0.01 * cv2.arcLength(cnt,True) # approxPolyDP()用來計算近似的多邊形框。有三個參數 # cnt為輪廓,epsilon為ε——表示源輪廓與近似多邊形的最大差值,越小越接近 # 第三個是布爾標記,用來表示這個多邊形是否閉合 approx = cv2.approxPolyDP(cnt,epsilon,True) # convexHull()可以從輪廓獲取凸形狀 hull = cv2.convexHull(cnt) # 源圖像輪廓-綠色 cv2.drawContours(black, [cnt], -1, (0, 255, 0), 2) # 近似多邊形-藍色 cv2.drawContours(black, [approx], -1, (255, 0, 0), 2) # 凸包-紅色 cv2.drawContours(black, [hull], -1, (0, 0, 255), 2) cv2.imshow("hull", black) cv2.waitKey() cv2.destroyAllWindows()
本來也有疑問,有了一個精確的輪廓,為什么還需要一個近似的多邊形?
書中給出答案,近似多邊形是由一組直線構成,這樣可以便于后續的操作和處理。
想來也是,直線構成的區域總是比無限個曲率的曲線構成的區域方便處理。
直線和圓檢測
直線檢測可以通過HoughLinesP函數完成,HoughLinesP是標準Hough變換經過優化,使用概率Hough變換。
import cv2 import numpy as np img = cv2.imread('lines.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray,50,120) # 最小直線長度,小于該長度會被消除 minLineLength = 20 # 最大線段間隙,一條直線的間隙長度大于這個值會被認為是兩條直線 maxLineGap = 5 # HoughLinesP()會接受一個由Canny邊緣檢測濾波器處理過的單通道二值圖像 # 不一定需要Canny濾波器,但是輸入是去噪且只有邊緣的圖像,效果會很好 # 第一個參數是輸入圖像 # 第二、第三個參數是線段的幾何表示rho和theta,一般取1和np.pi/180 # 第四個參數是閾值,低于該閾值的直線會被忽略 # 第五第六已經解釋 lines = cv2.HoughLinesP(edges,1,np.pi/180,20,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()
圓檢測可以通過HoughCircles函數檢測。
import cv2 import numpy as np planets = cv2.imread('planet_glow.jpg') 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("HoughCirlces", planets) cv2.waitKey() cv2.destroyAllWindows()
看完上述內容,你們掌握使用OpenCV怎么實現輪廓檢測的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。