在二进制对象内填充孔

时间:2012-04-25 12:52:17

标签: image-processing opencv javacv

我有一个问题,在黑色硬币内填充白洞,这样我就可以只有0-255二进制图像和填充的黑色硬币..我使用中位数过滤器来完成它但是在这种情况下,硬币之间的连接桥增长了经过几次侵蚀后无法识别它们......所以我需要在opencv中使用简单的floodFill方法

这是我的带洞图片:

enter image description here

编辑:类似函数的floodfill必须填充大组件中的漏洞而不提示X,Y坐标作为种子......

编辑:我尝试使用cvDrawContours函数,但我没有填充更大的轮廓。

这是我的代码:

        CvMemStorage mem = cvCreateMemStorage(0);
        CvSeq contours = new CvSeq();
        CvSeq ptr = new CvSeq();
        int sizeofCvContour = Loader.sizeof(CvContour.class);

        cvThreshold(gray, gray, 150, 255, CV_THRESH_BINARY_INV);

        int numOfContours = cvFindContours(gray, mem, contours, sizeofCvContour, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
        System.out.println("The num of contours: "+numOfContours); //prints 87, ok

        Random rand = new Random();
        for (ptr = contours; ptr != null; ptr = ptr.h_next()) {
            Color randomColor = new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat());
            CvScalar color = CV_RGB( randomColor.getRed(), randomColor.getGreen(), randomColor.getBlue());
            cvDrawContours(gray, ptr, color, color, -1, CV_FILLED, 8);
        }
        CanvasFrame canvas6  = new CanvasFrame("drawContours");
        canvas6.showImage(gray);

结果:(你可以在每枚硬币内看到黑洞)

enter image description here

7 个答案:

答案 0 :(得分:42)

有两种方法可以做到这一点:

1)轮廓填充:

首先反转图像,找到图像中的轮廓,用黑色填充并反转。

des = cv2.bitwise_not(gray)
contour,hier = cv2.findContours(des,cv2.RETR_CCOMP,cv2.CHAIN_APPROX_SIMPLE)

for cnt in contour:
    cv2.drawContours(des,[cnt],0,255,-1)

gray = cv2.bitwise_not(des)

结果图片:

enter image description here

2)影像开放:

kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(3,3))
res = cv2.morphologyEx(gray,cv2.MORPH_OPEN,kernel)

生成的图像如下:

enter image description here

你可以看到,两种情况都没有太大区别。

NB :灰色 - 灰度图像,所有代码都在OpenCV-Python中

答案 1 :(得分:5)

我想,一个简单的扩张和侵蚀会很好地缩小间隙。我想也许这就是你要找的东西。

更强大的解决方案是对整个图像进行边缘检测,然后对圆进行霍夫变换。一个快速谷歌显示有各种语言的代码示例使用霍夫变换的大小不变检测圆,所以希望这将给你一些东西继续。

使用霍夫变换的好处是该算法实际上可以估计每个圆的大小和位置,因此您可以基于该模型重建理想图像。重叠也应该非常强大,特别是考虑到输入图像的质量(即减少对误报的担心,因此可以降低结果的阈值)。

答案 2 :(得分:4)

你可能正在寻找Fillhole transformation,一种形态学图像重建的应用。

这种转变将填补你的硬币中的漏洞,即使以填充相邻硬币组之间的所有孔为代价。其他海报建议的Hough空间或基于开放的解决方案可能会为您提供更好的高级识别结果。

答案 3 :(得分:1)

尝试使用cvFindContours()功能。您可以使用它来查找连接的组件。使用正确的参数,此函数返回一个列表,其中包含每个连接组件的轮廓。

找出代表洞的轮廓。然后使用cvDrawContours()按前景色填充选定的轮廓,从而关闭孔。

答案 4 :(得分:1)

我认为如果对象被触摸或拥挤,使用轮廓和数学模型开放会有一些问题。 相反,找到并测试了以下简单的解决方案。它工作得非常好,不仅适用于此图像,还适用于任何其他图像。

这是http://blogs.mathworks.com/steve/2008/08/05/filling-small-holes/

中显示的步骤(已优化)

I:输入图片

1. filled_I = floodfill(I). // fill every hole in the image.
2. inverted_I = invert(I)`.   
3. holes_I = filled_I AND inverted_I. // finds all holes 
4. cc_list = connectedcomponent(holes_I) // list of all connected component in holes_I.
5. holes_I = remove(cc_list,holes_I, smallholes_threshold_size) // remove all holes from holes_I having size > smallholes_threshold_size.
6. out_I = I OR holes_I. // fill only the small holes.

简而言之,算法只是找到所有的洞,删除大洞,然后只在原始图像上写下小洞。

答案 5 :(得分:1)

我一直在互联网上寻找一个正确的 imfill 功能(如Matlab中的功能),但是在C中使用OpenCV。经过一些研究,我终于想出了一个解决方案:

IplImage* imfill(IplImage* src)
{
    CvScalar white = CV_RGB( 255, 255, 255 );

    IplImage* dst = cvCreateImage( cvGetSize(src), 8, 3);
    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* contour = 0;

    cvFindContours(src, storage, &contour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE );
    cvZero( dst );

    for( ; contour != 0; contour = contour->h_next )
    {
        cvDrawContours( dst, contour, white, white, 0, CV_FILLED);
    }

    IplImage* bin_imgFilled = cvCreateImage(cvGetSize(src), 8, 1);
    cvInRangeS(dst, white, white, bin_imgFilled);

    return bin_imgFilled;
}

为此:Original Binary Image

结果是:Final Binary Image

诀窍在于cvDrawContours函数的参数设置: cvDrawContours(dst,contour,white,white,0,CV_FILLED);

  • dst =目标图片
  • contour =指向第一个轮廓的指针
  • white =用于填充轮廓的颜色
  • 0 =绘制轮廓的最大级别。如果为0,则仅绘制轮廓
  • CV_FILLED =绘制轮廓的线条粗细。如果为负(例如,= CV_FILLED),则绘制轮廓内部。

openCV文档中的更多信息。

可能有一种方法可以直接将“dst”作为二进制图像,但我找不到如何将cvDrawContours函数与二进制值一起使用。

答案 6 :(得分:0)

如果有人正在寻找cpp实现-

            std::vector<std::vector<cv::Point> > contours_vector;

            cv::findContours(input_image, contours_vector, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);

            cv::Mat contourImage(input_image.size(), CV_8UC1, cv::Scalar(0));
            for ( ushort contour_index = 0; contour_index < contours_vector.size(); contour_index++) {
                cv::drawContours(contourImage, contours_vector, contour_index, cv::Scalar(255), -1);
            }

            cv::imshow("con", contourImage);
            cv::waitKey(0);

enter image description here

enter image description here