在 C# 中比较两个图像的算法

Posted

技术标签:

【中文标题】在 C# 中比较两个图像的算法【英文标题】:Algorithm to compare two images in C# 【发布时间】:2016-05-11 02:47:30 【问题描述】:

我正在用 C# 编写一个工具来查找重复图像。目前我创建文件的 MD5 校验和并比较它们。

不幸的是,图像可能是:

旋转了 90 度。 具有不同的尺寸(具有相同内容的较小图像)。 具有不同的压缩或文件类型(例如 jpeg 工件,见下文)。

解决这个问题的最佳方法是什么?

【问题讨论】:

使用边缘检测将两个图像缩放到相同大小,然后计算代表差异程度的值(与所有旋转相比)我的帮助 codeproject.com/Articles/374386/Simple-image-comparison-in-NET - 在这里阅读很有趣 AntiHeadshot 确实如此,但前提是图片是使用完全相同的算法和完全相同的设置修改的,否则您最终可能会产生巨大的差异。同样使用质量损失压缩,您最终可能会得到不同的图片,只需将其旋转两次 180 度 :) 更不用说调整大小了。所以过渡需要完美地重复。 可能重复:***.com/q/23931/5420829, ***.com/q/843972/5420829 在这里看看我的答案...***.com/a/25204466/2836621 【参考方案1】:

这里有很多高级的东西,但是很多图像实际上是同一个文件。我会考虑的一个措施是先做愚蠢的部分。创建一个目录列表并按大小顺序对图像文件进行排序。查找具有相同大小文件的所有对并检查每对是否完全匹配。对于每场比赛,你可以删除双打。

现在是有趣的部分..

上述解决方案中没有出现的一件事是使用颜色。您可以使用颜色直方图比较。对于红色、绿色和蓝色通道,计算每种颜色的出现次数并将计数放入树整数 [255] 数组中。然后将每个 RGB 数组标准化为浮点 [0.0 .. 1.0] 值并作为 Vector3(RGB) 距离进行比较。这种方法可以找到图像的旋转和调整大小的版本。颜色域中的匹配并不能保证......但您可以使用它(再次)对文件进行分组,以进一步完善处理并加快处理速度。

【讨论】:

【参考方案2】:
private List<byte> colorList = new List<byte>();
private string hash;

private string GetImageHash(Bitmap bmpSource)

    colorList.Clear();
    int i,j;
    Bitmap bmpMin = new Bitmap(bmpSource, new Size(16, 16)); //create new image with 16x16 pixel
    for ( j = 0 ; j < bmpMin.Height; j++)
    
        for ( i = 0; i < bmpMin.Width; i++)
        
            colorList.Add(bmpMin.GetPixel(i, j).R);
        
    
    SHA1Managed sha = new SHA1Managed();
    byte[] checksum = sha.ComputeHash(colorList.ToArray());
    hash = BitConverter.ToString(checksum).Replace("-", String.Empty);
    sha.Dispose();
    bmpMin.Dispose();
    return hash;

【讨论】:

嗨,Akash,在查看答案时,我对您的答案投了反对票,因为这不是问题的答案。问题是,图像可以缩放(不同大小)甚至旋转 90 度,或者它可能包含来自 .jpg 质量转换的噪声。在这种情况下,无法使用精确的校验和。【参考方案3】:

这是一个简单的方法,使用 256 位图像哈希(MD5 有 128 位)

    将图片大小调整为 16x16 像素

    将颜色减少为 black/white(在此控制台输出中等于 true/false)李>

    将布尔值读入List&lt;bool&gt; - 这是哈希

代码

public static List<bool> GetHash(Bitmap bmpSource)

    List<bool> lResult = new List<bool>();         
    //create new image with 16x16 pixel
    Bitmap bmpMin = new Bitmap(bmpSource, new Size(16, 16));
    for (int j = 0; j < bmpMin.Height; j++)
    
        for (int i = 0; i < bmpMin.Width; i++)
        
            //reduce colors to true / false                
            lResult.Add(bmpMin.GetPixel(i, j).GetBrightness() < 0.5f);
                     
    
    return lResult;

我知道,GetPixel 并没有那么快,但在 16x16 像素的图像上它不应该是瓶颈。

    将此哈希值与其他图像的哈希值进行比较并添加容差。(与其他哈希值不同的像素数)

代码:

List<bool> iHash1 = GetHash(new Bitmap(@"C:\mykoala1.jpg"));
List<bool> iHash2 = GetHash(new Bitmap(@"C:\mykoala2.jpg"));

//determine the number of equal pixel (x of 256)
int equalElements = iHash1.Zip(iHash2, (i, j) => i == j).Count(eq => eq);

所以这段代码能够找到相同的图像:

不同的文件格式(例如 jpg、png、bmp) 旋转(90、180、270),水平/垂直翻转 - 通过改变ij的迭代顺序 不同的维度(需要相同的方面) 不同的压缩方式(在出现 jpeg 伪影等质量损失的情况下需要有容差) - 您可以接受 99% 的平等是相同的图像,而 50% 的平等是不同的图像。 colored 变为 geyscaled,反之亦然(因为亮度与颜色无关)

更新/改进:

使用此方法一段时间后,我注意到可以进行一些改进

replacingGetPixel 以获得更多性能 使用 exeif-thumbnail 而不是读取整个图像以提高性能 而不是将0.5f 设置为在明暗之间有所不同 - 使用所有 256 个像素的不同中值亮度。否则,暗/亮图像被假定为相同,它可以检测亮度发生变化的图像。 如果您需要fast 计算,请使用bool[]List&lt;bool&gt; 如果您需要存储大量哈希并需要节省内存,请使用Bitarray,因为布尔值不会存储在位中,需要byte!

【讨论】:

保留一点颜色(例如灰度)应该会提高精度,但更难实现,无论如何都是好的directlon 这个算法的使用例子你可以在这里找到:github.com/ukushu/ImgComparator。顺便说一句,非常感谢:) @Dror 你可以用这个方法比较n张图片,只需创建所有图片的哈希 @fubo 非常感谢。您能否扩展您改进的第三点:“而不是将 0.5f 设置为明暗之间的差异 - 使用所有 256 像素的不同中值亮度”。我的一些位图包含不同的颜色,所以我需要使用 RGB 值进行比较。实现这一点的最简单方法是什么? @fubo 感谢您的这种方法,以及详细的解释,它节省了我 2 到 3 天的时间。正如预期的那样,矿工更改对我有用【参考方案4】:

您可以查看Algorithm to compare two images 以查看可用的图像比较方法。

除非您想自己重新创建完整的算法,否则您应该尝试使用已经存在的库或它们的至少部分代码(只要它们的许可证适合您)。

对于边缘检测和相关计算机视觉算法的开源 C# 实现,您可以尝试EmguCV,它是 OpenCV 的包装器。

【讨论】:

【参考方案5】:

有趣的问题,考虑到图像的比较并不难,

    这些图像是相同的(第一个不是第二个的一部分,反之亦然) 图像仅旋转 90 度的倍数

进行比较的一种方法是,

    将两个图像的大小调整为最小尺寸 对每个图像应用边缘检测,生成黑白图像(或 0 和 1 的数组) 比较生成的位图(第一个保持不动,将第二个旋转 90 度 3 次)并计算匹配像素的百分比并获得最高值

现在,如果该值在一个合理的值范围内,比如 90%(可能必须通过做一些实验来确定),那么您可以放心地假设两者是相同的,但如果出现以下情况,这将不起作用,

    即使角落有几个像素差异,例如第二张图片是从第一张图片中裁剪出来的 图像的旋转角度不是 90 度的倍数(尽管这种可能性不大)

【讨论】:

【参考方案6】:

将图像重新采样到某个常见分辨率后,您可以使用小波分解并比较此分解的系数,而不是图像本身。仅比较前 N 个系数将使该方法对噪声和其他伪影更加稳健。

有几种可用的小波 C# 实现。一个例子是https://waveletstudio.codeplex.com/

【讨论】:

我刚刚意识到,如果不是所有图像都是方形的,那么重新采样到一些常见的分辨率并不是那么简单,但是您可以重新采样图像以具有相同的高度(保持纵横比)。不同的宽度可能是图像不相同的第一个指示。之后你就可以应用我上面提到的小波了。 有趣的想法。使用小波变换时,只要纵横比相似,非正方形图像就不会造成问题。使用小波分析的另一个优点:第二张图像可以是第一张图像的略微裁剪版本(但不是小细节)。在这种情况下,(FFT 的)小波分析仍应产生更好的距离测量结果,而其他方法则失败。

以上是关于在 C# 中比较两个图像的算法的主要内容,如果未能解决你的问题,请参考以下文章

项目实战 | c#与VisionPro联合编程添加图像处理算法

比较两个频谱图以找到它们匹配算法的偏移量

基础排序算法 c#

图像相似度比较

图像对抗算法(先导篇)

有没有更好的算法来比较每个图像与文件夹中的其他图像?