3

私は OpenCV が初めてで、画像内のオブジェクトの数を数えようとしています。MATLAB Image Processing Toolbox を使用する前にこれを行い、OpenCV (Android) でも同じアプローチを採用しました。

最初のステップは、画像をグレースケールに変換することでした。次に、しきい値を設定してから、ブロブの数を数えます。Matlab には、"bwlabel" というコマンドがあり、ブロブの数が表示されます。私はOpenCVでそのようなことを見つけることができませんでした(繰り返しますが、私はOpenCVとAndroidの初心者です)。

ここに私のコードがあります、

//JPG to Bitmap to MAT
Bitmap i = BitmapFactory.decodeFile(imgPath + "mms.jpg");
Bitmap bmpImg = i.copy(Bitmap.Config.ARGB_8888, false);
Mat srcMat = new Mat ( bmpImg.getHeight(), bmpImg.getWidth(), CvType.CV_8UC3);
Utils.bitmapToMat(bmpImg, srcMat);

ここに画像の説明を入力

//convert to gray scale and save image
Mat gray = new Mat(srcMat.size(), CvType.CV_8UC1);
Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_RGB2GRAY,4);
//write bitmap
Boolean bool = Highgui.imwrite(imgPath + "gray.jpg", gray);

ここに画像の説明を入力

//thresholding
Mat threshed = new Mat(bmpImg.getWidth(),bmpImg.getHeight(), CvType.CV_8UC1);
Imgproc.adaptiveThreshold(gray, threshed, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 75, 5);//15, 8 were original tests. Casey was 75,10
Core.bitwise_not(threshed, threshed);
Utils.matToBitmap(threshed, bmpImg);
//write bitmap
bool = Highgui.imwrite(imgPath + "threshed.jpg", threshed);
Toast.makeText(this, "Thresholded image saved!", Toast.LENGTH_SHORT).show();

ここに画像の説明を入力

次のステップでは、拡張と侵食を使用して穴と文字を埋めようとしましたが、ブロブが互いにくっついてしまい、最終的には間違ったカウントになります。膨張と侵食のパラメーターを調整する際に、穴を埋めることとブロブを互いに付着させることの間にはトレードオフがあります。

ここにコードがあります、

//morphological operations
//dilation
Mat dilated = new Mat(bmpImg.getWidth(),bmpImg.getHeight(), CvType.CV_8UC1);
Imgproc.dilate(threshed, dilated, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new org.opencv.core.Size (16, 16)));
Utils.matToBitmap(dilated, bmpImg);
//write bitmap
bool = Highgui.imwrite(imgPath + "dilated.jpg", dilated);
Toast.makeText(this, "Dilated image saved!", Toast.LENGTH_SHORT).show();

ここに画像の説明を入力

//erosion
Mat eroded = new Mat(bmpImg.getWidth(),bmpImg.getHeight(), CvType.CV_8UC1);
Imgproc.erode(dilated, eroded, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new org.opencv.core.Size(15, 15)));
Utils.matToBitmap(eroded, bmpImg);
//write bitmap
bool = Highgui.imwrite(imgPath + "eroded.jpg", eroded);
Toast.makeText(this, "Eroded image saved!", Toast.LENGTH_SHORT).show();

時々私のM&Mが隣り合っているかもしれないからです!;)

ここに画像の説明を入力

Hough Circles も使用しようとしましたが、結果は非常に信頼できません (実際のコインだけでなく、コインの画像でもテストされています)。

ここにコードがあります、

//hough circles
Mat circles = new Mat();

// parameters
int iCannyUpperThreshold = 100;
int iMinRadius = 20;
int iMaxRadius = 400;
int iAccumulator = 100;

Imgproc.HoughCircles(gray, circles, Imgproc.CV_HOUGH_GRADIENT, 
         1.0, gray.rows() / 8, iCannyUpperThreshold, iAccumulator, 
         iMinRadius, iMaxRadius);

// draw
if (circles.cols() > 0)
{
    Toast.makeText(this, "Coins : " +circles.cols() , Toast.LENGTH_LONG).show();
}
else
{
    Toast.makeText(this, "No coins found", Toast.LENGTH_LONG).show();
}

このアプローチの問題は、アルゴリズムが完全な円のみに制限されていることです(AFAIK)。そのため、机の上にある M&M やコインをスキャンしてカウントしようとすると、うまく機能しません (デバイスの角度が変わるため)。このアプローチでは、時々、ノーが少なくなります。検出されたコインの数と、時にはそれ以上の数 (なぜそれ以上なのかわかりません??)。

この画像をスキャンすると、アプリは 19 コインを表示することもあれば、38 コインをカウントすることもあります...円として検出される可能性のある他の機能があることは知っていますが、なぜ 38 なのかまったくわかりません..?

ここに画像の説明を入力

だから私の質問...

  1. 隣接するブロブを結合せずに穴を埋めるより良い方法はありますか?
  2. オブジェクトの数を正確にカウントするにはどうすればよいですか? HoughCircles アプローチでサークルのみをカウントするようにアプリを制限したくありません。

参考までに: OpenCV-2.4.9-android-sdk. 私もOpenCVとAndroidの初心者であることを心に留めておいてください。

どんな助けでも大歓迎です。

ありがとう&乾杯!

ジャイナム

4

2 に答える 2

1

続行するには、入力として生成したしきい値画像を取得し、さらに変更します。現在のコードは C++ ですが、Android プラットフォームに簡単に変換できると思います。 入力画像

膨張やぼかしの代わりに、塗りつぶしを試すことができます

その結果、

洪水で満たされた画像

最後に、取得した輪郭検出アルゴリズム アルゴリズムを適用します。 最終出力

上記のコードは

    Mat dst = imread($path to the threshold image); // image should be single channel black and white image
    imshow("dst",dst);

    cv::Mat mask = cv::Mat::zeros(dst.rows + 2, dst.cols + 2, CV_8U);

            // A image with size greater than the present object is created

    cv::floodFill(dst, mask, cv::Point(0,0), 255, 0, cv::Scalar(), cv::Scalar(),  4 + (255 << 8) + cv::FLOODFILL_MASK_ONLY);
    erode(mask,mask,Mat());
    // Now to remove the outer boundary
    rectangle(mask,Rect(0,0,mask.cols,mask.rows), Scalar(255,255,255),2,8,0);
    imshow("Mask",mask);


    Mat copy;
    mask.copyTo(copy);

    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    findContours( copy, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );

    vector<vector<Point> > contours_poly( contours.size() );
    vector<Rect> boundRect( contours.size() );
    vector<Point2f>center( contours.size() );
    vector<float>Distance( contours.size() );
    vector<float>radius( contours.size() );

    Mat drawing = cv::Mat::zeros(mask.rows, mask.cols, CV_8U);
    int num_object = 0;
    for( int i = 0; i < contours.size(); i++ ){
        approxPolyDP( Mat(contours[i]), contours_poly[i], 3, true );

            // To get rid of the smaller object and the outer rectangle created
            //because of the additional mask image we enforce a lower limit on area 
            //to remove noise and an upper limit to remove the outer border.    

        if (contourArea(contours_poly[i])>(mask.rows*mask.cols/10000) && contourArea(contours_poly[i])<mask.rows*mask.cols*0.9){
            boundRect[i] = boundingRect( Mat(contours_poly[i]) );
            minEnclosingCircle( (Mat)contours_poly[i], center[i], radius[i] );
            circle(drawing,center[i], (int)radius[i], Scalar(255,255,255), 2, 8, 0);
            rectangle(drawing,boundRect[i], Scalar(255,255,255),2,8,0);
            num_object++;
        }
    }

    cout <<"No. of object detected =" <<num_object<<endl;


    imshow("drawing",drawing);

    waitKey(2);
    char key = (char) waitKey(20);
    if(key == 32){
    // You can save your images here using a space

            }

これが問題の解決に役立つことを願っています

于 2014-06-13T19:58:04.740 に答える