我可以比较两个相等大小的位图以确定它们是否相同的最快方法是什么?

Eri*_*bes 38 c# comparison bitmap

我正在尝试编写一个函数来确定两个相等大小的位图是否相同.我现在的功能只是在每个位图中一次比较一个像素,在第一个不相等的像素处返回false.

虽然这可行,并且适用于小位图,但在生产中我将在紧密循环和更大的图像中使用它,所以我需要一种更好的方法.有没有人有任何建议?

我使用的语言就是C# - 是的,我已经在使用.LockBits方法了.=)

编辑:我编写了一些给出的建议的实现,这里是基准测试.设置:两个相同(最坏情况)的位图,大小为100x100,每个迭代次数为10,000次.结果如下:

CompareByInts (Marc Gravell) :   1107ms
CompareByMD5  (Skilldrick)   :   4222ms
CompareByMask (GrayWizardX)  :    949ms
Run Code Online (Sandbox Code Playgroud)

在CompareByInts和CompareByMask中,我使用指针直接访问内存; 在MD5方法中,我使用Marshal.Copy来检索字节数组并将其作为参数传递给MD5.ComputeHash.CompareByMask只是稍快一点​​,但考虑到上下文,我认为任何改进都是有用的.

感谢大家.=)

编辑2:忘了打开优化 - 这样做可以让GrayWizardX的答案更加强大:

CompareByInts   (Marc Gravell) :    944ms
CompareByMD5    (Skilldrick)   :   4275ms
CompareByMask   (GrayWizardX)  :    630ms
CompareByMemCmp (Erik)         :    105ms
Run Code Online (Sandbox Code Playgroud)

有趣的是,MD5方法根本没有改进.

编辑3:发布了我的答案(MemCmp),它将其他方法从水中吹走.OO

Eri*_*bes 34

编辑8-31-12:根据Joey的评论,请注意您比较的位图格式.它们可能在步幅上包含填充,使得位图不相等,尽管它们在像素方面是等效的.有关详细信息,请参阅此问题.


阅读有关比较字节数组的问题的答案已经产生了一个更快的方法:使用P/Invoke和msvcrt中的memcmp API调用.这是代码:

[DllImport("msvcrt.dll")]
private static extern int memcmp(IntPtr b1, IntPtr b2, long count);

public static bool CompareMemCmp(Bitmap b1, Bitmap b2)
{
    if ((b1 == null) != (b2 == null)) return false;
    if (b1.Size != b2.Size) return false;

    var bd1 = b1.LockBits(new Rectangle(new Point(0, 0), b1.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    var bd2 = b2.LockBits(new Rectangle(new Point(0, 0), b2.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

    try
    {
        IntPtr bd1scan0 = bd1.Scan0;
        IntPtr bd2scan0 = bd2.Scan0;

        int stride = bd1.Stride;
        int len = stride * b1.Height;

        return memcmp(bd1scan0, bd2scan0, len) == 0;
    }
    finally
    {
        b1.UnlockBits(bd1);
        b2.UnlockBits(bd2);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 旁注:[这不一定有效,因为每个步幅可能包含填充](http://stackoverflow.com/q/12205247/73070). (7认同)
  • 请注意,`memcmp` 的最后一个参数是一个 `IntPtr`,而不是一个 `long`,因为如果程序以 32 位运行,则它是 32 位,在 64 位时它是 64 位。显然,你必须`return memcmp(bd1scan0, ​​bd2scan0, ​​(IntPtr)len) == 0;` (2认同)

Gra*_*rdx 9

如果您正在尝试确定它们是否100%相等,则可以反转一个并将其添加到另一个,如果它们为零则相同.使用不安全的代码扩展它,一次取64位作为一个长的数字,并以这种方式进行数学计算,任何差异都可能导致立即失败.

如果图像不是100%相同(比较png到jpeg),或者如果你不是在寻找100%匹配,那么你还有更多的工作要做.

祝好运.


Mar*_*ell 8

好吧,你正在使用.LockBits,所以大概你使用的是不安全的代码.而不是将每一行origin(Scan0 + y * Stride)视为a byte*,考虑将其视为int*; int算术很快,你只需做1/4的工作.而对于ARGB中的图像,您可能仍在以像素为单位进行讨论,使数学变得简单.


Ski*_*ick 6

你能拿出每个哈希并进行比较吗?这有点概率,但实际上并非如此.

感谢Ram,这是这种技术的示例实现.

  • 它不会快速失败,但如果你必须将1个图像与多个候选者进行比较,它确实会更好... (4认同)