2012-04-25 97 views
24

我在填充黑色硬币内部的白洞时遇到了问题,因此我只能使用填充黑色硬币的0-255二进制图像。我已经使用了中值滤镜来实现它,但是在那种情况下硬币之间的连接桥梁的发展和它去是不可能的侵蚀后,多次认出来......所以,我需要一个简单的floodFill类似方法在OpenCV中在二进制物体内填充孔

这是我与孔图像:

enter image description here

编辑:填充函数必须填补孔在没有提示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

回答

36

有两种方法做到这一点:

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

+0

+1只是因为我喜欢简单的解决方案。 – karlphillip 2012-04-25 15:03:34

+0

在我的情况cvDrawContours不起作用我认为,我已更新的帖子.. – 2012-04-25 16:03:53

+0

好吧,我有一个参数问题,我应该使用:cvDrawContours(灰色,ptr,颜色,0,CV_FILLED,8); 0作为max_level参数。嗯,现在它充满了成功:) – 2012-04-25 16:11:44

1

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

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

+0

在我的情况下,cvDrawContours不起作用我认为,我已更新后... – 2012-04-25 16:04:50

5

我想象一下,一个简单的扩张和侵蚀会相当好地缩小差距。我想也许这就是你要找的。

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

使用hough变换的好处是该算法实际上会给你估计每个圆的大小和位置,所以你可以基于该模型重建一个理想的图像。它也应该是非常强大的重叠,尤其是考虑到输入图像的质量(即不必担心误报,因此可以降低结果的阈值)。

3

你可能会寻找Fillhole transformation,形态图像重建的应用。

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

+2

但是对于opencv不适用于matlab – 2012-04-25 14:40:45

+0

@lbstr:该页面主要解释理论。我无法为您提供OpenCV代码。 – thiton 2012-04-25 14:42:04

+0

链接已死。 – Reunanen 2018-01-03 09:17:55

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. 

总之,算法就是找到所有的孔,去除大的,然后写只有原始图像上的小图像。

0

我一直在环顾网络找到一个合适的imfill功能(作为Matlab中的一个),但在OpenCV中使用C工作。一些reaserches后,我终于想出了一个解决方案:

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 =目标图像
  • 轮廓=指针到第一轮廓
  • 白色=颜色用于填充轮廓
  • 0 =最大水平绘制的轮廓。如果为0,则只绘制轮廓
  • CV_FILLED =绘制等高线的线的粗细。如果是负值(例如= CV_FILLED),则绘制轮廓内部。

更多信息请参阅openCV文档。

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