counting objects & better way to filling holes

I am new to OpenCV and am trying to count the number of objects in an image. I have done this before using MATLAB Image Processing Toolbox and adapted the same approach in OpenCV (Android) also.

The first step was to convert an image to gray scale. Then to threshold it and then counting the number of blobs. In Matlab there is a command - "bwlabel", which gives the number of blobs. I couldn't find such thing in OpenCV (again, I am a noob in OpenCV as well as Android).

Here is my code,

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

在这里输入图像描述

In the next step, I tried to fill the holes and letters using dilation followed by an erosion but the blobs gets attached to each other which will ultimately give a wrong count. There is a tradeoff between filling holes and getting the blobs attached to each other on tuning the parameters for dilation and erosion.

Here is the code,

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

Because sometimes my M&Ms might be just next to each other! ;)

在这里输入图像描述

I also tried to use Hough Circles but the result is very unreliable (tested with coin images as well as real coins)

Here is the code,

//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();
}

The problem with this approach is that the algorithm is limited to perfect circles only (AFAIK). So, it doesn't work well when I try to scan and count M&Ms or coins lying on my desk (because angle of the device changes). With this approach, sometimes I get less no. of coins detected and sometimes more (I don't get it why more??).

On scanning this image the app sometimes shows 19 coins and sometimes 38 coins counted...I know there are other features which may be detected as circles but I totally don't get it why 38..?

在这里输入图像描述

So my questions...

  • Is there a better way to fill holes without joining adjacent blobs?
  • How do I count the number of objects accurately? I don't want to limit my app to counting only circles with HoughCircles approach.
  • FYI : OpenCV-2.4.9-android-sdk. Kindly keep in mind that I am a newbie in OpenCV and Android too.

    Any help is much appreciated.

    Thanks & Cheers!

    Jainam


    So to proceed we take your threshold image which you have generated as input and further modify it. The present code is in C++ but I guess you can easily convert it into android platform 输入图像

    Now instead of dilation or blurring you can try flood fill

    which results in

    充满洪水的图像

    Finally now applying the contour detection algorithm algorithm we get 最终输出

    The code for the above is

        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
    
                }
    

    I hope this helps you in solving your problem


    Just check it out,

  • Blur source.

  • Threshold binary inverted on gray.

  • Find contours, note that you should use CV_RETR_EXTERNAL as contour retrieval mode.

  • You can take the contours size as your object count.

    在这里输入图像描述

    在这里输入图像描述

    Code:

      Mat tmp,thr;
      Mat src=imread("img.jpg",1);
      blur(src,src,Size(3,3));
      cvtColor(src,tmp,CV_BGR2GRAY);
      threshold(tmp,thr,220,255,THRESH_BINARY_INV);
      imshow("thr",thr);
    
      vector< vector <Point> > contours; // Vector for storing contour
      vector< Vec4i > hierarchy;
    
      findContours( thr, contours, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE ); // Find the contours in the image
      for( int i = 0; i< contours.size(); i=hierarchy[i][0] ) // iterate through each contour.
         {
           Rect r= boundingRect(contours[i]);
           rectangle(src,r, Scalar(0,0,255),2,8,0);
         }
     cout<<"Numeber of contour = "<<contours.size()<<endl;
     imshow("src",src);
     waitKey();
    
    链接地址: http://www.djcxy.com/p/39508.html

    上一篇: 请告诉我关于这个计算机视觉任务

    下一篇: 计数物体和更好的填孔方法