###### 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)