- 查找輪廓的不同特征,例如面積,周長,重心,邊界等
1.矩
圖像的矩可以幫助我們計算圖像的質心,面積等。
函數cv2.momen()會將計算得到的矩以一個字典的形式返回,
我們的測試圖像如下:
例程如下:
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread('8.jpg',0)
ret,thresh = cv2.threshold(img,127,255,0)
im,contours,hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
M = cv2.moments(cnt)
print(M)
#會返回一個字典
#計算重心
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
2.輪廓面積
輪廓面積可以用cv2.contourArea()計算得到,也可以使用矩,M['m00‘]
print(cv2.contourArea(cnt))
3.輪廓周長
也被稱為弧長。可以使用函數cv2.arcLength()計算得到。這個函數的第二參數可以用來指定對象的形狀是閉合的還是打開的(即曲線)
print(cv2.arcLength(cnt,True))#試了一下,如果強行賦予False,得到的周長會比用True短一些
4.輪廓近似
將輪廓形狀近似到另外一種有更少點組成的輪廓形狀,新輪廓的點的數目由我們設定的准確度來決定。使用的Douglas-Peucker算法在此不細講了。
我們假設要在下圖中找一個矩形
然而這個圖凹凸不平,直接提取輪廓無法提取到一個完美的矩形。因此我們就可以使用這個函數來近似這個形狀了。這個函數的第二個參數epsilon,是從原始輪廓到近似輪廓的最大距離,是一個准確度參數。因此對它的調整對最后的結果很重要。
例程如下:
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
im = cv2.imread('9.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)#用這個方式轉換的原因是最后輸出時希望能看到彩色的的輪廓圖
ret,thresh = cv2.threshold(img,127,255,0)
img,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)#這個和上面的True都是表示是閉曲線
#approx得到的僅僅是幾個點集,需要將其連接起來
# cv2.drawContours(im,approx,-1,(0,0,255),3) 該函數只能畫出幾個點
cv2.polylines(im, [approx], True, (0, 255, 0), 2)#需要用這個函數才能畫出輪廓,中間的True也是指示是否為閉曲線的
cv2.imshow('img',im)
cv2.waitKey(0)
最終效果如下:
指定為10%時: 指定為1%時:
5.凸包
凸包與輪廓近似相似,有些時候他們給出的結果是一樣的,但是本質上講他們是不同的。函數cv2,convexHull()可以用來檢測一個曲線是否具有凸性缺陷,並能糾正缺陷。一般來說,凸性曲線總是凸出來的,至少是頻帶。如果有地方凹進去了就被叫做凸性缺陷。如下圖所示:
紅色表示了他的凸包,而凸性缺陷被雙箭頭標出來了。
對於參數還有需要說明的地方:
hull = cv2.convexHull(points[],hull[],clockwise,retirnPoints)
- point是我們要傳入的輪廓
- hull 輸出,通常不需要
- clockwise 方向標志,如果設置為True,輸出的凸包是順時針方向的,否則為逆時針方向
- returnPoints 默認為True。它會返回凸包上點的坐標。如果設置為False,則會返回凸包點對應於原輪廓上的點坐標
實際操作中,不如需要獲得上面的凸包,只需要:
hull = cv2.convexHull(cnt)
即可。但是如果你想獲得凸性缺陷,需要把returnPoints設置為False。以上面的矩形為例,在獲取他的輪廓cnt后,先把returnPoin設置為True查找凸包,會得到矩形的四個角點坐標。再將returnPoints設為False,將得到輪廓點的索引,即前面的四個坐標在原輪廓中的序列位置。
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
im = cv2.imread('9.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)#用這個方式轉換的原因是最后輸出時希望能看到彩色的的輪廓圖
ret,thresh = cv2.threshold(img,127,255,0)
img,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
hull = cv2.convexHull(cnt,returnPoints=True)
print(hull)
# cv2.drawContours(im,hull,-1,(0,0,255),3) #該函數只能畫出幾個點
cv2.polylines(im, [hull], True, (0, 255, 0), 2)#需要用這個函數才能畫出輪廓,中間的True也是指示是否為閉曲線的
cv2.imshow('img',im)
cv2.waitKey(0)
設置為True時得到的結果是
[[[161 126]]
[[ 8 126]]
[[ 8 10]]
[[161 10]]]
是矩形四個角點的坐標
設置為False后
[[113]
[ 60]
[ 0]
[126]]
表示的是上面四個點在cnt中的索引號,且不能畫出凸包了
6.凸性檢測
就是檢測一個曲線是不是凸的,只能返回True和False
k = cv2.isContourConvex(cnt)
7.邊界矩形
有兩類邊界矩形。
直邊界矩形 一個直矩形(就是沒有旋轉的矩形)。它不會考慮對象是否旋轉。所以邊界矩形的面積不是最小的。可以使用函數cv2.boundingRect()查找得到的。
x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
返回值中,(x,y)是矩陣左上角的坐標,(w,h)是舉行的寬和高。
旋轉邊角矩形 這個邊界矩形是面積最小的,因為他考慮了對象的旋轉。用到的函數為cv2.minAreaRect()。返回的是一個Box2D結構,其中包含矩形左上角角點的坐標(x,y),以及矩形的寬和高(w,h),以及旋轉角度。但是要繪制這個矩形需要矩形的4個角點。可以通過函數cv2.boxPoints()獲得。
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
im = cv2.imread('10.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)#用這個方式轉換的原因是最后輸出時希望能看到彩色的的輪廓圖
ret,thresh = cv2.threshold(img,127,255,0)
img,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
#直邊界矩形
x,y,w,h = cv2.boundingRect(cnt)
im = cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
#旋轉邊界矩形
s = cv2.minAreaRect(cnt)
a = cv2.boxPoints(s)
a = np.int0(a)#必須轉換a的類型才能用polylines中畫出,我也不知道為啥
cv2.polylines(im,[a],True,(0,0,255),3)
cv2.imshow('img',im)
cv2.waitKey(0)
最終效果如下:
8.最小外接圓
函數cv2.minEnclosingCircle() 可以幫我們找到一個對象的外切圓。它是所有能夠包括對象的圓中面積中最小的一個。例程如下:
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
im = cv2.imread('10.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)#用這個方式轉換的原因是最后輸出時希望能看到彩色的的輪廓圖
ret,thresh = cv2.threshold(img,127,255,0)
img,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
im = cv2.circle(im,center,radius,(0,255,0),2)
# cv2.polylines(im,[a],True,(0,0,255),3)
cv2.imshow('img',im)
cv2.waitKey(0)
9.橢圓擬合
使用cv2.fitEllipse()找橢圓,返回值就是旋轉邊界矩形的內切圓。用cv2.ellipse()畫橢圓
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
im = cv2.imread('10.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)#用這個方式轉換的原因是最后輸出時希望能看到彩色的的輪廓圖
ret,thresh = cv2.threshold(img,127,255,0)
img,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
ellise = cv2.fitEllipse(cnt)
im = cv2.ellipse(im,ellise,(0,255,0),2)
cv2.imshow('img',im)
cv2.waitKey(0)
10.直線擬合
類似ゆ根據一組點擬合出一條直線,也可以根據 圖像中的白色點擬合出一條直線,不過過程較為復雜
書上的例程如下:
# -*- coding:utf-8 -*-
import numpy as np
import cv2
from matplotlib import pyplot as plt
im = cv2.imread('10.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)#用這個方式轉換的原因是最后輸出時希望能看到彩色的的輪廓圖
ret,thresh = cv2.threshold(img,127,255,0)
img,contours,hierarchy = cv2.findContours(thresh,1,2)
cnt = contours[0]
rows,cols = im.shape[:2]
#cv2.fitLine(points, distType, param, reps, aeps[, line ]) → line
#points – Input vector of 2D or 3D points, stored in std::vector<> or Mat.
#line – Output line parameters. In case of 2D fitting, it should be a vector of
#4 elements (likeVec4f) - (vx, vy, x0, y0), where (vx, vy) is a normalized
#vector collinear to the line and (x0, y0) is a point on the line. In case of
#3D fitting, it should be a vector of 6 elements (like Vec6f) - (vx, vy, vz,
#x0, y0, z0), where (vx, vy, vz) is a normalized vector collinear to the line
#and (x0, y0, z0) is a point on the line.
#distType – Distance used by the M-estimator
#distType=CV_DIST_L2
#ρ(r) = r2 /2 (the simplest and the fastest least-squares method)
#param – Numerical parameter ( C ) for some types of distances. If it is 0, an optimal value
#is chosen.
#reps – Sufficient accuracy for the radius (distance between the coordinate origin and the
#line).
#aeps – Sufficient accuracy for the angle. 0.01 would be a good default value for reps and
#aeps.
[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)
im = cv2.line(im,(cols-1,righty),(0,lefty),(0,255,0),2)
cv2.imshow('img',im)
cv2.waitKey(0)
