计数物体和更好的填孔方法

我是OpenCV的新手,并试图计算图像中的对象数量 。 在使用MATLAB Image Processing Toolbox之前,我已经完成了这个工作,并且在OpenCV(Android)中也采用了相同的方法。

第一步是将图像转换为灰度。 然后将其设为阈值,然后计算斑点的数量。 在Matlab中有一个命令 - “bwlabel”,它提供了斑点的数量。 我在OpenCV中找不到这样的东西(再次,我是OpenCV和Android的noob)。

这是我的代码,

//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&Ms可能会彼此相邻! ;)

在这里输入图像描述

我也尝试使用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&Ms或硬币时(因为设备的角度发生变化),这样做效果不佳。 采用这种方法,有时候我会减少不了。 的硬币检测,有时更多(我不明白为什么更多??)。

在扫描此图像时,该应用程序有时会显示19个硬币,有时会显示38个硬币......我知道还有其他功能可以作为圈子检测到,但我完全不明白为什么38 ..?

在这里输入图像描述

所以我的问题...

  • 有没有更好的方法来填充孔而不连接相邻的斑点?
  • 我如何准确计数对象的数量? 我不想限制我的应用只使用HoughCircles方法计算圆。
  • FYI:OpenCV-2.4.9-android-sdk。 请记住,我是OpenCV和Android的新手。

    任何帮助深表感谢。

    感谢和欢呼!

    Jainam


    因此,继续我们将您生成的阈值图像作为输入并进一步对其进行修改。 目前的代码是在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
    
                }
    

    我希望这可以帮助你解决你的问题


    只要看看,

  • 模糊来源。

  • 阈值二进制灰色倒置。

  • 找到轮廓,请注意,您应该使用CV_RETR_EXTERNAL作为轮廓检索模式。

  • 您可以将轮廓尺寸作为对象数量。

    在这里输入图像描述

    在这里输入图像描述

    码:

      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/39507.html

    上一篇: counting objects & better way to filling holes

    下一篇: OpenCV Bounding Box