7

我正在使用 JavaCV(OpenCV 包装器)进行硬币检测,但是当硬币连接时我遇到了一个小问题。如果我尝试腐蚀它们以分离这些硬币,它们会失去圆形形状,如果我尝试计算每个硬币内部的像素,可能会出现问题,以至于一些硬币可能被误算为更大的硬币。我想要做的是首先重塑它们并使它们像一个圆圈(等于那个硬币的半径),然后计算它们里面的像素。

这是我的阈值图像:在此处输入图像描述

这是侵蚀的图像:在此处输入图像描述

有什么建议么?或者有没有更好的方法来打破硬币之间的桥梁?

4

5 回答 5

11

它看起来类似于我最近不得不分离在琼脂板上生长的细菌菌落的问题。我对阈值图像执行了距离变换(在您的情况下,您需要将其反转)。然后找到距离图的峰值(通过计算扩张距离图和距离图之间的差异并找到零值)。然后,我假设每个峰值是圆(硬币)的中心,距离图中峰值的值是圆的半径。

这是此管道后图像的结果: 距离变换工作

我是 OpenCV 和 c++ 的新手,所以我的代码可能非常混乱,但我做到了:

int main( int argc, char** argv ){

    cv::Mat objects, distance,peaks,results;
    std::vector<std::vector<cv::Point> > contours;

    objects=cv::imread("CUfWj.jpg");
    objects.copyTo(results);
    cv::cvtColor(objects, objects, CV_BGR2GRAY);
    //THIS IS THE LINE TO BLUR THE IMAGE CF COMMENTS OF THIS POST
    cv::blur( objects,objects,cv::Size(3,3));
    cv::threshold(objects,objects,125,255,cv::THRESH_BINARY_INV);


    /*Applies a distance transform to "objects".
     * The result is saved in "distance" */
    cv::distanceTransform(objects,distance,CV_DIST_L2,CV_DIST_MASK_5);

    /* In order to find the local maxima, "distance"
     * is subtracted from the result of the dilatation of
     * "distance". All the peaks keep the save value */
    cv::dilate(distance,peaks,cv::Mat(),cv::Point(-1,-1),3);
    cv::dilate(objects,objects,cv::Mat(),cv::Point(-1,-1),3);

    /* Now all the peaks should be exactely 0*/
    peaks=peaks-distance;

    /* And the non-peaks 255*/
    cv::threshold(peaks,peaks,0,255,cv::THRESH_BINARY);
    peaks.convertTo(peaks,CV_8U);

    /* Only the zero values of "peaks" that are non-zero
     * in "objects" are the real peaks*/
    cv::bitwise_xor(peaks,objects,peaks);

    /* The peaks that are distant from less than
     * 2 pixels are merged by dilatation */
    cv::dilate(peaks,peaks,cv::Mat(),cv::Point(-1,-1),1);

    /* In order to map the peaks, findContours() is used.
     * The results are stored in "contours" */
    cv::findContours(peaks, contours, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
    /* The next steps are applied only if, at least,
     * one contour exists */
    cv::imwrite("CUfWj2.jpg",peaks);
    if(contours.size()>0){

        /* Defines vectors to store the moments of the peaks, the center
         * and the theoritical circles of the object of interest*/
        std::vector <cv::Moments> moms(contours.size());
        std::vector <cv::Point> centers(contours.size());
        std::vector<cv::Vec3f> circles(contours.size());
        float rad,x,y;
        /* Caculates the moments of each peak and then the center of the peak
         * which are approximatively the center of each objects of interest*/

        for(unsigned int i=0;i<contours.size();i++) {
            moms[i]= cv::moments(contours[i]);
            centers[i]= cv::Point(moms[i].m10/moms[i].m00,moms[i].m01/moms[i].m00);
            x= (float) (centers[i].x);
            y= (float) (centers[i].y);
            if(x>0 && y>0){
                rad= (float) (distance.at<float>((int)y,(int)x)+1);
                circles[i][0]= x;
                circles[i][3]= y;
                circles[i][2]= rad;
                cv::circle(results,centers[i],rad+1,cv::Scalar( 255, 0,0 ), 2, 4, 0 );
            }
        }
        cv::imwrite("CUfWj2.jpg",results);
    }

    return 1;
}
于 2012-04-25T22:32:22.330 回答
5

您不需要侵蚀,只需一组好的参数cvHoughCircles()

在此处输入图像描述

用于生成此图像的代码来自我的另一篇文章:Detecting Circles,具有以下参数:

CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, gray->height/12, 80, 26);
于 2012-04-25T13:16:56.400 回答
2

OpenCV 有一个名为 HoughCircles() 的函数,可以应用于您的案例,而无需分隔不同的圆圈。你可以从 JavaCV 调用它吗?如果是这样,它会做你想做的事(检测和计算圆圈),绕过你的分离问题。

要点是在不先分离圆圈的情况下准确检测圆圈。可以使用其他算法(例如模板匹配来代替广义霍夫变换,但您必须考虑到硬币的不同大小。

于 2012-04-25T10:21:34.387 回答
1

基于侵蚀的对象识别的常用方法是标记侵蚀图像中的连续区域,然后重新增长它们直到它们与原始图像中的区域匹配。不过,在您的情况下,霍夫圆是一个更好的主意。

于 2012-04-26T11:33:13.110 回答
0

在检测到连接的硬币后,我建议应用形态学操作将区域分类为“肯定硬币”和“绝对不是硬币”,应用距离变换,然后运行分水岭以确定边界。这个场景实际上是 OpenCV 中分水岭算法的演示示例——也许它是为了回答这个问题而创建的。

于 2014-07-23T12:58:17.693 回答