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

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

不幸的是,图像可能是:

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

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

【问题讨论】:

标签: c# image hash


【解决方案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 位)

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

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

    1. 将布尔值读入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 像素的图像上它不应该是瓶颈。

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

    代码:

    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】:

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

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

      进行比较的一种方法是,

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

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

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

      【讨论】:

        【解决方案6】:

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

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

        【讨论】:

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