比较图像和标记差异c#

时间:2013-07-11 21:10:26

标签: c# .net image comparison pixel

我目前正在开展一个项目,在这个项目中,我需要编写软件来比较由同一区域组成的两个图像,并围绕差异绘制一个框。我在几个小时内用c#.net编写了这个程序,但很快意识到它的运行成本非常高。以下是我实施的步骤。

  1. 创建了一个Pixel类,用于存储每个像素的x,y坐标,以及一个PixelRectangle类,用于存储像素列表以及width,height,x和y属性。

  2. 循环浏览每个图像的每个像素,比较每个相应像素的颜色。如果颜色不同,我会创建一个新的像素对象,其中包含该像素的x,y坐标,并将其添加到pixelDifference列表中。

  3. 接下来,我编写了一个方法,递归检查pixelDifference列表中的每个像素,以创建仅包含彼此紧邻的像素的PixelRectangle对象。 (很确定这个坏孩子造成了大部分破坏,因为它给了我一个堆栈溢出错误。)

  4. 然后,我根据存储在PixelRectangle对象列表中的像素计算出矩形的x,y坐标和尺寸,并在原始图像上绘制一个矩形,以显示差异所在。< / p>

  5. 我的问题是:我是否以正确的方式解决这个问题?四叉树是否对此项目有任何价值?如果你能给我一些关于如何通常实现这样的基本步骤,我将不胜感激。提前谢谢。

    • 戴夫。

2 个答案:

答案 0 :(得分:2)

看起来你想要实现blob检测。我的建议不是重新发明轮子,只需使用openCVSharp或emgu来做到这一点。 google'blob detection'&amp; OpenCV的

如果你想在这里自己做2美分我的价值:

首先,让我们澄清你想做什么。真是两件事:

  1. 计算两幅图像之间的差异(我假设它们是 相同的尺寸)

  2. 在“区域”周围绘制一个“不同”的框,用1表示。这里的问题是什么是“区域”,什么是“不同”。

  3. 我对每一步的建议:

    (我的假设是两个图像都是灰度。如果没有,计算每个像素的颜色总和得到灰度值)

    1)循环浏览两个图像中的所有像素并减去它们。设置绝对差值的阈值,以确定它们的差异是否足以表示场景中的实际变化(与传感器噪声等相反,如果图像来自相机)。然后将结果存储在第三个图像中。 0没有区别。 255表示差异。如果做得好,这应该真的很快。但是,在C#中,你必须使用指针来获得不错的性能。这里有一个如何执行此操作的示例(注意:代码未经过测试!!):

      /// <summary>
        /// computes difference between two images and stores result in a third image
        /// input images must be of same dimension and colour depth
        /// </summary>
        /// <param name="imageA">first image</param>
        /// <param name="imageB">second image</param>
        /// <param name="imageDiff">output 0 if same, 255 if different</param>
        /// <param name="width">width of images</param>
        /// <param name="height">height of images</param>
        /// <param name="channels">number of colour channels for the input images</param>
        unsafe void ComputeDiffernece(byte[] imageA, byte[] imageB, byte[] imageDiff, int width, int height, int channels, int threshold)
        {
            int ch = channels;
    
            fixed (byte* piA = imageB, piB = imageB, piD = imageDiff)
            {
    
                if (ch > 1) // this a colour image (assuming for RGB ch == 3 and RGBA  == 4)
                {
                    for (int r = 0; r < height; r++)
                    {
                        byte* pA = piA + r * width * ch;
                        byte* pB = piB + r * width * ch;
                        byte* pD = piD + r * width; //this has only one channels!
    
                        for (int c = 0; c < width; c++)
                        {
                            //assuming three colour channels. if channels is larger ignore extra (as it's likely alpha)
                            int LA = pA[c * ch] + pA[c * ch + 1] + pA[c * ch + 2];
                            int LB = pB[c * ch] + pB[c * ch + 1] + pB[c * ch + 2];
    
                            if (Math.Abs(LA - LB) > threshold)
                            {
                                pD[c] = 255;
                            }
                            else
                            {
                                pD[c] = 0;
                            }
    
                        }
                    }
                }
                else //single grey scale channels
                {
                    for (int r = 0; r < height; r++)
                    {
                        byte* pA = piA + r * width;
                        byte* pB = piB + r * width;
                        byte* pD = piD + r * width; //this has only one channels!
    
                        for (int c = 0; c < width; c++)
                        {
                            if (Math.Abs(pA[c] - pB[c]) > threshold)
                            {
                                pD[c] = 255;
                            }
                            else
                            {
                                pD[c] = 0;
                            }
                        }
                    }
                }
            }
        }
    

    2)

    不确定你所在地区的意思。几种解决方案取决于你的意思。从最简单到最难。

    a)在输出中为每个差异像素红色着色

    b)假设您只有一个差异区域(不太可能)计算输出图像中所有255个像素的边界框。这可以使用所有255个像素上的x和y位置的简单max / min来完成。单次通过图像应该非常快。

    c)如果你有很多不同的领域发生变化 - 计算“连通组件”。这是一组彼此连接的像素。当然,这仅适用于二进制图像(即打开或关闭,或者在我们的情况下为0和255)。你可以在c#中实现这个,我以前做过这个。但我不会在这里为你做这件事。它有点牵扯。算法在那里。再次opencv或google connected components

    一旦你有一个CC的列表,每个人都会画一个方框。完成。

答案 1 :(得分:0)

你正在以正确的方式解决这个问题。如果正确实现,则第3步不应该导致StackOverflow异常,因此我将仔细研究该方法。

最可能发生的事情是您对PixelDifference的每个成员的递归检查无限运行。确保跟踪已检查的像素。检查Pixel后,在检查相邻像素时不再需要考虑它。在检查任何相邻像素之前,请确保它本身尚未检查过。

作为跟踪已检查哪些像素的替代方法,您可以在检查后从PixelDifference中删除项目。当然,这可能需要更改您实现算法的方式,因为在检查它时从List中删除元素会带来一系列全新的问题。