以前の節で凸性について学んだ。内側に凹んでいれば凸性の欠陥という。この凸性の欠陥を見つけるための関数が cv2.convexityDefects(contour, convexhull[, convexityDefects]) である.使い方を次に示す:
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
Note: 凸性の欠陥検出をするためにはconvex hullの計算をする時に returnPoints = False とフラグを指定しなければならない.
戻り値はarray型のデータで,その各行は次の4つからなる: [ 始点, 終点, 最も遠い点, 最も遠い点までの近似距離]
.これを画像を用いて説明しよう.まず初めに「始点」と「終点」を結ぶ線を引き,「最も遠い点」に円を描く.ここで最初の3個の値は cnt のインデックスであるため,それぞれの座標を得るには輪郭cntから取得しなければならないことに注意. (コードと使用する画像)
注意: OpenCV3ではOpenCV2と異なり cv2.findContours()は2つではなく3つの値を返すため、 最初の戻り値は_
にセットしている(つまり、その値を無視している)
%matplotlib inline
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('star.png')
img_gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 127, 255,0)
_, contours,hierarchy = cv2.findContours(thresh,2,1)
cnt = contours[0]
hull = cv2.convexHull(cnt,returnPoints = False)
defects = cv2.convexityDefects(cnt,hull)
for i in range(defects.shape[0]):
s,e,f,d = defects[i,0]
start = tuple(cnt[s][0])
end = tuple(cnt[e][0])
far = tuple(cnt[f][0])
cv2.line(img,start,end,[0,255,0],2)
cv2.circle(img,far,5,[0,0,255],-1)
plt.imshow(cv2.cvtColor(img,cv2.COLOR_RGB2BGR))
plt.show()
cv2.pointPolygonTest(contour, pt, measureDist) 関数は点と輪郭を結ぶ最短距離を計算する関数である.指定された点ptが輪郭contour
の外側にあれば負の値,内側にあれば正の値,輪郭上にあれば0
を返す.
第3引数measureDist
は , True
であれば符号付き距離を計算し,False
を指定すると+1
,-1
, 0
(それぞれ輪郭の内側,外側,輪郭上)のどれかの値を返す.
点(50,50)をテストする場合を例に示す:
dist = cv2.pointPolygonTest(cnt,(50,50),True)
print(dist)
Note 距離の計算は時間がかかる処理なので、距離が必要なければ第3引数に False
を指定する. 実際、False
を指定すると2,3倍高速になる.
%timeit dist = cv2.pointPolygonTest(cnt,(50,50),True)
%timeit dist = cv2.pointPolygonTest(cnt,(50,50),False)
cv2.matchShapes(contour1, contour2, method, parameter) 関数を使うと二つの形状(もしくは輪郭)をマッチングし,モーメントの値を基にして計算した形状の差を表す数値を返す.数値が小さいほど二つの形状が似ていることを表す.その計算方法にはいろいろなものがあり、第3引数で選択でき、CV_CONTOURS_MATCH_I1
, CV_CONTOURS_MATCH_I2
, CV_CONTOURS_MATCH_I3
が選択できる(詳しくはドキュメントを参照のこと).これらの方法はみな Huモーメントを利用している.
import cv2
import numpy as np
img1 = cv2.imread('star.png',0)
img2 = cv2.imread('star2.png',0)
ret, thresh = cv2.threshold(img1, 127, 255,0)
ret, thresh2 = cv2.threshold(img2, 127, 255,0)
_, contours,hierarchy = cv2.findContours(thresh,2,1)
cnt1 = contours[0]
_, contours,hierarchy = cv2.findContours(thresh2,2,1)
cnt2 = contours[0]
ret = cv2.matchShapes(cnt1,cnt2,1,0.0)
print (ret)
%matplotlib inline
import cv2
import numpy as np
imageName = {2:'A', 3:'B',4:'C'}
img1 = cv2.imread('star2.png',0)
ret, thresh = cv2.threshold(img1, 127, 255,0)
_, contours,hierarchy = cv2.findContours(thresh,2,1)
cnt1 = contours[0]
for num in range(2,5):
img2 = cv2.imread('star'+str(num)+'.png',0)
ret, thresh2 = cv2.threshold(img2, 127, 255,0)
_, contours,hierarchy = cv2.findContours(thresh2,2,1)
cnt2 = contours[0]
ret = cv2.matchShapes(cnt1,cnt2,2,0.0)
print ("Matching image A with image %s = %f" % (imageName[num], ret))
この比較方法では回転に強いマッチングとなっていることがわかる.
参考: Hu モーメント(英語) は並進,回転,スケールに対して不変な7個のモーメントである.これらの値は cv2.HuMoments(m[, hu]) 関数を使って計算できる.