找到高质量和低质量,像素化图像之间的匹配 - 是否可能?怎么样?

时间:2012-07-26 14:03:49

标签: c# image algorithm image-processing pattern-matching

我有问题。我的公司给了我一项非常无聊的任务。我们有两个对话框数据库。其中一个数据库包含可怕质量的图像,另一个非常高质量。

不幸的是,质量可怕的对话包含了对其他信息的重要映射。

我的任务是,手动完成所有不良图像并将它们匹配到好的图像。

是否有可能在任何程度上自动化此过程?以下是两个对话框(从Google图像中随机抽取)的示例:

Good quality image

Bad Quality image

所以我现在正在尝试用C#编写一个程序来从数据库中提取这些照片,循环浏览它们,找到具有常见形状的照片,然后返回它们的ID。我最好的选择是什么?

18 个答案:

答案 0 :(得分:29)

我真的认为没有理由为此使用任何外部库,我已经做了很多次这样的事情并且以下算法运行得很好。我假设如果您将两个图像进行比较,它们具有相同的尺寸,但如果不这样做,您可以调整一个尺寸。

badness := 0.0
For x, y over the entire image:
  r, g, b := color at x,y in image 1
  R, G, B := color at x,y in image 2
  badness += (r-R)*(r-R) + (g-G)*(g-G) + (b-B)*(b-B)
badness /= (image width) * (image height)

现在,您在两张图片之间存在标准化的错误值,错误越低,图像匹配的可能性就越大。这很简单有效,有很多东西可以使它在某些情况下更好或更快地工作,但你可能不需要这样的东西。你甚至不需要将错误标准化,但是如果你想手动查看几个可能的匹配,你就可以为它设置一个阈值。


由于这个问题得到了更多的关注,我决定在你多次处理许多图像的情况下添加一种方法来加快速度。当我有几万个我需要比较的图像时,我使用了这种方法,我确信一对典型的图像会有很大不同。我也知道我的所有图像都是完全相同的尺寸。在您比较对话框的情况下,您的典型图像可能主要是灰色的,并且您的某些图像可能需要调整大小(尽管可能只是表示不匹配),在这种情况下,此方法可能无法获得得多。

我们的想法是形成一个quad-tree,其中每个节点代表节点所代表区域的平均RGB值。因此,4x4图像将具有RGB值等于图像的平均RGB值的根节点,其子节点将具有表示其各自2x2区域的平均RGB值的RGB值,并且他们的子节点将是代表个别像素。 (实际上,最好不要超过大约16x16的区域,此时你应该开始比较单个像素。)

在开始比较图像之前,您还需要确定不良阈值。您不会以任何可靠的准确度计算高于此阈值的错误,因此这基本上是您愿意将图像标记为“不匹配”的阈值。

现在,当您将图像A与图像B进行比较时,首先要比较其四叉树表示的根节点。像处理单个像素图像一样计算不良情况,如果不良情况超过阈值,则立即返回并报告此级别的不良情况。因为你正在使用标准化的坏处,并且由于使用平方差异来计算坏处,所以任何特定级别的不良将等于或小于较低级别的不良,所以如果它在任何点超过阈值,你知道它也会超过单个像素级别的阈值。

如果阈值测试传递给nxn图像,则只需降低到下一级别并将其与2nx2n图像进行比较。一旦你变得足够低,只需比较各个像素。根据您的图像语料库,这可能允许您跳过大量的比较。

答案 1 :(得分:14)

我个人会想要一个图像散列算法。

图像散列的目标是将图像内容转换为特征序列,以获得精简表示。 该特征序列(即比特矢量)必须足够短以便快速匹配并保留可区分特征以使相似性测量可行。

有几种算法可以通过开源社区免费获得。

一个简单的例子可以在this article中找到,Neal Krawetz博士展示了平均哈希算法的工作原理:

  
      
  1. 缩小尺寸。消除高频和细节的最快方法是缩小图像。在这种情况下,将其缩小到8x8,以便总共有64个像素。不要费心保持纵横比,只需将其压缩到适合8x8的方形。这样,无论比例或宽高比如何,哈希都将匹配图像的任何变化。
  2.   
  3. 减少颜色。微小的8x8图像转换为灰度。这会将散列从64像素(64红色,64绿色和64蓝色)更改为64种颜色。
  4.   
  5. 平均颜色。计算64种颜色的平均值。
  6.   
  7. 计算位。这是有趣的部分。每个位都是根据颜色值是高于还是低于平均值来设置的。
  8.   
  9. 构建哈希。将64位设置为64位整数。只要您保持一致,订单无关紧要。 (我使用big-endian从左到右,从上到下设置位。)
  10.   

David Oftedal写了C# command-line application,可以使用Average Hash算法对图像进行分类和比较。 (我用你的样本图像测试了他的实现,我得到了98.4%的相似度。)

此解决方案的主要好处是您只能读取每个图像一次,创建哈希值并根据它们的相似性对它们进行分类(例如,使用Hamming distance)。

通过这种方式,您可以将特征提取阶段与分类阶段分离,如果您发现它不够准确,您可以轻松切换到另一个哈希算法。


修改

你可以找到一个简单的例子here(它包括一组40个图像的测试集,它得到40/40的分数。)

答案 2 :(得分:6)

Commercial TinEye API是一个非常好的选择。

我以前完成了图像匹配程序,现在图像处理技术非常出色,它的进步非常多。

ps这里是您从谷歌中提取的两张随机照片来自: http://www.tineye.com/search/1ec9ebbf1b5b3b81cb52a7e8dbf42cb63126b4ea/

答案 3 :(得分:5)

Here's a topic讨论了已经在OpenCV库中实现的与算法的图像相似性。在C#应用程序中导入低级函数应该没有问题。

答案 4 :(得分:4)

由于这是一次性工作,我会使用脚本(选择您喜欢的语言;我可能会选择Perl)和ImageMagick。您可以使用C#来完成与脚本相同的操作,尽管代码更多。只需调用命令行实用程序并解析生成的输出。

检查一对相似性的脚本大约是10行,如下所示:

首先使用identify检索尺寸并检查纵横比几乎相同。如果没有,没有匹配。如果是,则使用convert将较大的图像缩放为较小的图像。您应该事先用filter选项进行一些实验,以找到在已知等效图像中产生最大相似性的选项。其中9个可用。

然后使用compare函数生成相似性度量。比较足够聪明,可以处理翻译和裁剪。尝试找出不会产生太多误报的相似性阈值。

答案 5 :(得分:2)

我认为采用混合方法最好解决您的特定批次匹配问题

  1. 将@Paolo Morreti建议的Image Hashing algorithm应用于所有图片
  2. 对于一组中的每个图像,找到具有更接近设定距离的散列的图像子集
  3. 对于这个缩小的搜索空间,您现在可以应用@Running Wild或@Raskolnikov建议的昂贵的匹配方法......最好的一个获胜。

答案 6 :(得分:2)

您可以尝试基于内容的图像检索CBIR)。

坦率地说:

  1. 对于数据库中的每个图像,使用a生成指纹 傅立叶变换
  2. 加载源图像,制作指纹 image
  3. 计算源和所有之间的欧几里德距离 数据库中的图像
  4. 对结果进行排序

答案 7 :(得分:2)

我会做这样的事情:

  • 如果您已经知道模糊的图像是如何模糊的,请在比较之前将相同的功能应用于高质量图像。

    • 然后如上所述使用最小二乘比较图像。
    • 最低值应该给你一个匹配。理想情况下,如果两个图像相同,则会得到0
    • 为了加快速度,您可以对下采样图像执行大多数比较,然后针对图像的选定子样本进行优化
  • 如果您不知道,请尝试各种可能的功能(JPEG压缩,缩减采样......)并重复

答案 8 :(得分:1)

如果你只计算图像的像素差异,它只会在相同大小的图像或你确切知道如何在水平和垂直方向上缩放它时起作用,你也不会有任何移位或旋转不变性。

所以我建议只在你有最简单的问题形式时使用像素差异度量标准(图像在所有特征中都相同,但质量不同,而且质量不同的方式是什么?jpeg工件或只是重新缩放?),否则我建议使用标准化的互相关,它是更稳定的指标。 您可以使用FFTW或OpenCV来完成。

答案 9 :(得分:1)

如果质量差只是降低分辨率的原因那么:

  • 将高质量图像重新缩放至低质量图像分辨率(或将其重新缩放至相同的低分辨率)
  • 比较每个像素颜色以找到最接近的匹配

因此,例如将所有图像重新缩放到32x32并按像素比较该设置应该会给你非常合理的结果,而且它仍然很容易做到。虽然重新缩放方法可以在这里有所作为。

答案 10 :(得分:1)

恕我直言,最好的解决方案是模糊两个图像,然后使用一些相似性度量(相关/互信息等)来获得前K(K = 5可能是?)的选择。

答案 11 :(得分:1)

如果从图像中提取轮廓,可以使用ShapeContext获得非常好的图像匹配。

ShapeContext是为这些精确的东西构建的(比较基于相互形状的图像)

ShapeContext实现链接: Original publication A goot ppt on the subject CodeProject page about ShapeContext

*您可能需要尝试一些“轮廓提取”技术,如阈值或傅立叶变换,或者看看这个CodeProject page about contour extraction

祝你好运。

答案 12 :(得分:0)

我认为来自两个数据库的图像显示相同的对话框,图像应该接近相同但质量不同?然后匹配的图像将具有相同(或非常接近相同)的宽高比。

如果从高质量图像(或等效图像)生成低质量图像,则应使用与高质量图像相同的图像处理步骤作为预处理步骤,并与低质量图像数据库匹配。然后逐像素比较或直方图匹配应该很好。

如果您有许多图像,图像匹配可能会占用大量资源。也许多通道方法是个好主意?例如: 通过1:使用像纵横比这样的简单方法来获得图像(db中的宽度和高度字段?)(计算上便宜) 通过2:匹配或组合直方图为第一颜色通道(或所有通道)(相对计算便宜)

我也会推荐OpenCV。您可以将它与c,c ++和Python(以及很快的Java)一起使用。

答案 13 :(得分:0)

Running Wild的答案非常接近。你在这里做的是计算每个图像的峰值信噪比,或PSNR。在你的情况下,你真的只需要均方误差,但它的平方分量有助于计算图像之间的差异。

PSNR Reference

您的代码应如下所示:

sum = 0.0
for(imageHeight){
  for(imageWidth){
    errorR = firstImage(r,x,y) - secondImage(r,x,y)
    errorG = firstImage(g,x,y) - secondImage(g,x,y)
    errorB = firstImage(b,x,y) - secondImage(b,x,y)
    totalError = square(errorR) + square(errorG) + square(errorB)
  }
  sum += totalError
}
meanSquaredError = (sum / (imageHeight * imageWidth)) / 3

答案 14 :(得分:0)

大声思考:

如果您使用两个应该作为图层进行比较的图像并将它们组合在一起(从另一个图形中减去),您将获得一个新图像(某些绘图程序可以编写脚本以进行批量转换,或者您可以通过编写微小的DirectX或OpenGL程序)

接下来,您必须获得所得图像的亮度;它越黑,匹配就越好。

答案 15 :(得分:0)

我真的很喜欢Running Wild的算法,如果你能让这两个图像更相似,我认为它会更有效,例如通过降低更好的图像的质量。

答案 16 :(得分:0)

你可以尝试一种块匹配算法,虽然我不确定它对你的具体问题的确切有效性 - http://scien.stanford.edu/pages/labsite/2001/ee368/projects2001/dropbox/project17/block.html - http://www.aforgenet.com/framework/docs/html/05d0ab7d-a1ae-7ea5-9f7b-a966c7824669.htm

即使这不起作用,您仍应查看Aforge.net库。这里有几个工具(包括上面的块匹配)可以帮助您完成此过程 - http://www.aforgenet.com/

答案 17 :(得分:0)

您是否尝试过将轮廓/阈值技术与步行平均窗口(RGB值)结合使用?