1.關於輪廓的一些認識

1.1.為什麼要實作輪廓?

邊緣檢測雖可檢測出邊緣,但邊緣不一定連續,無法構成一個整體。而影像輪廓是指將邊緣連接起來的整體,用於後續運算。


1.2.實作流程

讀圖 >> 影像二值化 >> 尋找輪廓 >> 繪製輪廓


1.3.尋找影像輪廓

img , contours, hierarchy = cv2.findContours(img, mode, method)


1.3.1.關於輸出

img:原圖之八位元單通道二值化影像

contours:輪廓。每個輪廓對應4個元素來說明目前輪廓的層次關係,[next(後一個輪廓的索引編號), previous(前一個輪廓的索引編號), first_child(第一個子輪廓索引標號), parent(父輪廓索引標號)]。輪廓的層次參數是由mode所決定,不同mode會獲得不同的輪廓編號,獲得的hierarchy也不同。

hierarchy:影像階層。


1.3.2.關於參數

mode:輪廓檢索方式,常見的方式如下:

  • cv2.RETR_EXTERNAL:只檢測外輪廓
  • cv2.RETR_LIST:檢測到的輪廓,不建立等級關係
  • cv2.RETR_CCOMP:檢測所有輪廓並組織成兩級層次結構
  • cv2.RETR_TREE:建立一個等級樹結構的輪廓


method:輪廓近似的方法

  • cv2.CHAIN_APPROX_NONE:儲存所有輪廓點,相鄰像素間位置差不超過1。
  • cv2.CHAIN_APPROX_SIMPLE:壓縮水平、垂直、對角線方向之元素,只保留該方向的終點座標。


 1.4.繪製影像輪廓

img = cv2.drawContours(image, contours, contourIndex, color, thickness, lineType, hierarchy,maxLevel, offset)

1.4.1. 參數與輸出結果說明

img:待繪製影像

contours:需繪製的輪廓 

contourIndex: 繪製輪廓的索引號,-1表示全部畫出

color:繪製的顏色

thickness:輪廓的粗細,-1表實線 

lineType: 繪製線的形式

hierarchy: 輸出的層次資訊

maxLevel: 輪廓層次的深度。若值為0,表僅繪製第0層輪廓

offset:偏移參數,使輪廓偏移到不同的位置而展示出來


實作後的結果說明如下:

下圖為原圖。

<source:OpenCV Sample Image>

依序將各圖形的輪廓畫出。

<source:Greatway9999>

左圖為二值化後的圖片,供對照用。右圖為將各圖形的輪廓呈現在同張圖上。

<source:Greatway9999>


cv2.contourArea() 指令可計算輪廓面積。cv2.arcLength() 指令可計算輪廓長度。輸出結果如下圖所示。

<source:Greatway9999>

使用方法請參考以下程式。


1.4.2.範例程式

import numpy as np

import cv2

#img = cv2.imread('pic3.png', cv2.IMREAD_GRAYSCALE)

img = cv2.imread('pic3.png')

img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)


ret_binary, img_binary = cv2.threshold(img_gray, 177, 255, cv2.THRESH_BINARY_INV)

contours, hierarchy = cv2.findContours(img_binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

print(hierarchy)

cv2.imshow('img',img)

cv2.imshow('img_binary',img_binary)


new_img = img

new_img = cv2.drawContours(new_img, contours, -1, (0, 0, 255), 3)

cv2.imshow('new_img',new_img)


#計算輪廓面積

n = len(contours)

contoursImg=[]

for i in range(n):

    print('contours['+str(i)+']面積=', cv2.contourArea(contours[i]))

    temp = np.zeros(img.shape, np.uint8)

    contoursImg.append(temp)

    contoursImg[i] = cv2.drawContours(contoursImg[i], contours, i, (0, 0, 255), 3)

    cv2.imshow('contoursImg['+str(i)+']',contoursImg[i])


    

#計算輪廓長度    

countLen = []

for j in range(n):

    countLen.append(cv2.arcLength(contours[j], True))

    print('第'+str(j)+'的輪廓長度為%d'% countLen[j])


countLenSum = np.sum(countLen)    

countLenAverage = countLenSum / n

print('輪廓總長度:',countLenSum)

print('輪廓平均長度:',countLenAverage)

   

cv2.waitKey()

cv2.destroyAllWindows()


2.輪廓擬合的常見方法

矩形包圍框

最小包圍矩形框

最小包圍圓形

最佳擬合橢圓

最小包圍三角形


---

參考資料:

0 留言