填补Emgu CV中的空洞

7
我该如何在 Emgu CV 中填充二进制图像中的空洞? 在 Aforge.net 中,使用 Fillholes 类很容易实现。
3个回答

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;
    }

上述方法的好处在于您可以有选择性地填补符合您标准的空洞。例如,您可能希望填补像素计数(斑点内黑色像素的数量)低于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;
    }

我认为这是最好的答案。 - Rick2047
我也是。以前经常会导致内存溢出问题。 - Vito Gentile

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

在这种情况下,“new MCvScalar(0,0,0),new MCvScalar(0,0,0)”并不重要,因为您只是填充二进制图像的结果。您可以尝试其他设置,看看能够实现什么样的结果。“if(comp.area < 10000)”是需要更改的关键常量,如果要更改方法将填充哪个大小的孔。

以下是您可以期望的结果:

原始图像

Original Image

结果

The Resultant Image

这种方法的问题在于它非常占用内存,并且在200x200图像上吃掉了6GB的RAM,当我尝试200x300时,它吃掉了所有8GB的RAM,并使一切都崩溃了。除非您的大部分图像都是白色并且想要填补微小的间隙,或者您可以最小化应用该方法的位置,否则我建议避免使用它。如果您不希望编写自己的类,则建议使用Aforge FillHoles类,因为它是专门为此目的设计的。

祝好

克里斯


1
您可以使用FillConvexPoly
image.FillConvexPoly(externalContours.ToArray(), new Gray(255));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接