目錄
一、輪廓
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:輪廓的檢索模式,四種:
- cv2.RETR_EXTERNAL表示隻檢測外輪廓
- cv2.RETR_LIST檢測的輪廓不建立等級關系
- cv2.RETR_CCOMP建立兩個等級的輪廓,上面的一層為外邊界,裡面的一層為内孔的邊界資訊。如果内孔内還有一個連通物體,這個物體的邊界也在頂層。
- cv2.RETR_TREE建立一個等級樹結構的輪廓
- method:輪廓的近似辦法:
- cv2.CHAIN_APPROX_NONE存儲所有的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
- cv2.CHAIN_APPROX_SIMPLE壓縮水準方向,垂直方向,對角線方向的元素,隻保留該方向的終點坐标,例如一個矩形輪廓隻需4個點來儲存輪廓資訊
- 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()

對于每一個輪廓的counters都會有一個這樣對應清單:
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
我們計算對象的重心:
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]]]
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]]
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()
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()
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)