天天看點

16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

目錄

一、輪廓

1、什麼是輪廓

2、怎麼繪制輪廓

3、輪廓的近似方法

二、輪廓特征

1、矩

2、輪廓面積

3、輪廓的周長(弧長)

4、輪廓近似

5、凸包、凸性檢測

6、邊界矩形

7、最小外接圓

8、橢圓拟合

9、直線拟合

一、輪廓

1、什麼是輪廓

輪廓可以簡單認為是成為連續的點(連着邊界)連在一起的曲線,具有相同的顔色或者是灰階。輪廓在形狀分析和物體識别方面中很有用。

  • 為了準确,要使用二值化圖像。需要進行閥值化處理或者Canny邊界檢測
  • 查找輪廓的函數會修改原始圖像。如果繼續使用原始圖像的話,應該将原始圖像存儲到其他變量中
  • 在Opencv中,查找輪廓就像是在黑色的背景中找白色物體。

2、怎麼繪制輪廓

找到輪廓:contours, hierarchy = cv2.findContours(thresh, mode, method)

  • contours:輪廓本身(是一個清單)
  • hierachy:每條輪廓對應的屬性
  • thresh:輸入的二值化後的圖像
  • mode:輪廓的檢索模式,四種:
  1. cv2.RETR_EXTERNAL表示隻檢測外輪廓
  2. cv2.RETR_LIST檢測的輪廓不建立等級關系
  3. cv2.RETR_CCOMP建立兩個等級的輪廓,上面的一層為外邊界,裡面的一層為内孔的邊界資訊。如果内孔内還有一個連通物體,這個物體的邊界也在頂層。
  4. cv2.RETR_TREE建立一個等級樹結構的輪廓
  • method:輪廓的近似辦法:
  1. cv2.CHAIN_APPROX_NONE存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
  2.  cv2.CHAIN_APPROX_SIMPLE壓縮水準方向,垂直方向,對角線方向的元素,隻保留該方向的終點坐标,例如一個矩形輪廓隻需4個點來儲存輪廓資訊
  3. cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

注意:opencv2傳回兩個值:contours:hierarchy;opencv3會傳回三個值,分别是img, countours, hierarchy

繪制輪廓:cv2.drawContours( img, contours , contourIdx , color(r,g,b),thickness)

  • contourIdx:繪制輪廓list中的哪條輪廓,如果是-1,則繪制其中的所有輪廓
  • color(r,g,b):線條的顔色
  • thickness:輪廓線的寬度,如果是-1(cv2.FILLED),則為填充模式
import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('outline1.png')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray,127,255,0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
imag = cv2.drawContours(img,contours,-1,(0,255,0),3)

while True:
    cv2.imshow('img',img)
    cv2.imshow('imag', imag)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
cv2.destroyAllWindows()
           
16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征
16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

對于每一個輪廓的counters都會有一個這樣對應清單:

16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

3、輪廓的近似方法

之前 提到的輪廓是一個形狀具有相同灰階值的邊界,它會存儲形狀邊界上的所有的(x,y)坐标。實際上,我們不需要所有的點,當需要直線的時候,找到兩個端點就可。cv2.CHAIN_APPROX_SIMPLE可以實作。它會将輪廓上的備援點去掉,壓縮輪廓,進而節約記憶體開支。

二、輪廓特征

1、矩

圖像的矩可以幫助我們計算圖像的質心、面積等

函數cv2.moments()會将計算得到的矩以一個字典的形式傳回

import cv2

img = cv2.imread('rectangle.png',0)
ret, thresh = cv2.threshold(img, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
M = cv2.moments(cnt)
print(M)
print(len(M))
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
print(cx,cy)

#輸出
{'m00': 135072.0, 'm10': 54501552.0, 'm01': 34443360.0, 'm20': 24467257248.0, 'm11': 13897895760.0, 'm02': 9716674464.0, 'm30': 11870574279480.0, 'm21': 6239150598240.0, 'm12': 3920678146224.0, 'm03': 2953896996960.0, 'mu20': 2475881016.0, 'mu11': -1.9073486328125e-06, 'mu02': 933617663.9999981, 'mu30': 0.0, 'mu21': 0.0018310546875, 'mu12': 0.00079345703125, 'mu03': 0.00048828125, 'nu20': 0.13570601851851852, 'nu11': -1.0454407429638942e-16, 'nu02': 0.0511727078891257, 'nu30': 0.0, 'nu21': 2.7307880219458127e-16, 'nu12': 1.1833414761765188e-16, 'nu03': 7.2821013918555e-17}
24
403 255
           

我們計算對象的重心:

16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

2、輪廓面積

函數cv2.contourArea()計算得到

import cv2

img = cv2.imread('rectangle.png',0)
ret, thresh = cv2.threshold(img, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
area = cv2.contourArea(cnt)
print(area)

#輸出
135072.0
           

3、輪廓的周長(弧長)

函數cv2.arcLength(cnt, True)計算周長(閉合:True)

import cv2

img = cv2.imread('rectangle.png',0)
ret, thresh = cv2.threshold(img, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
perimeter = cv2.arcLength(cnt, True)
print(perimeter)

#輸出
1514.0
           

4、輪廓近似

将輪廓形狀近似到另外一種由更少點組成的輪廓形狀,新輪廓形狀的點的數目由我們設定的準确度來決定。

假設我們要在一幅圖檔中查找一個矩形,但是由于圖像的種種原因我們找不到一個完美的矩形,而是一個鼓鼓的矩形,那麼我們可以用函數來近似這個形狀:

epsilon = 0.1*cv2.arcLength(cnt, True)

approx = cv2.approxPolyDP(cnt, epsilon, True)

import cv2

img = cv2.imread('rectangle.png',0)
ret, thresh = cv2.threshold(img, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
epsilon = 0.1*cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, epsilon, True)
print(approx)

#輸出
[[[169 111]]

 [[169 399]]

 [[638 399]]

 [[638 111]]]

           
16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

5、凸包、凸性檢測

暫不處理

6、邊界矩形

直邊界矩形,一個直矩形,沒有旋轉,是以直矩形的面積不是最小的。可以通過函數找到:

注意注意:千萬不要直接img = cv2.imread('outline2.png',0)這樣讀取一個灰階圖檔,這樣,你在畫圖像的時候你會發現,我為什麼畫出來的隻有灰階線條!

x,y,w,h = cv2.boundingRect(cnt)

cv2.rectangle(img, (x,y), (x+w,y+h),(0,0,255),2)

  • (x,y):矩形的左上角的坐标
  • (x+w,y+h):矩形的寬和長

rect = cv2.minAreaRect(cnt)

box = np.int0(cv2.boxPoints(rect))

cv2.polylines(img,[box],True,(255,0,0),2)

  • rect:得到最小外接矩形的(中心(x,y), (寬,高), 旋轉角度)
  • box:得到最外接矩形的四個點
import numpy as np
import cv2

img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
#畫出直形矩形
x,y,w,h = cv2.boundingRect(cnt)
print(x,y,w,h)
cv2.rectangle(img, (x,y), (x+w,y+h),(0,0,255),2)
#畫出最小矩形(由畫多邊形的代碼畫出)
rect = cv2.minAreaRect(cnt)
box = np.int0(cv2.boxPoints(rect))
print(box)#矩形的四個點
cv2.polylines(img,[box],True,(255,0,0),2)
while True:
    cv2.imshow('image',img)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
cv2.destroyAllWindows()

#輸出
161 52 545 400
[[191 480]
 [ 93 313]
 [663 -19]
 [761 148]]
           
16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

7、最小外接圓

(x,y), radius = cv2.minEnclosingCircle(cnt)

得到圓心坐标和半徑

import cv2

img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
(x,y), radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
cv2.circle(img, center, radius, (0,255,0),2)
while True:
    cv2.imshow('image',img)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
cv2.destroyAllWindows()
           
16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

8、橢圓拟合

ellipse = cv2.fitEllipse(cnt)

cv2.ellipse(img, ellipse,(0,255,0),2)

import cv2

img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img, ellipse,(0,255,0),2)
while True:
    cv2.imshow('image',img)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
cv2.destroyAllWindows()
           
16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

9、直線拟合

可以根據一組點拟合出一條直線,同樣我們也可以為圖像中的白色點拟合出一條直線

rows, cols = img.shape[:2]

[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)

lefty = int((-x*vy/vx)+y)

righty = int(((cols-x)*vy/vx)+y)

cv2.line(img, (cols-1,righty),(0,lefty),(0,255,0),5)

  • (cols-1,righty),(0,lefty):直線的兩個坐标
import cv2

img = cv2.imread('outline2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127,255,0)
contours, hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]
rows, cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx)+y)
righty = int(((cols-x)*vy/vx)+y)
cv2.line(img, (cols-1,righty),(0,lefty),(0,255,0),5)
print((cols-1,righty),(0,lefty))
while True:
    cv2.imshow('image',img)
    k = cv2.waitKey(1) & 0xFF
    if k == 27:
        break
cv2.destroyAllWindows()

#輸出
(797, -12) (0, 571)
           
16-opencv-python-Opencv中的輪廓一、輪廓二、輪廓特征

繼續閱讀