OpenCV コンピューター画像処理 - 凸面欠陥 + 点ポリゴン テスト + 形状マッチング + 輪郭レイヤー化および cv.findContours()
目次
1. 凸状欠陥
一般に、凸曲線とは凸または平らな曲線のことで、内側が凸(凹)の場合を凸欠陥と呼びます。OpenCV では cv.convexityDefects() というメソッドが用意されています。
この関数は、各行に次の値 - [始点、終点、最遠点、最遠点までのおおよその距離] を含む配列を返します。画像で視覚化でき、始点と終点を結ぶ線を描きます。点を指定し、最も遠い点に円を描きます
import cv2 as cv
import numpy as np
img = cv.imread(r'E:\image\test14.png')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(img_gray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, 2, 1)
cnt = contours[0]
hull = cv.convexHull(cnt, returnPoints=False)
defects = cv.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])
cv.line(img, start, end, [0, 255, 0], 2)
cv.circle(img, far, 5, [0, 0, 255], -1)
cv.imshow('img', img)
cv.waitKey(0)
cv.destroyAllWindows()
前回の概要の内容で、関数 cv.convexHull() について説明しましたが、この関数のプロパティが returnPoints = False の場合、凸欠陥を見つけることができます。
コードの説明: 再びエキサイティングなコード解析の時間です。行 4 ~ 5 は、画像を読み取り、グレースケール イメージに変換する昔ながらの方法です。行 6 は、threshold() 関数を使用してしきい値を設定します。行 7 は、findContours() メソッドを使用して輪郭を見つけます。 convexHull() 関数は、輪郭の凸欠陥をチェックし、次に convexityDefects() 関数を使用して、開始点と終了点などを含む、これらの凸欠陥の対応点の関連情報を取得します。
2. 点多角形テスト
新しい関数がポイント ポリゴン テストに含まれています: cv.pointPolygonTest()、名前が示すように、これはテスト関数です。最初のパラメーターは輪郭、2 番目のパラメーターはテスト ポイント、3 番目のパラメーターはmeasureDist を参照します。値が True の場合、この関数は符号付き距離を見つけます。そうでない場合は、点が等高線の内側、上、または外側にあるかどうかのみが反映されます (それぞれ 1、-1、および 0 を返します)。
import cv2 as cv
import numpy as np
img = cv.imread(r'E:\image\test12.png')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(img_gray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, 2, 1)
cnt = contours[0]
img02 = cv.drawContours(img, [cnt], 0, (0, 0,255), 3)
dist = cv.pointPolygonTest(cnt, (20, 20), True)
dist2 = cv.pointPolygonTest(cnt, (192.5, 156), True)
print("point:(20,20) = " + str(dist))
print("point:(192.5,156) = " + str(dist2))
cv.imshow('test', img02)
cv.waitKey(0)
cv.destroyWindow()
3. 形状マッチング
OpenCV の形状マッチングは関数 cv.matchShapes() に関連しています。この関数を使用すると 2 つの形状 (または 2 つの輪郭) を比較でき、類似性を示すメトリクスを返します。値が低いほど、一致が高くなります。モーメント値から計算されます
画像を回転しても結果に大きな影響を与えないことは注目に値します
import cv2 as cv
import numpy as np
from matplotlib import pyplot as pyt
img1 = cv.imread(r'E:/image/star01.png', 0)
img2 = cv.imread(r'E:/image/star02.png', 0)
img3 = cv.imread(r'E:/image/rectangle01.png', 0)
ret, thresh = cv.threshold(img1, 127, 255, 0)
ret, thresh2 = cv.threshold(img2, 127, 255, 0)
ret, thresh3 = cv.threshold(img3, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, 2, 1)
cnt1 = contours[0]
contours, hierarchy = cv.findContours(thresh2, 2, 1)
cnt2 = contours[0]
contours, hierarchy = cv.findContours(thresh3, 2, 1)
cnt3 = contours[0]
ret = cv.matchShapes(cnt1, cnt2, 1, 0.0)
ret2 = cv.matchShapes(cnt1, cnt3, 1, 0.0)
print("星图1与星图2的匹配度量 = " + str(ret))
print("星图1与方图1的匹配度量 = " + str(ret2))
pyt.subplot(1, 3, 1), pyt.imshow(img1, cmap="gray")
pyt.title("star_img01"), pyt.xticks([]), pyt.yticks([])
pyt.subplot(1, 3, 2), pyt.imshow(img2, cmap="gray")
pyt.title("star_img02"), pyt.xticks([]), pyt.yticks([])
pyt.subplot(1, 3, 3), pyt.imshow(img3, cmap="gray")
pyt.title("rectangle_img01"), pyt.xticks([]), pyt.yticks([])
pyt.show()
4. 輪郭の階層化と cv.findContours()
4.1 輪郭の階層化と cv.findcontours() の関係
前の記事では、cv.findcontours() 関数を使用して輪郭を検索する方法について説明し、その 3 番目のパラメーターの輪郭取得モードについて説明しました。このパラメーターには、通常、cv.RETR_LIST または cv.RETR_TREE を使用して良好な結果を取得します. 効果、そしてこの関数の 3 つの出力を理解する必要があります。その出力には、イメージ (画像)、輪郭 (輪郭)、および階層を含む 3 つの配列が含まれています。これが、実際にこのセクションで説明する内容です。 「輪郭階層」
場合によっては、ネストされたグラフィックスなど、一部の形状が他の形状の内側に配置されることがあります。この場合、外側のものを親クラスと呼び、内側のものをサブクラスと呼びます。そのため、輪郭間には特定の関係があります。輪郭が互いにどのように接続されているか、たとえば、別の輪郭の子であるか、親であるかなどを指定できます。この関係を表現したものを階層と呼びます
したがって、各輪郭は、それがどのレベルであるか、誰がその子で、誰がその親であるかなどについての独自の情報を持っています。OpenCV は、それを 4 つの値を持つ配列として表します: [Next、Previous、First_Child、Parent]、これは 3 番目のパラメーターです。 findcontours() 関数に入ります
4.2 輪郭検索モード(4つのパラメータ)
RETR_LIST
これは 4 つのフラグの中で最も単純です。すべての輪郭を取得するだけで、親子関係は作成されません。このルールでは、親輪郭と子輪郭は等しく、単なる輪郭であり、両方とも同じ階層に属します。開発中に階層機能を使用しない場合、それが最良の選択です
RETR_EXTERNAL
このフラグを使用すると、最も外側のフラグのみが返され、すべての子供の輪郭が残ります。より比喩的に言えば、このルールによれば、各家族の長男だけが注目され、家族の他のメンバーは気にされません。外側の輪郭だけを気にする場合に使用できます
RETR_CCOMP
このフラグはすべての輪郭を取得し、それらを 2 レベルの階層に配置します。オブジェクトの外側の輪郭 (つまり、オブジェクトの境界) は階層 1 に配置され、オブジェクト内の穴の輪郭 (存在する場合) は階層 1 に配置されます。階層-2、その中にオブジェクトがある場合、その輪郭は階層 1 にのみ再配置され、その穴は階層 2 に再配置されます。
RETR_TREE
これは完璧な男で、すべてのプロフィールを取得し、すべてのプロフィールを取得する必要があるときに使用する完全な家族階層リストを作成します。
次にコードを見てみましょう。練習は常に最良の教師です
import cv2 as cv
import numpy as np
from matplotlib import pyplot as pyt
img = cv.imread(r'E:/image/the_first.png')
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
ret, thresh = cv.threshold(img_gray, 127, 255, 0)
contours, hierarchy = cv.findContours(thresh, cv.RETR_LIST, cv.CHAIN_APPROX_NONE)
cnt = contours[0]
contours, hierarchy = cv.findContours(thresh, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE)
cnt2 = contours[0]
contours, hierarchy = cv.findContours(thresh, cv.RETR_CCOMP, cv.CHAIN_APPROX_NONE)
cnt3 = contours[0]
contours, hierarchy = cv.findContours(thresh, cv.RETR_TREE, cv.CHAIN_APPROX_NONE)
cnt4 = contours[0]
img1 = cv.drawContours(thresh, [cnt], 0, (255, 0, 0), 2)
img2 = cv.drawContours(thresh, [cnt2], 0, (0, 255, 0), 2)
img3 = cv.drawContours(thresh, [cnt3], 0, (0, 0, 255), 2)
img4 = cv.drawContours(thresh, [cnt4], 0, (0, 0, 0), 2)
pyt.subplot(2, 2, 1), pyt.imshow(img1, cmap="gray")
pyt.title("RETR_LIST"), pyt.xticks([]), pyt.yticks([])
pyt.subplot(2, 2, 2), pyt.imshow(img2, cmap="gray")
pyt.title("RETR_EXTERNAL"), pyt.xticks([]), pyt.yticks([])
pyt.subplot(2, 2, 3), pyt.imshow(img3, cmap="gray")
pyt.title("RETR_CCOMP"), pyt.xticks([]), pyt.yticks([])
pyt.subplot(2, 2, 4), pyt.imshow(img3, cmap="gray")
pyt.title("RETR_TREE"), pyt.xticks([]), pyt.yticks([])
pyt.show()
(注: 記事の内容については、OpenCV4.1 の中国語公式ドキュメントを参照してください)
記事が役に立った場合は、ワンクリックと 3 つのリンクでサポートしてください。