在 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<bool>
- 这是哈希
代码:
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),水平/垂直翻转 - 通过改变i
和j
的迭代顺序
不同的维度(需要相同的方面)
不同的压缩方式(在出现 jpeg 伪影等质量损失的情况下需要有容差) - 您可以接受 99% 的平等是相同的图像,而 50% 的平等是不同的图像。
colored 变为 geyscaled,反之亦然(因为亮度与颜色无关)
更新/改进:
使用此方法一段时间后,我注意到可以进行一些改进
replacingGetPixel
以获得更多性能
使用 exeif-thumbnail 而不是读取整个图像以提高性能
而不是将0.5f
设置为在明暗之间有所不同 - 使用所有 256 个像素的不同中值亮度。否则,暗/亮图像被假定为相同,它可以检测亮度发生变化的图像。
如果您需要fast 计算,请使用bool[]
或List<bool>
如果您需要存储大量哈希并需要节省内存,请使用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# 中比较两个图像的算法的主要内容,如果未能解决你的问题,请参考以下文章