web-dev-qa-db-ja.com

画像上のコインの検出(および楕円のフィット)

私は現在、平らな表面(机など)に横たわっているいくつかのコインを検出しようとしているプロジェクトに取り組んでいます。コインは重ならず、他のオブジェクトによって隠されません。しかし、他のオブジェクトが見える可能性があり、照明条件は完璧ではないかもしれません...

したがって、各ポイントは楕円として表示されるはずです。カメラの位置がわからないので、楕円の形状は、コインが撮影される角度に応じて、円(上からの眺め)から平らな楕円までさまざまです。

私の問題は、コインをどのように抽出し、最終的にそれらの上に楕円を合わせる方法がわからないことです(これをさらに計算するために探しています)。

今のところ、OpenCVでしきい値を設定し、findContours()を使用して等高線を取得し、楕円を近似することにより、最初の試みを行ったばかりです。残念ながら、等高線はコインの形状(反射、悪い照明など)を私に与えることはめったになく、ユーザーにしきい値を設定させたくないので、この方法も好ましくありません。

別のアイデアは、その画像で楕円のテンプレートマッチング方法を使用することでしたが、カメラの角度や楕円のサイズがわからないため、これがうまく機能しないと思います...

だから私は誰かが私の場合に機能する方法を教えてくれるかどうか尋ねたいと思いました。

画像から3枚のコインをすばやく抽出する方法はありますか?モバイルデバイスでリアルタイムに計算を行う必要があり、異なるまたは変化するライトまたは背景の色に対してメソッドが高すぎてはなりません。 =

誰もがどの方法が私のために働くことができるかについて私に何かヒントを与えることができれば素晴らしいでしょう。

44
florianbaethge

以下は、従来のアプローチを実装したC99ソースです(OpenCV doco に基づいています):

#include "cv.h"
#include "highgui.h"

#include <stdio.h>

#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif

//
// We need this to be high enough to get rid of things that are too small too
// have a definite shape.  Otherwise, they will end up as ellipse false positives.
//
#define MIN_AREA 100.00    
//
// One way to tell if an object is an ellipse is to look at the relationship
// of its area to its dimensions.  If its actual occupied area can be estimated
// using the well-known area formula Area = PI*A*B, then it has a good chance of
// being an ellipse.
//
// This value is the maximum permissible error between actual and estimated area.
//
#define MAX_TOL  100.00

int main( int argc, char** argv )
{
    IplImage* src;
    // the first command line parameter must be file name of binary (black-n-white) image
    if( argc == 2 && (src=cvLoadImage(argv[1], 0))!= 0)
    {
        IplImage* dst  = cvCreateImage( cvGetSize(src), 8, 3 );
        CvMemStorage* storage = cvCreateMemStorage(0);
        CvSeq* contour = 0;    
        cvThreshold( src, src, 1, 255, CV_THRESH_BINARY );
        //
        // Invert the image such that white is foreground, black is background.
        // Dilate to get rid of noise.
        //
        cvXorS(src, cvScalar(255, 0, 0, 0), src, NULL);
        cvDilate(src, src, NULL, 2);    
        cvFindContours( src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE, cvPoint(0,0));
        cvZero( dst );

        for( ; contour != 0; contour = contour->h_next )
        {
            double actual_area = fabs(cvContourArea(contour, CV_WHOLE_SEQ, 0));
            if (actual_area < MIN_AREA)
                continue;

            //
            // FIXME:
            // Assuming the axes of the ellipse are vertical/perpendicular.
            //
            CvRect rect = ((CvContour *)contour)->rect;
            int A = rect.width / 2; 
            int B = rect.height / 2;
            double estimated_area = M_PI * A * B;
            double error = fabs(actual_area - estimated_area);    
            if (error > MAX_TOL)
                continue;    
            printf
            (
                 "center x: %d y: %d A: %d B: %d\n",
                 rect.x + A,
                 rect.y + B,
                 A,
                 B
            );

            CvScalar color = CV_RGB( Rand() % 255, Rand() % 255, Rand() % 255 );
            cvDrawContours( dst, contour, color, color, -1, CV_FILLED, 8, cvPoint(0,0));
        }

        cvSaveImage("coins.png", dst, 0);
    }
}

Carnieriが提供したバイナリイメージを考えると、これは出力です。

./opencv-contour.out coin-ohtsu.pbm
center x: 291 y: 328 A: 54 B: 42
center x: 286 y: 225 A: 46 B: 32
center x: 471 y: 221 A: 48 B: 33
center x: 140 y: 210 A: 42 B: 28
center x: 419 y: 116 A: 32 B: 19

そして、これは出力画像です:

coins

改善できること:

  • さまざまな楕円の向きを処理します(現在、軸は垂直/水平であると仮定しています)。これは、画像モーメントを使用して行うのは難しくありません。
  • オブジェクトの凸性を確認します(cvConvexityDefectsを見てください)

コインを他のオブジェクトと区別する最良の方法は、おそらく形状によるものです。他の低レベルの画像機能は考えられません(明らかに色が出ています)。だから、私は2つのアプローチを考えることができます:

従来のオブジェクト検出

最初のタスクは、オブジェクト(コインと非コイン)をバックグラウンドから分離することです。 Carnieriによって示唆されたOhtsuの方法は、ここでうまく機能します。画像がbipartiteであることを心配しているようですが、これが問題になるとは思いません。かなりの量のデスクが見える限り、ヒストグラムにピークが1つあることが保証されます。そして、机の上に視覚的に区別できるオブジェクトがいくつかある限り、2番目のピークが保証されます。

膨張 しきい値処理によって残ったノイズを取り除くために、バイナリイメージを数回使用します。コインは比較的大きいので、この形態学的操作を生き延びなければなりません。

リージョングローイングを使用して白いピクセルをオブジェクトにグループ化します。隣接する前景ピクセルを繰り返し接続します。この操作の最後に、切り離されたオブジェクトのリストが表示され、各オブジェクトが占めるピクセルがわかります。

この情報から、オブジェクトの幅と高さがわかります(前のステップから)。そのため、オブジェクトを囲む楕円のサイズを推定し、この特定のオブジェクトが楕円とどの程度一致するかを確認できます。幅と高さの比を使用する方が簡単な場合があります。

または、 moments を使用して、より正確な方法でオブジェクトの形状を決定できます。

49
mpenkov

将来私がやったように他の誰かがこの問題を抱えていても、C++を使用している場合:

findContoursを使用して輪郭を見つけたら(上記のMishaの答えのように)、fitEllipseを使用して楕円を簡単に適合させることができます。

    vector<vector<Point> > contours;

    findContours(img, contours, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0,0));

    RotatedRect rotRecs[contours.size()];

    for (int i = 0; i < contours.size(); i++) {
        rotRecs[i] = fitEllipse(contours[i]);
    }
6
jamsandwich

あなたの問題に最適な方法が何なのかわかりません。ただし、具体的にはしきい値処理については、画像ヒストグラムの分析に基づいて最適なしきい値を自動的に見つける大津の方法を使用できます。パラメーターThresholdTypeTHRESH_OTSUと等しいOpenCVの threshold メソッドを使用します。

ただし、Otsuの方法は、バイモーダルヒストグラムを持つ画像(たとえば、暗い背景に明るいオブジェクトがある画像)でのみ機能することに注意してください。

おそらくこれを見たことがありますが、2Dポイントのセット(たとえば、接続されたコンポーネント)の周りに 楕円のフィッティング のメソッドもあります。

[〜#〜] edit [〜#〜]:サンプル画像に適用されるOtsuの方法:

グレースケール画像: grayscale image

大津の方法を適用した結果: Otsu image

6
carnieri