Main Content

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

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

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

さまざまな色の円形プラスチック チップのイメージを読み取って表示します。検出対象となるたくさんの円があるだけでなく、円を検出するという観点でこのイメージを捉えると、いくつかの興味深い点が見えてきます。

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

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

rgb = imread("coloredChips.png");
imshow(rgb)

Figure contains an axes object. The axes object contains an object of type image.

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

関数 drawline を使用して、円の適切な半径の範囲を検索します。チップのおおよその直径の上に線を描画します。

d = drawline;

Figure contains an axes object. The axes object contains 2 objects of type image, images.roi.line.

ライン ROI の長さはチップの直径です。通常のチップの直径は 40 ~ 50 ピクセルの範囲にあります。

pos = d.Position;
diffPos = diff(pos);
diameter = hypot(diffPos(1),diffPos(2))
diameter = 45

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

関数 imfindcircles により、ある半径の範囲を持つ円を検索します。20 ~ 25 ピクセルの範囲の半径を持つ円を検索します。その前に、背景と比較してオブジェクトが明るいか暗いかを確認しておきます。これを確認するために、このイメージのグレースケール バージョンを見てみましょう。

gray_image = im2gray(rgb);
imshow(gray_image)

Figure contains an axes object. The axes object contains an object of type image.

背景がかなり明るく、ほとんどのチップが背景より暗いことがわかります。しかし、既定では、imfindcircles は背景よりも明るい円形オブジェクトを検出します。したがって、暗い円を検索するには、imfindcircles で名前と値の引数 ObjectPolarity"dark" に指定します。

[centers,radii] = imfindcircles(rgb,[20 25],ObjectPolarity="dark")
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 = 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 からの出力変数 centersradii は、viscircles に直接渡すことが可能です。

imshow(rgb)
h = viscircles(centers,radii);

Figure contains an axes object. The axes object contains 3 objects of type line, image.

円の中心は正しく配置されており、対応する半径も実際のチップとうまく一致しているようです。しかし、まだ検出されていないチップがかなりあります。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);

Figure contains an axes object. The axes object contains 3 objects of type line, image.

手順 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);

Figure contains an axes object. The axes object contains 3 objects of type line, image.

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);

Figure contains an axes object. The axes object contains 3 objects of type line, image.

imfindcircles の両方のメソッドが、一部だけ見えている (隠れている) チップの中心と半径を正確に検出することに注意してください。

手順 7: まだ検出されない円がある理由

最後の結果を見てみると、面白いことに、imfindcircles はイメージ内の黄色のチップを検出していません。黄色のチップは、背景とのコントラストが強くありません。それどころか、背景とかなり近い強度をもっているようです。黄色のチップは、想定していたほど背景より暗くないということでしょうか。これを確認するために、このイメージのグレースケール バージョンをもう一度表示します。

imshow(gray_image)

Figure contains an axes object. The axes object contains an object of type 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");

Figure contains an axes object. The axes object contains 3 objects of type line, image.

検出されていなかった黄色のチップが 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");

Figure contains an axes object. The axes object contains 3 objects of type line, image.

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

これで、imfindcircles はすべての黄色のチップと 1 つの緑のチップを検出しました。これらのチップは青で描画し、先に検出済みの他のチップ (ObjectPolarity"dark" に設定して検出したもの) は赤で描画します。

h = viscircles(centers,radii);

Figure contains an axes object. The axes object contains 5 objects of type line, image.

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

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

参考

| |

関連するトピック