###### tags: `OpenCV`,`輪廓檢測` # OpenCV 輪廓檢測- contour detection(籃網偵測,字母模板偵測) * 輪廓通常是從邊緣獲得的,但它們的目標是成為對象輪廓,因此,它們需要是閉合曲線。 **contour偵測處理流程** ![](https://i.imgur.com/eIMevYA.jpg 把輪廓找出來- **findContours語法** contours, hierarchy = cv2.findContours(source_image, retrieval_mode, approx_method) output的部分有兩塊 1. contours-找出來的輪廓 2. hierarchy-找出來的輪廓,彼此間有沒有關係? 有沒有小輪廓是在大輪廓裡面的? input的部分 1.source_image-目標照片 2.retrieval_mode-擷取模式 * cv2.RETR_EXTERNAL-只擷取最外圍的輪廓 * cv2.RETR_LIST-擷取大大小小所有輪廓,擷取結果沒有父子關係,大家都平等 * cv2.RETR_CCOMP-會列出內、外兩層關係 * cv2.RETR_TREE-會列出完整所有關係 3.approx_method-輪廓紀錄方式 * cv2.CHAIN_APPROX_NONE-最精細紀錄模式 * cv2.CHAIN_APPROX_SIMPLE-只記錄畫出輪廓的關鍵點 把輪廓畫出來- **drawContours語法** marked_img = cv2.drawContours(img, contours, contourIdx,color,thickness, lineType = cv.LINE_8, hierarchy = cv.Mat(), maxLevel = INT_MAX, offset = cv.Point(0, 0))) input的部分 1.img-BGR目標照片,要標註輪廓的照片 2.contours-我們偵測到的輪廓 3.contourIdx-你要畫的輪廓,如果你要畫全部的輪廓,就用-1 4.color-BGR,畫輪廓的顏色 5.lineType 6.offset-偏離程度 :star:進行contour偵測前,有一個關鍵,圖最好是黑底白圖(目標是白色的) 實作 ```python import cv2 image = cv2.imread('basketball.jpg') #我們等一下需要一張乾淨的底圖來標示 imageCopy= image.copy() cv2.imshow('BGR image', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/FOrdtqo.jpg) ```python #轉灰階 gray_image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) cv2.imshow('gray', gray_image) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/hxJhip4.jpg) ```python #轉二元圖 ret,binary_im = cv2.threshold(gray_image,100, 255,cv2.THRESH_BINARY) cv2.imshow('binary', binary_im) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/OQaO7ib.jpg) ```python contours,hierarchy = cv2.findContours(binary_im,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) #設定一下drawContours的參數 contours_to_plot= -1 #畫全部 plotting_color= (0,255,0)#畫綠色框 thickness= -1 #開始畫contours with_contours = cv2.drawContours(image,contours, contours_to_plot, plotting_color,thickness) cv2.imshow('contours', with_contours) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/rgHHVyU.jpg) ```python #標示矩形邊框 for cnt in contours: x,y,w,h = cv2.boundingRect(cnt) image = cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,255),2) cv2.imshow('contours', image) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/KKiHV7O.jpg) ```python #根據面積,挑出籃網的部分 required_contour = max(contours, key = cv2.contourArea) x,y,w,h = cv2.boundingRect(required_contour) img_copy2 = cv2.rectangle(imageCopy, (x,y),(x+w, y+h),(0,255,255),2) cv2.imshow('largest contour', img_copy2) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/tpBZGD4.jpg) **cv2.boundingRect** 矩形邊框(Bounding Rectangle)是說,用一個最小的矩形,把找到的形狀包起來。 cv2.boundingRect(img) img是一個二值圖,也就是它的參數; 返回四個值,分別是x,y,w,h( x,y是矩型左上點的坐標,w,h是矩型的寬和高) ![](https://i.imgur.com/b9GsyA3.jpg) **cv2.contourArea** 計算輪廓的面積 cv2.contourArea(contour, oriented=True) contour:表示某輸入單個輪廓,為array oriented:表示某個方向上輪廓的面積值,這裡指順時針或者逆時針。若為True,該函數返回一個帶符號的面積值,正負值取決於輪廓的方向(順時針還是逆時針),若為False,表示以絕對值返回 **cv2.arcLength** 計算輪廓的周長 cv2.arcLength(contour, closed=True) contour:表示某輸入單個輪廓,為array closed:用於指示曲線是否封閉 **cv2.approxPolyDP** cv2.approxPolyDP()函數是輪廓近似函數,是opencv中對指定的點集進行多邊形逼近的函數,其逼近的精度可通過參數設置 approxPolyDP(curve, epsilon, closed, approxCurve=None) curve:表示輸入的點集 epslion:指定的精度,也即原始曲線與近似曲線之間的最大距離,不過這個值我們一般按照周長的大小進行比較 close:若為True,則說明近似曲線為閉合的;反之,若為False,則斷開 :100:找模板實作比對 ```python import cv2 #讀取照片 image = cv2.imread('phrase_handwritten.png') cv2.imshow( 'Original image' ,image ) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/3EObd2e.jpg) ```python #轉灰階 gray_image = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY) #轉二元 ret,binary_im = cv2.threshold(gray_image,0,255,cv2.THRESH_OTSU) cv2.imshow( 'binary image' , binary_im ) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/EWzCkKA.jpg) ```python #找輪廓 contours_list,hierarchy = cv2.findContours(binary_im,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) for cnt in contours_list: x,y,w,h = cv2.boundingRect(cnt) cv2.rectangle(image,(x,y),(x+w,y+h),(0, 255, 255),2) cv2.imshow( 'Contours marked on RGB image' , image ) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/Mp3p2AV.jpg) ```python #做模板 ref_gray = cv2.imread('b3.png', cv2.IMREAD_GRAYSCALE) ret, ref_binary = cv2.threshold(ref_gray,0,255,cv2.THRESH_OTSU) cv2.imshow( 'Reference image' , ref_binary ) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/AMrxCYI.jpg) ```python #開始比較 ref_contour_list,hierarchy = cv2.findContours(ref_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if len(ref_contour_list)==1: ref_contour= ref_contour_list[0] else: print('找到的模板輪廓超過1個,需要確認一下用哪一個?') ctr= 0 dist_list= [] for cnt in contours_list: retval=cv2.matchShapes(cnt, ref_contour,cv2.CONTOURS_MATCH_I1,0) dist_list.append(retval) ctr= ctr+1 min_dist= min(dist_list) #找出距離最近的 ind_min_dist= dist_list.index(min_dist) #挑出那張圖 ``` ```python required_cnt= contours_list[ind_min_dist] x,y,w,h = cv2.boundingRect(required_cnt) imagecopy= cv2.imread('phrase_handwritten.png') cv2.rectangle(imagecopy,(x,y),(x+w,y+h),(255, 0, 0),2) cv2.imshow( 'Detected B' , imagecopy ) cv2.waitKey(0) cv2.destroyAllWindows() ``` ![](https://i.imgur.com/aFVyvXc.jpg)