ドキュメンテーション

最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

イメージ内の円形オブジェクトの検出と測定

この例では、imfindcircles を使用してイメージ内の円または円形オブジェクトを自動的に検出する方法を示します。viscircles を使用して、検出された円を可視化する方法も示します。

手順 1: イメージの読み込み

この例では、さまざまな色の丸いプラスチック チップのイメージを使用します。

rgb = imread('coloredChips.png');
figure
imshow(rgb)

検出対象となるたくさんの円があるだけでなく、円を検出するという観点でこのイメージを捉えると、いくつかの興味深い点が見えてきます。

  1. さまざまな色のチップがあり、これらは背景とのコントラストも異なっています。青と赤のチップはこの背景とのコントラストがはっきりとしています。一方、一部の黄色のチップは背景とのコントラストがあいまいです。

  2. 他のチップの上に重なっているチップもあれば、お互い触れそうなくらいに近いものもあります。重なっているオブジェクトの境界とオブジェクト遮蔽は、オブジェクト検出においては常に難題です。

手順 2: 円を検索するための半径の範囲の判別

imfindcircles が円を検索するには、半径の範囲が必要です。適切な半径の範囲をすばやく検出するには、対話型ツール imdistline を使用して、さまざまなオブジェクトのおよその半径を推定します。

d = imdistline;

imdistline は、移動してチップ上に合わせられるドラッグ可能なツールを作成し、およその半径を表す数字を表示します。ほとんどのチップの半径は 21 ~ 23 ピクセルの範囲内です。確実に検出するため、少し範囲の広い 20 ~ 25 ピクセルの半径を使用します。次のステップに進む前に、imdistline ツールを削除しておきます。

delete(d);

手順 3: 円を検出する最初の試行

このイメージに対して、検索半径 [20 25] ピクセルを指定して imfindcircles を呼び出します。その前に、背景と比較してオブジェクトが明るいか暗いかを確認しておきます。これを確認するために、このイメージのグレースケール バージョンを見てみましょう。

gray_image = rgb2gray(rgb);
imshow(gray_image);

背景がかなり明るく、ほとんどのチップが背景より暗いことがわかります。しかし、既定では、imfindcircles は背景よりも明るい円形オブジェクトを検出します。このため、imfindcircles でパラメーター 'ObjectPolarity' を 'dark' に設定して、暗い円を検索します。

[centers, radii] = imfindcircles(rgb,[20 25],'ObjectPolarity','dark')      %#ok<NASGU,ASGLU> Variables 'centers' and 'radii' are needed to display the output with proper names.
centers =

     []


radii =

     []

出力 centersradii が空になっていますが、これは円が検出されなかったことを表しています。このような結果になることはよくあります。これは、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 =

  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 =

   23.1604
   22.5710
   22.9576
   23.7356
   22.9551
   22.9995
   22.9055
   23.0298

今回は、imfindcircles が円をいくつか検出しました。正確には、8 つです。centers には円の中心の場所が格納されており、radii にはこれらの円の推定半径が格納されています。

手順 5: イメージ上での円の描画

関数 viscircles を使用すると、イメージ上に円を描画できます。imfindcircles からの出力変数 centersradii は、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,'Method','twostage');

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 の 'EdgeColor' パラメーターを変更して、異なる色 (ここでは青) で "明るい" 円を描画します。

imshow(rgb);

hBright = viscircles(centersBright, radiiBright,'EdgeColor','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,'EdgeColor','b');

手順 11: "暗い" 円と "明るい" 円を同時に描画

これで、imfindcircles はすべての黄色のチップと 1 つの緑のチップを検出しました。これらのチップを青で、'ObjectPolarity' を 'dark' にして先に検出していた他のチップを赤で描画します。

h = viscircles(centers,radii);

すべての円が検出されています。最後に、注意の必要な点があります。パラメーターを検出率が高くなるような値に変更すると、検出される円は増えますが、円を誤って検出する可能性も高くなります。正しく検出できる円の数 (検出率) と誤って検出される円の数 (誤認率) の間にはトレードオフがあります。

円探しをお楽しみください!

この情報は役に立ちましたか?