38

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

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

我的任务是手动检查所有不好的图像并将它们与好的图像匹配。

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

图像质量好

劣质图像

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

4

18 回答 18

28

我真的认为没有理由为此使用任何外部库,我已经多次做过这种事情,并且以下算法效果很好。我假设如果您要比较两张具有相同尺寸的图像,但如果它们不同,您可以调整其中一张的大小。

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)

现在您已经获得了两个图像之间的标准化不良值,不良值越低,图像匹配的可能性就越大。这是简单而有效的,在某些情况下有很多东西可以让它更好或更快地工作,但你可能不需要这样的东西。您甚至不需要对不良进行规范化,但是如果您想手动查看几个可能的匹配项,您只需为其设置一个阈值即可。


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

这个想法是形成一个四叉树,其中每个节点代表该节点代表的区域的平均 RGB 值。因此,4x4 图像的根节点的 RGB 值等于图像的平均 RGB 值,其子节点的 RGB 值表示其各自 2x2 区域的平均 RGB 值,而它们的子节点将代表单个像素。(实际上,最好不要超过大约 16x16 的区域,此时您应该开始比较单个像素。)

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

现在,当您比较图像 A 和图像 B 时,首先比较它们的四叉树表示的根节点。就像计算单个像素图像一样计算 badness,如果 badness 超过您的阈值,则立即返回并报告此级别的 badness。因为您使用的是标准化的坏度,并且由于坏度是使用平方差计算的,所以任何特定级别的坏度都将等于或小于较低级别的坏度,所以如果它在任何点超过阈值,你知道它也会超过单个像素级别的阈值。

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

于 2012-07-26T15:34:24.627 回答
15

我个人会选择图像哈希算法。

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

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

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

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

David Oftedal编写了一个C# 命令行应用程序,它可以使用平均哈希算法对图像进行分类和比较。(我用你的示例图像测试了他的实现,我得到了 98.4% 的相似性)。

此解决方案的主要好处是您只需读取每个图像一次,创建散列并根据它们的相似性对它们进行分类(例如,使用汉明距离)。

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


编辑

你可以在这里找到一个简单的例子(它包括一个由 40 张图像组成的测试集,它得到了 40/40 的分数)。

于 2012-07-31T20:25:49.850 回答
6

这是一个讨论图像相似度算法的主题,已经在OpenCV库中实现。在 C# 应用程序中导入低级函数应该没有问题。

于 2012-07-26T14:13:45.117 回答
6

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

我过去做过图像匹配程序,现在的图像处理技术非常棒,它非常先进。

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

于 2012-07-31T05:50:33.223 回答
5

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

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

首先检索尺寸identify并检查几乎相同的纵横比。如果不是,则不匹配。如果是这样,则使用 将较大的图像缩放到较小的大小convert。您应该提前尝试一下filter选项,以找到在已知等效图像中产生最大相似性的选项。其中九个可用。

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

于 2012-07-30T20:51:13.603 回答
2

我会做这样的事情:

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

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

于 2012-07-31T11:44:16.610 回答
2

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

说穿:

  1. 对于数据库中的每个图像,使用傅里叶变换生成指纹
  2. 加载源图片,对图片做指纹
  3. 计算源图像与数据库中所有图像之间的欧几里得距离
  4. 对结果进行排序
于 2012-08-02T05:04:08.077 回答
2

我认为对此的混合方法将最好地解决您的特定批次匹配问题

  1. 将@Paolo Morreti 建议的图像哈希算法应用于所有图像
  2. 对于一组中的每个图像,找到哈希值接近设定距离的图像子集
  3. 对于这个缩小的搜索空间,您现在可以按照@Running Wild 或@Raskolnikov 的建议应用昂贵的匹配方法......最好的一个获胜。
于 2012-08-02T11:27:16.967 回答
1

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

于 2012-07-26T14:09:10.433 回答
1

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

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

ShapeContext 实现链接: 原创发布主题 CodeProject页面上关于ShapeContext的goot ppt

*您可能需要尝试一些“轮廓提取”技术,例如阈值或傅立叶变换,或查看有关轮廓提取的 CodeProject 页面

祝你好运。

于 2012-07-30T06:33:37.337 回答
1

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

因此,我建议仅在您遇到最简单形式的问题时才使用像素差异度量(图像在所有特征上都相同,但质量不同,顺便说一下,为什么质量不同?jpeg 伪影或只是重新缩放?),否则我建议使用归一化互相关,它是更稳定的度量。您可以使用 FFTW 或 OpenCV 来完成。

于 2012-07-31T08:35:54.610 回答
1

如果质量差只是分辨率较低的结果,那么:

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

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

于 2012-08-01T15:47:37.110 回答
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/

于 2012-07-26T14:14:26.903 回答
0

我真的很喜欢 Running Wild 的算法,我认为如果你可以让两张图片更相似,它会更有效,例如通过降低更好的一张的质量。

于 2012-07-30T06:45:09.653 回答
0

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

PSNR 参考

您的代码应如下所示:

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
于 2012-07-31T00:23:53.673 回答
0

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

如果低质量图像是从高质量图像(或等效图像)产生的,那么您应该使用与高质量图像预处理步骤相同的图像处理过程,并与低质量图像数据库匹配。然后逐像素比较或直方图匹配应该可以很好地工作。

如果您有很多图像,图像匹配会使用大量资源。也许多通道方法是个好主意?例如: 第 1 阶段:使用简单的方法,如纵横比来对图像进行分组(db 中的宽度和高度字段?)(计算成本低) 第 2 阶段:通过直方图匹配或分组第一个颜色通道(或所有通道)(相对计算便宜的)

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

于 2012-07-31T11:28:31.343 回答
0

只是大声思考:

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

接下来,您必须获得结果图像的亮度;它越暗,匹配越好。

于 2012-08-01T08:46:23.337 回答
0

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

于 2012-08-02T14:34:09.247 回答