2013-07-11 47 views
4

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

  1. 创建存储在x,每个像素的y坐标和与宽度,高度,x和y属性一起存储像素的列表的PixelRectangle类的像素类。

  2. 循环遍历每个图像的每个像素,比较每个对应像素的颜色。如果颜色不同,我然后用该像素的x,y坐标创建一个新的像素对象,并将其添加到pixelDifference List。

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

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

我的问题是:我正在做这个正确的方法吗?四叉树会为这个项目保留任何价值吗?如果你可以给我关于这样的事情的基本步骤,我会很感激。提前致谢。

  • Dave。
+0

只是一个tought,但这些图像如何看实际字节?如果他们对一个像素或者甚至一个像素使用一定数量的字节,则可以检查实际文件本身的差异,而不必逐像素地分析图像。再次,我甚至不确定是否是这种情况,只是一个想法。 – aevitas

+0

当你说差异时,你的意思是个别像素是不同的? 'LockBits'可以帮助我看到 – Sayse

+0

为什么不显示匹配灰度和红色差异? – Paparazzi

回答

0

你几乎用正确的方式去做。第3步不应该导致StackOverflow异常,如果它正确实施,所以我会仔细看看该方法。

最可能发生的事情是您对PixelDifference的每个成员的递归检查无限运行。确保你跟踪哪些像素已被检查。一旦检查了像素,在检查邻近像素时就不再需要考虑了。在检查任何邻近像素之前,确保它没有被检查过。

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

+0

我会说我制作像素列表太慢。在图像循环中保留并更新4个变量:x max,y max,x min,y min。这是你的盒子坐标。 –

2

看起来像你想要实现斑点检测。我的建议是不要重新发明轮子,只使用openCVSharp或emgu来做到这一点。谷歌“污点检测” & OpenCV的

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

首先,让我们澄清你想要做什么。实际上是两个单独的东西:

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

  2. 画一个框围绕“区域”通过1测量的是“不同”这里的问题是什么是'区域',什么是'不同的'。

我的建议为每个步骤:(我的假设是两个图像的灰度若否,计算的颜色之和为每个像素获得的灰度值)

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)

不知道你的意思面积这里什么。取决于你的意思的几种解决方案。从最简单到最难。

一)颜色,假定你只有差异(不可能一个面积)计算所有255个像素的边框在输出图像的每个像素的差异红色在输出

B)。这可以在所有255个像素上使用x和y位置的简单最大/最小值来完成。单通过图像,应该非常快。 c)如果你有很多不同的区域发生变化 - 计算“连接的组件”。这是相互连接的像素的集合。当然这只适用于二进制图像(即开或关,或在我们的情况下为0和255)。你可以在C#中实现这一点,我已经做到了这一点。但我不会在这里为你做这件事。这有点牵扯。算法在那里。再次opencv或谷歌connected components

一旦你有一个CC的列表画每个周围的框。完成。

0

找到两幅图像的差异有一个更简单的方法。

所以,如果你有两个图像

Image<Gray, Byte> A; 
Image<Gray, Byte> B; 

您可以通过

A - B 

当然很快得到他们的差异,图像不存储负值,所以在情况下得到的差异,其中像素图像B大于图像A

B - A 

将它们组合在一起

(A - B) + (B - A) 

这是好的,但我们可以做得更好。

这可以使用傅里叶变换进行评估。

CvInvoke.cvDFT(A.Convert<Gray, Single>().Ptr, DFTA.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_FORWARD, -1); 
CvInvoke.cvDFT(B.Convert<Gray, Single>().Ptr, DFTB.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_FORWARD, -1); 

CvInvoke.cvDFT((DFTB - DFTA).Convert<Gray, Single>().Ptr, AB.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_INVERSE, -1); 
CvInvoke.cvDFT((DFTA - DFTB).Ptr, BA.Ptr, Emgu.CV.CvEnum.CV_DXT.CV_DXT_INVERSE, -1); 

我发现这个方法的结果好多了。 您可以制作一个二进制图像,即:阈值的图像,所以没有变化的像素有0,而有变化的像素存储255.

现在至于第二部分的问题,我想有一个简单的粗略解决方案:

将图像分割成矩形区域。也许没有必要去使用四叉树。比如说,一个8x8的网格......(对于不同的结果,您可以尝试不同的网格大小)。

然后使用这些区域内的convex hull函数。这些凸包可以通过找到它们顶点的最小和最大x坐标来变成矩形。

应该是快速和简单

+0

定位在区域边界上的矩形将被拆分。因此,如果需要,可以在最后添加一个额外的步骤来组合这些矩形。 – sav