このページの翻訳は最新ではありません。ここをクリックして、英語の最新版を参照してください。
イメージ内の円形オブジェクトの検出と測定
この例では、イメージ内の円または円形オブジェクトを自動的に検出し、検出された円を可視化する方法を説明します。
手順 1: イメージの読み込み
さまざまな色の円形プラスチック チップのイメージを読み取って表示します。検出対象となるたくさんの円があるだけでなく、円を検出するという観点でこのイメージを捉えると、いくつかの興味深い点が見えてきます。
さまざまな色のチップがあり、これらは背景とのコントラストも異なっています。青と赤のチップはこの背景とのコントラストがはっきりとしています。一方、一部の黄色のチップは背景とのコントラストがあいまいです。
他のチップの上に重なっているチップもあれば、お互い触れそうなくらいに近いものもあります。オブジェクトの境界の重なりとオブジェクトのオクルージョンは、オブジェクト検出においては常に難題です。
rgb = imread('coloredChips.png');
imshow(rgb)
手順 2: 円を探索するための半径の範囲の判別
関数 drawline
を使用して、円の適切な半径の範囲を検索します。チップのおおよその直径の上に線を描画します。
d = drawline;
ライン ROI の長さはチップの直径です。通常のチップの直径は 40 ~ 50 ピクセルの範囲にあります。
pos = d.Position; diffPos = diff(pos); diameter = hypot(diffPos(1),diffPos(2))
diameter = 45.3448
手順 3: 円を検出する最初の試行
関数 imfindcircles
により、ある半径の範囲を持つ円を検索します。20 ~ 25 ピクセルの範囲の半径を持つ円を検索します。その前に、背景と比較してオブジェクトが明るいか暗いかを確認しておきます。これを確認するために、このイメージのグレースケール バージョンを見てみましょう。
gray_image = rgb2gray(rgb); imshow(gray_image)
背景がかなり明るく、ほとんどのチップが背景より暗いことがわかります。しかし、既定では、imfindcircles
は背景よりも明るい円形オブジェクトを検出します。このため、imfindcircles
でパラメーター 'ObjectPolarity' を 'dark' に設定して、暗い円を探索します。
[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark')
centers = [] radii = []
出力 centers
と radii
が空になっていますが、これは円が検出されなかったことを表しています。このような結果になることはよくあります。これは、imfindcircles
が円の "検出器" であり、多くの検出器と同じように、imfindcircles
には感度を決定する内部の "検出しきい値" があるためです。つまり、検出器の特定の (円の) 検出が "有効" であると見なされるには、その検出に対する信頼度が一定のレベルを超えていなければなりません。imfindcircles
にはパラメーター 'Sensitivity' があり、これを使用して、この内部しきい値と、結果的に得られるアルゴリズムの感度を制御できます。Sensitivity' を高い値にすると検出しきい値が低くなり、検出される円が多くなります。これは、ホーム セキュリティ システムで使用されるモーション検出器での感度制御と似ています。
手順 4: 検出感度を上げる
チップのイメージに戻ります。既定の感度レベルではすべての円が内部しきい値より低く、そのために円が検出されなかった可能性があります。Sensitivity' は 0 から 1 までの数値で、既定では 0.85 に設定されています。Sensitivity' を 0.9 に上げます。
[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ... 'Sensitivity',0.9)
centers = 8×2
146.1895 198.5824
328.8132 135.5883
130.3134 43.8039
175.2698 297.0583
312.2831 192.3709
327.1316 297.0077
243.9893 166.4538
271.5873 280.8920
radii = 8×1
23.1604
22.5710
22.9576
23.7356
22.9551
22.9995
22.9055
23.0298
今回は、imfindcircles
が円をいくつか検出しました。正確には、8 つです。centers
には円の中心の場所が格納されており、radii
にはこれらの円の推定半径が格納されています。
手順 5: イメージ上での円の描画
関数 viscircles
を使用すると、イメージ上に円を描画できます。imfindcircles
からの出力変数 centers
と radii
は、viscircles
に直接渡すことが可能です。
imshow(rgb) h = viscircles(centers,radii);
円の中心は正しく配置されており、対応する半径も実際のチップとうまく一致しているようです。しかし、まだ検出されていないチップがかなりあります。Sensitivity' をもう少し高い 0.92 に上げてみましょう。
[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ... 'Sensitivity',0.92); length(centers)
ans = 16
'Sensitivity' を上げたら、円の数も増えました。これらの円をイメージ上にもう一度プロットします。
delete(h) % Delete previously drawn circles
h = viscircles(centers,radii);
手順 6: 円の検出に 2 つ目の方法 (2 段階) を使用
この方法の方が、よい結果になります。imfindcircles
には円を検出するための方法が 2 つあります。ここまでは、円を検出するために "位相符号化" メソッドという既定のメソッドを使用してきました。imfindcircles
には、一般に "2 段階" メソッドと呼ばれているもう 1 つのメソッドがあります。2 段階メソッドを使用して結果を表示します。
[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ... 'Sensitivity',0.92,'Method','twostage'); delete(h) h = viscircles(centers,radii);
0.92 の Sensitivity で、2 段階メソッドのほうが多くの円を検出しています。概略を述べると、これらの 2 つのメソッドは異なる長所をもっていて、補完しあっています。通常、位相符号化メソッドは 2 段階メソッドよりも速く、ノイズに対するロバスト性がわずかに優れています。しかし、2 段階メソッドと同じ数を検出するには、'Sensitivity' レベルを高くしなければなりません。たとえば、位相符号化メソッドで 'Sensitivity' レベルを 0.95 に上げると、同じ数のチップが検出されます。
[centers,radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark', ... 'Sensitivity',0.95); delete(h) viscircles(centers,radii);
imfindcircles
の両方のメソッドが、一部だけ見えている (隠れている) チップの中心と半径を正確に検出することに注意してください。
手順 7: まだ検出されない円がある理由
最後の結果を見てみると、面白いことに、imfindcircles
はイメージ内の黄色のチップを検出していません。黄色のチップは、背景とのコントラストが強くありません。それどころか、背景とかなり近い強度をもっているようです。黄色のチップは、想定していたほど背景より "暗く" ないということでしょうか。これを確認するために、このイメージのグレースケール バージョンをもう一度表示します。
imshow(gray_image)
手順 8: イメージ内の "明るい" 円の検出
黄色のチップは、背景と比較すると、ほとんど同じ強度です。背景より明るいかもしれません。そこで、黄色のチップを検出するために、'ObjectPolarity' を 'bright' に変更します。
[centersBright,radiiBright] = imfindcircles(rgb,[20 25], ... 'ObjectPolarity','bright','Sensitivity',0.92);
手順 9: 異なる色での "明るい" 円の描画
viscircles
の 'Color' パラメーターを変更して、異なる色で "明るい" 円を描画します。
imshow(rgb) hBright = viscircles(centersBright, radiiBright,'Color','b');
検出されていなかった黄色のチップが 3 つ検出されましたが、黄色のチップがまだ 1 つ抜け落ちています。これらの黄色のチップは、この背景では他のチップのように "際立って" いないため、検出が困難です。
手順 10: 'EdgeThreshold' の値を下げる
imfindcircles
には 'EdgeThreshold' という別のパラメーターがあり、ここで役に立ちます。円を検出するために、imfindcircles
はイメージ内のエッジ ピクセルのみを使用します。これらのエッジ ピクセルは、基本的には高い勾配値をもつピクセルです。EdgeThreshold' パラメーターは、あるピクセルがエッジ ピクセルであると見なされ計算に組み込まれるには、そのピクセルでの勾配値がどの程度 "高く" なければならないかを制御します。このパラメーターに高い (1 に近い) 値を指定すると、強いエッジ (高い勾配値) のみが計算に組み込まれ、低い (0 に近い) 値を指定すると、許容範囲が広くなり、弱いエッジ (低い勾配値) も計算に組み込まれます。検出されない黄色のチップの場合は、コントラストが低いため、チップ外周の境界ピクセルの一部は低い勾配値をもつことが予想されます。したがって、'EdgeThreshold' パラメーターの値を下げて、黄色のチップのほとんどのエッジ ピクセルが計算に組み込まれるようにします。
[centersBright,radiiBright,metricBright] = imfindcircles(rgb,[20 25], ... 'ObjectPolarity','bright','Sensitivity',0.92,'EdgeThreshold',0.1); delete(hBright) hBright = viscircles(centersBright, radiiBright,'Color','b');
手順 11: "暗い" 円と "明るい" 円を同時に描画
これで、imfindcircles
はすべての黄色のチップと 1 つの緑のチップを検出しました。これらのチップを青で、'ObjectPolarity' を 'dark' にして先に検出していた他のチップを赤で描画します。
h = viscircles(centers,radii);
すべての円が検出されています。最後に、注意の必要な点があります。パラメーターを検出率が高くなるような値に変更すると、検出される円は増えますが、円を誤って検出する可能性も高くなります。正しく検出できる円の数 (検出率) と誤って検出される円の数 (誤認率) の間にはトレードオフがあります。
円探しをお楽しみください!