如何扫描两个图像的区别是什么? [英] How to scan two images for differences?

查看:300
本文介绍了如何扫描两个图像的区别是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图扫描2幅图像(32bppArgb格式),确定何时是有区别的,并存储差块的边界在矩形列表。



假设这些是图片:



第二:



我想获得不同的矩形范围(在我们的情况下,打开目录窗口)。



这是我做了什么:

 私人不安全的名单,LT;矩形> CodeImage(BMP位图,位图BMP2)
{

名单,LT;矩形> REC =新的List<矩形>();
bmData = bmp.LockBits(新System.Drawing.Rectangle(0,0,1920,1080),System.Drawing.Imaging.ImageLockMode.ReadOnly,bmp.PixelFormat);
bmData2 = bmp2.LockBits(新System.Drawing.Rectangle(0,0,1920,1080),System.Drawing.Imaging.ImageLockMode.ReadOnly,bmp2.PixelFormat);

IntPtr的SCAN0 = bmData.Scan0;
IntPtr的scan02 = bmData2.Scan0;
INT跨距= bmData.Stride;
INT stride2 = bmData2.Stride;
INT nWidth = bmp.Width;
INT nHeight参数= bmp.Height;
INT其minX = int.MaxValue; ;
INT MINY = int.MaxValue;
INT MAXX = 0;
布尔发现= FALSE;


为(INT Y = 0; Y< nHeight参数; Y ++)
{
字节* P =(BYTE *)scan0.ToPointer();
P + = Y *的步伐;
字节* P2 =(字节*)scan02.ToPointer();
P2 + = Y * stride2;
表示(中间体X = 0; X&下; nWidth; X ++)
{

如果(P [0] = P2 [0] || P [1]! = P2 [1] || p [2]!= P2 [2] || p [3]!= P2 [3])//发现差异-开始存储的位置。
{

发现= TRUE;
如果(X<其minX)
其minX = X;
如果(X> MAXX)
MAXX = X;
如果(Y< MINY)
MINY = Y;

}

,否则
{

如果(找到)
{

INT高度= getBlockHeight(步幅,SCAN0,MAXX,MINY,scan02,stride2);
=发现虚假;
矩形临时=新的Rectangle(疯丫头,MINY,MAXX - 疯丫头,高度);
rec.Add(临时);
// X + =其minX;
Y + =高度;
=其minX int.MaxValue;
MINY = int.MaxValue;
MAXX = 0;

}
}
P + = 4;
P2 + = 4;
}
}

返回REC;
}
公共不安全INT getBlockHeight(INT大步,IntPtr的扫描,INT X,INT Y1,IntPtr的scan02,INT stride2)//一个函数来获取现有块的高度。
{
INT高度= 0; ;
为(INT Y = Y1; Y< 1080; Y ++)//只在我们的情况下,其1080的高度例如─。
{
字节* p值=(字节*)scan.ToPointer();
P + =(Y *步幅)+(X * 4); //设置指针指向一个特定的潜力点。
字节* P2 =(字节*)scan02.ToPointer();
P2 + =(Y * stride2)+(X * 4); //设置指针到特定的电势点。
如果(P [0]!= P2 [0] || P [1]!= P2 [1] || P [2]!= P2 [2] || P [3]!= P2 [ 3])//依然改变高度的增加** **Ÿ块。

高度++;
}

}
返回高度;
}

这其实是我如何调用该方法:

 位图= Image.FromFile(@C:\Users\itapi\Desktop\1.png)作为位图; //生成一个32bppRgba位图; 
位图B = Image.FromFile(@C:\Users\itapi\Desktop\2.png)作为位图; //

名单,LT;矩形> L1 = CodeImage(A,B);
INT I = 0;
的foreach(矩形REC在L1)
{
I ++;
位图TMP = b.Clone(REC,a.PixelFormat);
tmp.Save(i.ToString()+巴纽);
}



但我没有得到确切的矩形。只有我越来越一半,有时甚至更糟。我认为有些事情在代码的逻辑是错误的。



代码@nico

 私人不安全的名单,LT;矩形> CodeImage(BMP位图,位图BMP2)
{


名单,LT;矩形> REC =新的List<矩形>();
变种bmData1 = bmp.LockBits(新System.Drawing.Rectangle(0,0,bmp.Width,bmp.Height),System.Drawing.Imaging.ImageLockMode.ReadOnly,bmp.PixelFormat);
变种bmData2 = bmp2.LockBits(新System.Drawing.Rectangle(0,0,bmp.Width,bmp.Height),System.Drawing.Imaging.ImageLockMode.ReadOnly,bmp2.PixelFormat);

INT bytesPerPixel = 3;

IntPtr的scan01 = bmData1.Scan0;
IntPtr的scan02 = bmData2.Scan0;
INT stride1 = bmData1.Stride;
INT stride2 = bmData2.Stride;
INT nWidth = bmp.Width;
INT nHeight参数= bmp.Height;

布尔[] =走访新布尔[nWidth * nHeight参数]

字节* BASE1 =(BYTE *)scan01.ToPointer();
字节* BASE2 =(BYTE *)scan02.ToPointer();

为(INT Y = 0; Y< nHeight参数; Y + = 5)
{
字节* P1 = BASE1;
字节* P2 = BASE2;

表示(中间体X = 0; X&下; nWidth; X + = 5)!
{
如果(ArePixelsEqual(P1,P2,bytesPerPixel)及&放大器;!(参观[X + nWidth * Y))
{
//填写不同的区域
INT其minX = X;
INT MAXX = X;
INT MINY = Y;
INT MAXY = Y;

变种PT =新的点(X,Y);

堆栈<点和GT; toBeProcessed =新的堆栈<点和GT;();
访问[X + nWidth * Y] =真;
toBeProcessed.Push(PT);
而(toBeProcessed.Count大于0)
{
变种过程= toBeProcessed.Pop();
VAR ptr1的=(BYTE *)scan01.ToPointer()+ process.Y * stride1 + process.X * bytesPerPixel;
VAR PTR2 =(BYTE *)scan02.ToPointer()+ process.Y * stride2 + process.X * bytesPerPixel;
//检查像素平等
如果(ArePixelsEqual(ptr1的,PTR2,bytesPerPixel))
继续;

//这个像素是不同的
//更新矩形
如果(process.X<其minX)=其minX process.X;
如果(process.X> MAXX)MAXX = process.X;
如果(process.Y< MINY)MINY = process.Y;
如果(process.Y> MAXY)MAXY = process.Y;

点n; INT IDX;
//放入堆栈
如果邻居(process.X - 1> = 0)
{
N =新的点(process.X - 1,process.Y); IDX = n.x的+ nWidth * N.Y;
如果{走访[IDX] =真(参观[IDX]!); toBeProcessed.Push(N); }
}

如果(process.X±1℃; nWidth)
{
N =新点(process.X + 1,process.Y); IDX = n.x的+ nWidth * N.Y;
如果{走访[IDX] =真(参观[IDX]!); toBeProcessed.Push(N); }
}

如果(process.Y - 1> = 0)
{
N =新的点(process.X,process.Y - 1); IDX = n.x的+ nWidth * N.Y;
如果{走访[IDX] =真(参观[IDX]!); toBeProcessed.Push(N); }
}

如果(process.Y±1℃; nHeight参数)
{
N =新的点(process.X,process.Y + 1); IDX = n.x的+ nWidth * N.Y;
如果{走访[IDX] =真(参观[IDX]!); toBeProcessed.Push(N); }
}
}

如果(((MAXX - 其minX + 1)→5)及((MAXY - MINY + 1)→5))
rec.Add(新的Rectangle(疯丫头,MINY,MAXX - 其minX + 1,MAXY - MINY + 1));
}

P1 + = 5 * bytesPerPixel;
P2 + = 5 * bytesPerPixel;
}

BASE1 + = 5 * stride1;
BASE2 + = 5 * stride2;
}


bmp.UnlockBits(bmData1);
bmp2.UnlockBits(bmData2);

返回REC;
}


解决方案

我看到一对夫妇的问题与您的代码。如果我理解正确的话,你




  1. 找到一个像素这两个图像之间的不同。

  2. 那么你从那里继续扫描到右侧,直到找到其中两个图像再次相同的位置。

  3. 那么你从最后一个与众不同的像素底部扫描,直到找到其中两个图像是相同的再次的位置。

  4. 则存储矩形,并在其下方



下一行的



我说得对不对,到目前为止



两个明显的事情可以去错在这里?




  • 如果两个矩形具有重叠的Y范围,你就麻烦了:你会发现第一个矩形细,然后跳到底部Y坐标,忽略离开所有的像素或对你刚发现的矩形。

  • 即使只有一个矩形,你假设的每个的上矩形的边框像素是不同的,而所有的其它像素是相同的。如果这个假设是无效的,你会停下来太早了搜索和发现只有矩形的部分。



如果您的图像来从扫描仪或数码相机,或者如果它们包含有损压缩(JPEG)文物,第二个假设几乎肯定是错误的。为了说明这一点,这是我得到的时候我庆祝每一个相同的像素您链接黑两JPG图像,和每一个不同的像素白色:





您看到的是的的矩形。相反,很多在你身边正在寻找的矩形像素是不同的:





这是因为JPEG压缩文物。但是,即使你使用无损源图像,在边界的像素可能不会形成,因为抗锯齿还是因为背景只是发生的有在该地区类似的颜色。

$ B完美的矩形,
$ b

您可以尝试来改善你的算法,但如果你看那个边界,你会发现的所有的各种丑恶的反以任何几何假设你会成功。



这可能会是更好地执行这一正确的方式。含义:




  • 执行一项的洪水填充算法(例如,将它们设置为相同或通过存储在一个单独的面具标志),然后递归检查,如果4相邻像素。

  • 或实施连通区域标记算法,这标志着每个不同的像素临时整数标签,采用巧妙的数据结构来跟踪临时标签连接。如果你只在一个边界框感兴趣的话,你甚至不必合并临时标签,只是合并相邻的标记区的边框。



连通区域标记是一般的快一点,但有点棘手得到比洪水填充的权利。



最后一个忠告:我会重新考虑你的无第三方库的政策,如果我是你。即使您的最终产品将不包含任何第三方库,开发可能是由快了很多,如果您使用充分证明,从库中经过严格测试的,有用的积木,然后取而代之的是一个个用自己的代码。 (谁知道,你甚至可以找到一个合适的许可证,这是如此比你自己的代码快得多,你会坚持下去,最终...一个开源库)






地址:在你要重新考虑你的无库的位置的情况下:这里有一个快速和简单的实现使用AForge(其中有一个更比emgucv许可库):

 私有静态无效ProcessImages()
{
(*载入图像* )
VAR IMG1 = AForge.Imaging.Image.FromFile(@compare1.jpg);
VAR IMG2 = AForge.Imaging.Image.FromFile(@compare2.jpg);

(*计算绝对差值*)
变量差值=新AForge.Imaging.Filters.ThresholdedDifference(15)
{窗格在overlayImage = IMG1}
。适用(IMG2 );

(*创建和初始化BLOB柜台*)
变种BC =新AForge.Imaging.BlobCounter();
bc.FilterBlobs = TRUE;
bc.MinWidth = 5;
bc.MinHeight = 5;

(*发现斑点*)
bc.ProcessImage(差);

(*得出结果*)
的BitmapData数据= img2.LockBits(
新的Rectangle(0,0,img2.Width,img2.Height),
ImageLockMode .ReadWrite,img2.PixelFormat);

的foreach(在bc.GetObjectsRectangles VAR RC())
AForge.Imaging.Drawing.FillRectangle(数据,RC,Color.FromArgb(128,Color.Red));

img2.UnlockBits(数据);
img2.Save(@compareResult.jpg);
}



实际相差+斑点检测部(不加载和结果显示)约需持续时间43ms,对于第二次运行(这第一次需要更长的时间,当然,由于JITting,高速缓存等)



结果(矩形较大由于JPEG工件)




I'm trying to scan 2 images (32bppArgb format), identify when there is a difference and store the difference block's bounds in a list of rectangles.

Suppose these are the images:

second:

I want to get the different rectangle bounds (the opened directory window in our case).

This is what I've done:

private unsafe List<Rectangle> CodeImage(Bitmap bmp,Bitmap bmp2)
    {

        List<Rectangle> rec = new List<Rectangle>();
        bmData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, 1920, 1080), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
        bmData2 = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, 1920, 1080), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);

        IntPtr scan0 = bmData.Scan0;
        IntPtr scan02 = bmData2.Scan0;
        int stride = bmData.Stride;
        int stride2 = bmData2.Stride;
        int nWidth = bmp.Width;
        int nHeight = bmp.Height;
        int minX = int.MaxValue; ;
        int minY = int.MaxValue;
        int maxX = 0;
        bool found = false;


        for (int y = 0; y < nHeight; y++)
        {
            byte* p = (byte*)scan0.ToPointer();
            p += y * stride;
            byte* p2 = (byte*)scan02.ToPointer();
            p2 += y * stride2;
            for (int x = 0; x < nWidth; x++)
            {

                if (p[0] != p2[0] || p[1] != p2[1] || p[2] != p2[2] || p[3] != p2[3])//found differences-began to store positions.
                {

                    found = true;
                    if (x < minX)
                        minX = x;
                    if (x > maxX)
                        maxX = x;
                    if (y < minY)
                        minY = y;

                }

                else
                {

                    if (found)
                    {

                            int height = getBlockHeight(stride, scan0, maxX, minY,scan02,stride2);
                            found = false;
                            Rectangle temp = new Rectangle(minX, minY, maxX - minX, height);
                            rec.Add(temp);
                            //x += minX;
                            y += height;
                            minX = int.MaxValue;
                            minY = int.MaxValue;
                            maxX = 0;

                    }
                }
                p += 4;
                p2 += 4;
            }
        }

        return rec;
    }
    public unsafe int getBlockHeight(int stride, IntPtr scan, int x, int y1,IntPtr scan02,int stride2)//a function to get  an existing block height.
    {
        int height = 0; ;
        for (int y = y1; y < 1080; y++)//only for example- in our case its 1080 height.
        {
            byte* p = (byte*)scan.ToPointer();
            p += (y * stride) + (x * 4);//set the pointer to a specific potential point. 
             byte* p2 = (byte*)scan02.ToPointer();
            p2 += (y * stride2) + (x * 4); //set the pointer to a specific potential point. 
            if (p[0] != p2[0] || p[1] != p2[1] || p[2] != p2[2] || p[3] != p2[3])//still change on the height in the increasing **y** of the block.

                height++;
            }

        }
        return height;
    }

This is actually how I call the method:

    Bitmap a = Image.FromFile(@"C:\Users\itapi\Desktop\1.png") as Bitmap;//generates a 32bppRgba bitmap;
        Bitmap b = Image.FromFile(@"C:\Users\itapi\Desktop\2.png") as Bitmap;//

        List<Rectangle> l1 = CodeImage(a, b);
        int i = 0;
        foreach (Rectangle rec in l1)
        {
            i++;
            Bitmap tmp = b.Clone(rec, a.PixelFormat);
            tmp.Save(i.ToString() + ".png");
        }

But I'm not getting the exact rectangle.. I'm getting only half of that and sometimes even worse. I think something in the code's logic is wrong.

Code for @nico

    private unsafe List<Rectangle> CodeImage(Bitmap bmp, Bitmap bmp2)
    {


        List<Rectangle> rec = new List<Rectangle>();
        var bmData1 = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
        var bmData2 = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);

        int bytesPerPixel = 3;

        IntPtr scan01 = bmData1.Scan0;
        IntPtr scan02 = bmData2.Scan0;
        int stride1 = bmData1.Stride;
        int stride2 = bmData2.Stride;
        int nWidth = bmp.Width;
        int nHeight = bmp.Height;

        bool[] visited = new bool[nWidth * nHeight];

        byte* base1 = (byte*)scan01.ToPointer();
        byte* base2 = (byte*)scan02.ToPointer();

        for (int y = 0; y < nHeight; y+=5)
        {
            byte* p1 = base1;
            byte* p2 = base2;

            for (int x = 0; x < nWidth; x+=5)
            {
                if (!ArePixelsEqual(p1, p2, bytesPerPixel) && !(visited[x + nWidth * y]))
                {
                    // fill the different area
                    int minX = x;
                    int maxX = x;
                    int minY = y;
                    int maxY = y;

                    var pt = new Point(x, y);

                    Stack<Point> toBeProcessed = new Stack<Point>();
                    visited[x + nWidth * y] = true;
                    toBeProcessed.Push(pt);
                    while (toBeProcessed.Count > 0)
                    {
                        var process = toBeProcessed.Pop();
                        var ptr1 = (byte*)scan01.ToPointer() + process.Y * stride1 + process.X * bytesPerPixel;
                        var ptr2 = (byte*)scan02.ToPointer() + process.Y * stride2 + process.X * bytesPerPixel;
                        //Check pixel equality
                        if (ArePixelsEqual(ptr1, ptr2, bytesPerPixel))
                            continue;

                        //This pixel is different
                        //Update the rectangle
                        if (process.X < minX) minX = process.X;
                        if (process.X > maxX) maxX = process.X;
                        if (process.Y < minY) minY = process.Y;
                        if (process.Y > maxY) maxY = process.Y;

                        Point n; int idx;
                        //Put neighbors in stack
                        if (process.X - 1 >= 0)
                        {
                            n = new Point(process.X - 1, process.Y); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }

                        if (process.X + 1 < nWidth)
                        {
                            n = new Point(process.X + 1, process.Y); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }

                        if (process.Y - 1 >= 0)
                        {
                            n = new Point(process.X, process.Y - 1); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }

                        if (process.Y + 1 < nHeight)
                        {
                            n = new Point(process.X, process.Y + 1); idx = n.X + nWidth * n.Y;
                            if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
                        }
                    }

                    if (((maxX - minX + 1 )>5) & ((maxY - minY + 1)>5))
                    rec.Add(new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1));
                }

                p1 += 5 * bytesPerPixel;
                p2 += 5 * bytesPerPixel;
            }

            base1 += 5 * stride1;
            base2 += 5 * stride2;
        }


        bmp.UnlockBits(bmData1);
        bmp2.UnlockBits(bmData2);

        return rec;
    }

解决方案

I see a couple of problems with your code. If I understand it correctly, you

  1. find a pixel that's different between the two images.
  2. then you continue to scan from there to the right, until you find a position where both images are identical again.
  3. then you scan from the last "different" pixel to the bottom, until you find a position where both images are identical again.
  4. then you store that rectangle and start at the next line below it

Am I right so far?

Two obvious things can go wrong here:

  • If two rectangles have overlapping y-ranges, you're in trouble: You'll find the first rectangle fine, then skip to the bottom Y-coordinate, ignoring all the pixels left or right of the rectangle you just found.
  • Even if there is only one rectangle, you assume that every pixel on the rectangle's border is different, and all the other pixels are identical. If that assumption isn't valid, you'll stop searching too early, and only find parts of rectangles.

If your images come from a scanner or digital camera, or if they contain lossy compression (jpeg) artifacts, the second assumption will almost certainly be wrong. To illustrate this, here's what I get when I mark every identical pixel the two jpg images you linked black, and every different pixel white:

What you see is not a rectangle. Instead, a lot of pixels around the rectangles you're looking for are different:

That's because of jpeg compression artifacts. But even if you used lossless source images, pixels at the borders might not form perfect rectangles, because of antialiasing or because the background just happens to have a similar color in that region.

You could try to improve your algorithm, but if you look at that border, you will find all kinds of ugly counterexamples to any geometric assumptions you'll make.

It would probably be better to implement this "the right way". Meaning:

  • Either implement a flood fill algorithm that erases different pixels (e.g. by setting them to identical or by storing a flag in a separate mask), then recursively checks if the 4 neighbor pixels.
  • Or implement a connected component labeling algorithm, that marks each different pixel with a temporary integer label, using clever data structures to keep track which temporary labels are connected. If you're only interested in a bounding box, you don't even have to merge the temporary labels, just merge the bounding boxes of adjacent labeled areas.

Connected component labeling is in general a bit faster, but is a bit trickier to get right than flood fill.

One last advice: I would rethink your "no 3rd party libraries" policy if I were you. Even if your final product will contain no 3rd party libraries, development might by a lot faster if you used well-documented, well-tested, useful building blocks from a library, then replaced them one by one with your own code. (And who knows, you might even find an open source library with a suitable license that's so much faster than your own code that you'll stick with it in the end...)


ADD: In case you want to rethink your "no libraries" position: Here's a quick and simple implementation using AForge (which has a more permissive library than emgucv):

private static void ProcessImages()
{
    (* load images *)
    var img1 = AForge.Imaging.Image.FromFile(@"compare1.jpg");
    var img2 = AForge.Imaging.Image.FromFile(@"compare2.jpg");

    (* calculate absolute difference *)
    var difference = new AForge.Imaging.Filters.ThresholdedDifference(15)
        {OverlayImage = img1}
        .Apply(img2);

    (* create and initialize the blob counter *)
    var bc = new AForge.Imaging.BlobCounter();
    bc.FilterBlobs = true;
    bc.MinWidth = 5;
    bc.MinHeight = 5;

    (* find blobs *)
    bc.ProcessImage(difference);

    (* draw result *)
    BitmapData data = img2.LockBits(
       new Rectangle(0, 0, img2.Width, img2.Height),
          ImageLockMode.ReadWrite, img2.PixelFormat);

    foreach (var rc in bc.GetObjectsRectangles())
        AForge.Imaging.Drawing.FillRectangle(data, rc, Color.FromArgb(128,Color.Red));

    img2.UnlockBits(data);
    img2.Save(@"compareResult.jpg");
}

The actual difference + blob detection part (without loading and result display) takes about 43ms, for the second run (this first time takes longer of course, due to JITting, cache, etc.)

Result (the rectangle is larger due to jpeg artifacts):

这篇关于如何扫描两个图像的区别是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆