计算物体和填补洞的更好方法

时间:2014-06-13 11:59:01

标签: android opencv count fill blobs

我是OpenCV的新手,我正在尝试计算图片中的对象数量。我在使用MATLAB图像处理工具箱之前完成了这项工作,并在OpenCV(Android)中采用了相同的方法。

第一步是将图像转换为灰度。然后阈值,然后计算blob的数量。在Matlab中有一个命令 - “bwlabel”,它给出了blob的数量。我在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);

enter image description here

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

enter image description here

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

enter image description here

在下一步中,我尝试使用扩张填充孔和字母,然后进行侵蚀,但斑点相互连接,最终会产生错误的计数。在调整扩张和侵蚀参数时,需要在填充孔和使斑点相互连接之间进行权衡。

这是代码,

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

enter image description here

//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可能只是彼此相邻! ;)

enter image description here

我也试过使用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 ..?

enter image description here

所以我的问题......

  1. 有没有更好的方法来填充孔而不加入相邻的blob?
  2. 如何准确计算物体数量?我不想限制我的应用只计算使用HoughCircles方法的圈子。
  3. 仅供参考:OpenCV-2.4.9-android-sdk。请记住,我也是OpenCV和Android的新手。

    非常感谢任何帮助。

    谢谢&干杯!

    Jainam

2 个答案:

答案 0 :(得分:1)

因此,为了继续,我们将您生成的阈值图像作为输入并进一步修改。目前的代码是用C ++编写的,但我想你可以很容易地把它转换成android平台 Input Image

现在,您可以尝试填充

,而不是扩张或模糊

导致

Flood-filled image

最后,现在应用我们得到的轮廓检测算法算法 Final output

以上代码是

    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

            }

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

答案 1 :(得分:0)

请查看,

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

enter image description here

enter image description here

代码:

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