填补 emgu cv 中的漏洞

发布于 2024-12-01 13:54:37 字数 65 浏览 0 评论 0原文

如何填补 emgu cv 中二进制图像的漏洞? 在 Aforge.net 中这很简单,使用 Fillholes 类。

How can I fill the holes in binary image in emgu cv?
In Aforge.net it's easy, use Fillholes class.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

戏舞 2024-12-08 13:54:37

认为这个问题有点老了,我想为这个问题提供一个替代解决方案。

如果您使用以下方法,您可以获得与 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;
    }

Thought the question is a little bit old, I'd like to contribute an alternative solution to the problem.

You can obtain the same result as Chris' without memory problem if you use the following:

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

The good thing about the method above is that you can selectively fill holes that meets your criteria. For example, you may want to fill holes whose pixel count (count of black pixels inside the blob) is below 50, etc.

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;
    }
明月夜 2024-12-08 13:54:37

是的,有一个方法,但它有点混乱,因为它基于 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)”是要更改的关键常量,您想要更改该方法将填充的孔的大小。

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

原始

原始图像

结果

The Resultant Image

这种方法的问题是它的内存消耗极大,并且它在一台机器上耗尽了 6GB 的内存。 200x200 图像,当我尝试 200x300 时,它耗尽了我所有 8GB 的​​ RAM,让一切都崩溃了。除非你的大部分图像是白色的,并且你想填充微小的间隙,或者你可以最大限度地减少应用该方法的位置,否则我会避免它。我建议您编写自己的类来检查每个不是 255 的像素并添加它周围的像素数。然后,您可以记录不是 255 的每个像素的位置(在一个简单的列表中),如果您的计数低于阈值,则将图像中的这些位置设置为 255(通过迭代列表)。

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

干杯

克里斯

Yes there is a method but it's a bit messy as its based on cvFloodFill operation. Now all this algorithm is designed to do is fill an area with a colour until it reaches an edge similar to a region growing algorithm. To use this effectively you need to use a little inventive coding but I warn you this code is only to get you started it may require re-factoring to speed things up . As it stands the loop goes through each of your pixels that are less then 255 applies cvFloodFill checks what size the area is and then if it is under a certain area fill it in.

It is important to note that a copy of the image is made of the original image to be supplied to the cvFloodFill operation as a pointer is used. If the direct image is supplied then you will end up with a white image.

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

The "new MCvScalar(0, 0, 0), new MCvScalar(0, 0, 0)," are not really important in this case as you are only filling in results of a binary image. YOu could play around with other settings to see what results you can achieve. "if (comp.area < 10000)" is the key constant to change is you want to change what size hole the method will fill.

These are the results that you can expect:

Original

Original Image

Results

The Resultant Image

The problem with this method is it's extremely memory intensive and it managed to eat up 6GB of ram on a 200x200 image and when I tried 200x300 it ate all 8GB of my RAM and brought everything to a crashing halt. Unless a majority of your image is white and you want to fill in tiny gaps or you can minimise where you apply the method I would avoid it. I would suggest writing you own class to examine each pixel that is not 255 and add the number of pixels surrounding it. You can then record the position of each pixel that was not 255 (in a simple list) and if your count was bellow a threshold set these positions to 255 in your images (by iterating though the list).

I would stick with the Aforge FillHoles class if you do not wish to write your own as it is designed for this purpose.

Cheers

Chris

逐鹿 2024-12-08 13:54:37

您可以使用FillConvexPoly

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

you can use FillConvexPoly

image.FillConvexPoly(externalContours.ToArray(), new Gray(255));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文