在emgu cv中填充孔

时间:2011-08-26 12:01:49

标签: c# emgucv

如何在emgu cv中填充二进制图像中的孔? 在Aforge.net中,使用Fillholes类很容易。

感谢

3 个答案:

答案 0 :(得分:12)

认为这个问题有点陈旧,我想为这个问题提供另一种解决方案。

如果使用以下内容,您可以获得与Chris没有内存问题相同的结果:

private Image<Gray,byte> FillHoles(Image<Gray,byte> image)
    {
        var resultImage = image.CopyBlank();
        Gray gray = new Gray(255);
        using (var mem = new MemStorage())
        {
            for (var contour = image.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, 
                RETR_TYPE.CV_RETR_CCOMP, 
                mem); contour!= null; contour = contour.HNext)
            {
                resultImage.Draw(contour, gray, -1);
            }
        }

        return resultImage;
    }

上述方法的好处在于您可以选择性地填充符合条件的孔。例如,您可能想要填充其像素数(blob内的黑色像素数)低于50的孔等。

private Image<Gray,byte> FillHoles(Image<Gray,byte> image, int minArea, int maxArea)
    {
        var resultImage = image.CopyBlank();
        Gray gray = new Gray(255);

        using (var mem = new MemStorage())
        {
            for (var contour = image.FindContours(
                CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, 
                RETR_TYPE.CV_RETR_CCOMP, 
                mem); contour!= null; contour = contour.HNext)
            {
                if ( (contour.Area < maxArea) && (contour.Area > minArea) )
                    resultImage.Draw(contour, gray, -1);
            }
        }

        return resultImage;
    }

答案 1 :(得分:7)

是的,有一种方法,但它基于cvFloodFill操作有点乱。现在所有这个算法都是用一种颜色填充区域,直到它到达类似于区域增长算法的边缘。要有效地使用它,你需要使用一些创造性的编码,但我警告你这个代码只是为了让你开始它可能需要重新分解来加快速度。因为它的循环遍历每个小于255的像素cvFloodFill检查区域的大小,然后是否在某个区域下填充它。

重要的是要注意,当使用指针时,图像的副本由要提供给cvFloodFill操作的原始图像组成。如果提供了直接图像,那么最终会得到一张白色图像。

OpenFileDialog OpenFile = new OpenFileDialog();

if (OpenFileDialog.ShowDialog() == DialogResult.OK)
{
    Image<Bgr, byte> image = new Image<Bgr, byte>(OpenFile.FileName);

            for (int i = 0; i < image.Width; i++)
            {
                for (int j = 0; j < image.Height; j++)
                {
                    if (image.Data[j, i, 0] != 255)
                    {
                        Image<Bgr, byte> image_copy = image.Copy();
                        Image<Gray, byte> mask = new Image<Gray, byte>(image.Width + 2, image.Height + 2);
                        MCvConnectedComp comp = new MCvConnectedComp();
                        Point point1 = new Point(i, j);
                        //CvInvoke.cvFloodFill(
                        CvInvoke.cvFloodFill(image_copy.Ptr, point1, new MCvScalar(255, 255, 255, 255),
                        new MCvScalar(0, 0, 0),
                        new MCvScalar(0, 0, 0), out comp,
                        Emgu.CV.CvEnum.CONNECTIVITY.EIGHT_CONNECTED,
                        Emgu.CV.CvEnum.FLOODFILL_FLAG.DEFAULT, mask.Ptr);
                        if (comp.area < 10000)
                        {
                            image = image_copy.Copy();
                        }
                    }
                }
            }
}

“新MCvScalar(0,0,0),新MCvScalar(0,0,0)”在这种情况下并不重要,因为您只填写二进制图像的结果。你可以使用其他设置来查看你可以实现的结果。 “if(comp.area&lt; 10000)”是要更改的常量,您想要更改方法将填充的孔大小。

这些是您可以期待的结果:

<强>原始

Original Image

<强>结果

The Resultant Image

这种方法的问题在于内存非常密集,它在200x200图像上占用了6GB的内存,当我尝试200x300时,它吃掉了所有8GB的内存,并使所有内容都崩溃了。除非您的大部分图像是白色的,并且您希望填充微小的间隙,或者您可以最小化应用方法的位置,我会避免它。我建议您编写自己的类来检查每个不是255的像素并添加它周围的像素数。然后,您可以记录每个不是255的像素的位置(在一个简单的列表中),如果您的计数低于阈值,则在图像中将这些位置设置为255(通过迭代列表)。

如果您不想编写自己的Aforge FillHoles类,我会坚持使用它,因为它是为此目的而设计的。

干杯

克里斯

答案 2 :(得分:1)

您可以使用 FillConvexPoly

image.FillConvexPoly(externalContours.ToArray(), new Gray(255));