关于图像处理:如何在Mathematica中找到Waldo?

How do I find Waldo with Mathematica?

这个周末我很烦:有什么好方法可以解决那些沃尔多在哪里的问题?使用Mathematica(图像处理和其他功能)的[北美以外的沃利]拼图?

这是我目前所拥有的功能,它通过调暗来稍微降低视觉复杂性。一些非红色:

1
2
3
4
5
6
7
8
9
10
whereIsWaldo[url_] := Module[{waldo, waldo2, waldoMask},
    waldo = Import[url];
    waldo2 = Image[ImageData[
        waldo] /. {{r_, g_, b_} /;
          Not[r > .7 && g < .3 && b < .3] :> {0, 0,
          0}, {r_, g_, b_} /; (r > .7 && g < .3 && b < .3) :> {1, 1,
          1}}];
    waldoMask = Closing[waldo2, 4];
    ImageCompose[waldo, {waldoMask, .5}]
]

以及一个URL的例子,其中这个"有效":

1
whereIsWaldo["http://www.findwaldo.com/fankit/graphics/IntlManOfLiterature/Scenes/DepartmentStore.jpg"]

(瓦尔多在收银机旁):

Mathematica graphic


我找到沃尔多了!

waldo had been found

我是怎么做到的

首先,我过滤掉所有非红色的颜色

1
2
waldo = Import["http://www.findwaldo.com/fankit/graphics/IntlManOfLiterature/Scenes/DepartmentStore.jpg"];
red = Fold[ImageSubtract, #[[1]], Rest[#]] &@ColorSeparate[waldo];

接下来,我将计算这个图像与一个简单的黑白图案的相关性,以找到衬衫中的红白过渡。

1
2
3
corr = ImageCorrelate[red,
   Image@Join[ConstantArray[1, {2, 4}], ConstantArray[0, {2, 4}]],
   NormalizedSquaredEuclideanDistance];

我使用Binarize来挑选图像中具有足够高相关性的像素,并在它们周围画一个白色圆圈,用Dilation来强调它们。

1
pos = Dilation[ColorNegate[Binarize[corr, .12]], DiskMatrix[30]];

我必须在水平仪周围玩一点。如果水平太高,就会检出太多的假阳性。

最后,我把这个结果和原始图像结合起来得到上面的结果

1
found = ImageMultiply[waldo, ImageAdd[ColorConvert[pos,"GrayLevel"], .5]]


我猜是一种"防弹的方式来做到这一点"(想想中央情报局在任何时候都能在任何卫星图像中找到沃尔多,而不仅仅是一张没有竞争元素的单一图像,比如条纹衬衫)。我会在瓦尔多的许多图像上训练玻尔兹曼机器——他坐着、站着、被遮挡等等的所有变化;衬衫、帽子、照相机和所有的作品。你不需要大量的沃尔多语料库(也许3-5就足够了),但越多越好。

这将为以任何正确的排列方式出现的各种元素分配概率云,然后(通过分割)确定平均对象大小,将源图像分割成最类似个体的对象单元(考虑可能的遮挡和姿势变化),但由于Waldo图片通常包括很多人在大约相同的规模,这应该是一个非常容易的任务,然后饲料这些部门的预培训玻尔兹曼机器。它会给你每个人都是沃尔多的概率。选一个概率最高的。

这就是今天OCR、邮政编码阅读器和无频闪手写识别的工作原理。基本上你知道答案在那里,你或多或少知道它应该是什么样子,其他的一切都可能有共同的元素,但肯定是"不是它",所以你不必为"不是它"而烦恼,你只需在你以前见过的所有可能的"它"中寻找"它"的可能性(例如,在邮政编码中,你只训练BM 1,2只需3秒,然后将每个数字输入到每台机器,并选择一个最有信心的数字)。这比所有数字的单个神经网络学习特性要好得多。


我同意@gregoryklopper的观点,解决在任意图像中找到waldo(或任何感兴趣的对象)的一般问题的正确方法是训练一个受监督的机器学习分类器。利用许多正负标记的例子,可以训练支持向量机、增强决策树桩或Boltzmann机器等算法,以实现对该问题的高精度。Mathematica甚至在其机器学习框架中包含了这些算法。

培训瓦尔多分类器的两个挑战是:

  • 确定正确的图像特征转换。这就是@heike的答案可能有用的地方:一个红色过滤器和一个剥离的模式检测器(例如,小波或DCT分解)将是一个很好的方法,可以将原始像素转换成分类算法可以学习的格式。还需要一个基于块的分解来评估图像的所有子部分…但是,由于waldo是a)总是大致相同的大小,b)总是在每个图像中只出现一次,所以这就变得容易了。
  • 获得足够的培训实例。SVM在每个类中至少有100个例子是最有效的。助推的商业应用(例如,数码相机中的面部聚焦)被训练成数以百万计的正面和负面例子。
  • 一个快速的谷歌图片搜索会找到一些好的数据——我现在就要去收集一些培训示例并对其进行编码了!

    然而,即使是机器学习方法(或@ind建议的基于规则的方法)也会为像沃尔多斯之地这样的图像而苦苦挣扎!


    我不懂数学。…太糟糕了。但我最喜欢上面的答案。好的。

    不过,仅仅依靠条纹来收集答案还是有一个很大的缺陷(我个人对一次手动调整没有问题)。有一个例子(这里由布雷特冠军列出)表明,他们有时会打破衬衫的图案。所以它变成了一个更复杂的模式。好的。

    我会尝试使用形状ID和颜色以及空间关系的方法。就像人脸识别一样,你可以在一定比例下互相寻找几何图案。警告是通常这些形状中的一个或多个被遮挡。好的。

    在图像上取得白平衡,在图像上取得红平衡。我相信Waldo总是相同的值/色调,但是图像可能来自扫描,或者是错误的拷贝。然后总是参考沃尔多实际的一系列颜色:红色、白色、深棕色、蓝色、桃红色,鞋子颜色。好的。

    有一个衬衫的图案,还有裤子,眼镜,头发,脸,鞋子和帽子,定义了沃尔多。而且,相对于图像中的其他人,沃尔多是瘦的一面。好的。

    所以,找一些随机的人来获取这张照片中人的身高。在图像中随机点测量一堆物体的平均高度(一个简单的轮廓会产生相当多的个体)。如果每件事都不在某个标准差范围内,那么它们现在就被忽略了。将平均高度与图像高度进行比较。如果比率太大(例如1:2、1:4或类似的接近),请重试。运行10(?)有时要确保所有样本都非常接近,不包括任何超出标准差的平均值。在数学上可能吗?好的。

    这是你的瓦尔多尺码。沃尔索很瘦,所以你在找一些5:1或6:1(或其他什么)的东西。然而,这还不够。如果瓦尔多被部分隐藏,高度可能会改变。所以,你要找一块红白色的,2:1。但必须有更多的指标。好的。

  • 沃尔多有眼镜。搜索红白上方0.5:1的两个圆。
  • 蓝色裤子。在红白两色末端和他脚之间的任何距离内,任何相同宽度的蓝色。注意他穿的衬衫太短了,所以脚不太紧。
  • 帽子。红白相间,距离头顶两倍。注意下面一定是深色的头发,可能还有眼镜。
  • 长袖。红白与主红白有一定角度。
  • 深色头发。
  • 鞋子颜色。我不知道颜色。
  • 这些都可以适用。这些都是对照片中类似人物的负面检查,例如,2穿着红白围裙(太靠近鞋子),5消除了浅色头发。此外,形状只是这些测试的一个指示器。…在指定的距离内单独使用颜色可以获得良好的效果。好的。

    这将缩小要处理的区域。好的。

    存储这些结果将产生一组应该包含Waldo的区域。排除所有其他区域(例如,对于每个区域,选择一个比平均人大小大两倍的圆),然后运行@heike设置的流程,删除除红色以外的所有区域,依此类推。好的。

    有什么关于如何编写代码的想法吗?好的。

    编辑:好的。

    关于如何编写代码的想法。…排除除瓦尔多红以外的所有区域,将红色区域骨架化,并将其修剪到一个点。沃尔多的头发是棕色的,沃尔多的裤子是蓝色的,沃尔多的鞋是彩色的。对于瓦尔多肤色,排除,然后找到轮廓。好的。

    其次,排除非红色,扩大(很多)所有红色区域,然后骨架和修剪。这部分将列出可能的沃尔多中心点。这将是比较所有其他Waldo颜色部分的标记。好的。

    从这里开始,使用骨骼化的红色区域(而不是扩张的区域),计算每个区域中的线。如果有正确的号码(4,对吗?),这当然是一个可能的领域。如果不是,我想只是排除它(作为一个沃尔多中心。…可能还是他的帽子)。好的。

    然后检查上面是否有脸型,上面是否有发点,下面是否有裤点,下面是否有鞋点,等等。好的。

    还没有代码——还在读文档。好的。好啊。


    我有一个使用opencv找到waldo的快速解决方案。

    我使用了opencv中的模板匹配函数来查找waldo。

    为此,需要一个模板。所以我从原始图像中裁剪了Waldo,并将其用作模板。

    enter image description here

    接下来,我调用了cv2.matchTemplate()函数和归一化相关系数作为所使用的方法。它在单个区域返回很高的概率,如下面的白色部分所示(在左上方区域的某个位置):

    氧化镁

    使用cv2.minMaxLoc()函数找到最高可能区域的位置,然后我用它绘制矩形来突出waldo:

    氧化镁