【小林的OpenCV基礎課 14】進擊的輪廓畫手/凸包

開篇懷著敬畏的心情紀念下面的這位偉大的前輩。


小林最近比較肝,因為康娜醬要準備運動會了。

這一話我們講解在OpenCV中輪廓的另一個重要知識:凸包

「凸包(Convex Hull)是一個計算幾何(圖形學)中常見的概念。簡單來說,給定二維平面上的點集,凸包就是將最外層的點連接起來構成的凸多邊形,它是能包含點集中所有點的。理解物體形狀或輪廓的一種比較有用的方法便是計算一個物體的凸包,然後計算其凸缺陷(convexity defects)。」

關於凸包的尋找演算法,有興趣的同學可參考:

凸包演算法分析 - CSDN博客?

blog.csdn.net圖標數學:凸包演算法詳解 - 愛國吶 - 博客園?

www.cnblogs.com圖標

處理凸包的一般步驟:

  • 預處理
  • 獲取輪廓
  • 調用函數獲得凸包
  • 畫出凸包(可選),計算凸缺陷(可選)

關於凸包,在OpenCV中有兩個常用的函數:convexHull和convexityDefects,下面我們分別講解。

convexHull:根據輪廓計算凸包,當需要繪製凸包時可配合函數polylines使用。函數原型:

hull=cv2.convexHull(points[, hull[, clockwise[, returnPoints]]])

  • hull:凸包
  • points:點的集合,若要處理輪廓則該參數為對應的輪廓變數
  • clockwise:操作方向標識符,為True時,輸出的凸包為順時針方向,否則為逆時針方向
  • returnPoints:hull參數的返回形式,若為True則hull返回點坐標的形式,若為False則返回對應點的索引值

import cv2import numpy as npimg = cv2.imread(duolaameng.jpeg)# 圖像預處理grayImg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)_, binImg = cv2.threshold(grayImg, 100, 255, cv2.THRESH_BINARY)# 獲得輪廓_, contours, hierarchy = cv2.findContours(binImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img, contours, 1, (0, 255, 0), 2)x, y, w, h = cv2.boundingRect(contours[1])cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)# 計算凸包並畫出hull = cv2.convexHull(contours[1],True)cv2.polylines(img, [hull], True, (255, 0, 0), 3)cv2.imshow(Contours Image, img)cv2.waitKey()cv2.destroyAllWindows()

源圖

綠色為輪廓,藍色為凸包,紅色為包圍矩形

convexityDefects:這個函數用來計算凸包的凸缺陷,可以得到凸包的起點、終點坐標、輪廓上距離凸包最遠的點坐標和最遠點的近似長度,有時我們也用這個函數來繪製凸包。使用這個函數之前要先用convexHull函數(參數returnPoints置為False)得到凸包。函數原型:

convexityDefects=cv2.convexityDefects(contour, convexhull[, convexityDefects])

  • convexityDefects:輸出的凸缺陷向量,包含四個元素,分別為起點坐標、終點坐標、輪廓上距離凸包最遠的點坐標和最遠點的近似長度
  • contour:待處理的輪廓
  • convexhull:已有的凸包

import cv2import numpy as npimg = cv2.imread(duolaameng.jpeg)# 圖像預處理grayImg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)_, binImg = cv2.threshold(grayImg, 100, 255, cv2.THRESH_BINARY)# 獲取輪廓_, contours, hierarchy = cv2.findContours(binImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img, contours, 1, (0, 255, 0), 2)x, y, w, h = cv2.boundingRect(contours[1])cv2.rectangle(img, (x, y), (x+w, y+h), (0, 0, 255), 2)# 計算凸包並畫出hull = cv2.convexHull(contours[1],returnPoints=False)defects = cv2.convexityDefects(contours[1],hull)# 提取defects中的參數傳進rangefor i in range(defects.shape[0]):# 對defects參數進行拆解 s,e,f,d = defects[i,0]# 獲取起始點坐標 終點坐標和最遠點坐標 start = tuple(contours[1][s]) end = tuple(contours[1][e]) far = tuple(contours[1][f]) xs,ys = start[0] xe,ye = end[0] xf,yf = far[0]# 用直線繪製凸包 用點繪製最遠點 cv2.line(img,(xs,ys),(xe,ye),(0,255,0),3) cv2.circle(img,(xf,yf),4,(0,0,255),-1)cv2.imshow(Contours Image, img)cv2.waitKey()cv2.destroyAllWindows()

紅點為最遠點

細心的同學會發現,使用convexityDefects的參數繪製凸包的效果並不好,因為它的主要工作是計算凸缺陷,所以若需要繪製凸包,小林建議convexHull+polylines繪製。


關於凸包,還有幾個奇淫技巧,下面列舉下:

函數pointPolygonTest:計算某點與輪廓的相對位置關係,又叫多邊形測試。函數原型:

retval=cv2.pointPolygonTest(contour, pt, measureDist)

  • contour:輪廓信息
  • pt:某點的坐標
  • measureDist:當設為True時,返回值返回某點到輪廓的距離,設為False時返回0,1或-1
  • retval:當某點位於輪廓內時返回一個正值,在輪廓外返回一個負值,在輪廓上返回0。例如當measureDist設為False時返回0(在輪廓上)、1(在輪廓里)或-1(在輪廓外)

函數matchShapes:計算形狀或輪廓的匹配程度,返回值越小,說明相似度越高。函數原型:

retval=cv2.matchShapes(contour1, contour2, method, parameter)

  • contour1和contour2:需要對比的兩個輪廓
  • method:匹配方法,可查閱API
  • parameter:默認值為0

函數contourArea:計算輪廓面積

retval=cv2.contourArea(contour[, oriented])

  • retval:返回的面積值
  • contour:輪廓變數
  • oriented:若為True,返回一個帶符號的面積值,正負取決於輪廓的方向,若為False,則返回一個無符號的面積值

下面看幾個Demo

matchShapes的:

img = cv2.imread(duolaameng.jpeg)img2 = cv2.imread(duolaamengRotated.jpeg)#對源圖做適當旋轉# 圖像預處理grayImg = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)grayImg2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)_, binImg = cv2.threshold(grayImg, 100, 255, cv2.THRESH_BINARY)_, binImg2 = cv2.threshold(grayImg2, 100, 255, cv2.THRESH_BINARY)_, contours, hierarchy = cv2.findContours(binImg, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)_, contours2, hierarchy2 = cv2.findContours(binImg2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)ret = cv2.matchShapes(contours[1], contours2[1], 1, 0.0)print(ret)#顯示0.0001979362991957334

再來一個pointPolygonTest:

dist = cv2.pointPolygonTest(contours[1],(50,50),False)print(dist)#顯示-1.0dist = cv2.pointPolygonTest(contours[1],(50,50),True)print(dist)#顯示-240.5659992600783

這一話的代碼已經同步到Github上了,對應Class 4 Image Processing文件夾下的C4 Draw Hull.py。點擊Star,手有餘香(づ ̄ 3 ̄)づ

Kobayashi_OpenCV_py?

github.com


最後的最後

如果喜歡小林的專欄,就收藏了吧!してください!


推薦閱讀:

為什麼選擇Python
Python預測分析核心演算法——Day1
一個很簡單的歸簡法的Python實現。
Python環境搭建—安利Python小白的Python和Pycharm安裝詳細教程
Kivy中文編程指南:事件和屬性

TAG:OpenCV | Python | 計算機視覺 |