【问题标题】:Basic pattern recognition in binary (pixelated) image二值(像素化)图像中的基本模式识别
【发布时间】:2019-04-25 17:11:34
【问题描述】:

这是一种图像的裁剪示例(大约11x9 像素)(最终实际上都是大小28x28,但存储在内存中,扁平为784-components 数组)我会尝试将算法应用于:

基本上,我希望能够识别何时出现此形状(红线用于强调像素的分离,而周围的黑色边框用于更好地勾勒出StackOverflow的白色背景下的图像) :

它的方向无关紧要:必须在任何可能的表示(旋转和对称)中沿水平和垂直轴检测它(因此,例如,不应考虑 45° 旋转,也不应考虑对角对称:例如,仅考虑 90°、180° 和 270° 旋转)。

在我第一次展示的那张图片上可以找到两种解决方案,但只需要找到一个(忽略白色区域周围的灰色模糊):

以另一个示例为例(这也表明图像中的白色图形并不总是被黑色像素完全包围):

该函数应该返回 True,因为该形状存在:

现在,显然有一个简单的解决方案:

使用诸如pattern = [[1,0,0,0],[1,1,1,1]] 之类的变量,生成其变体,然后沿图像滑动所有变体,直到找到完全匹配的位置,此时整个过程停止并返回True

然而,在最坏的情况下,这将占用8*(28-2)*(28-4)*(2*4),这对于单个图像来说大约是 40000 次操作,这似乎有点矫枉过正(如果我的快速计算正确的话)。

我猜想使这种天真的方法变得更好的一种方法是首先扫描图像,直到找到第一个白色像素,然后开始寻找比该点早 4 行和 4 列的图案,但即使这样似乎也不够好。

有什么想法吗?也许这种功能已经在某些库中实现了? 我正在寻找一种能够击败我幼稚方法的实现或算法。

作为旁注,虽然是一种 hack,但我猜这是可以转移到 GPU 上的问题,但我对此没有太多经验。 虽然这不是我想要的主要内容,但如果您提供答案,请随时添加与 GPU 相关的注释。


编辑

我最终实现了接受的答案。你可以在this Gist看到我的代码。

【问题讨论】:

  • 当您设法找到解决问题的可行解决方案时,如果您愿意分享,我真的很想看看源代码。
  • 我一定会的。您介意分享一下您为这种解决方案考虑的应用程序类型吗?或者你只是因为好奇才想要代码? :)
  • @Jake12342134 你去吧:gist.github.com/payne911/…
  • 您好,感谢您的代码!我很高兴去看一看。我只是出于好奇才问的!
  • @Jake12342134 不用担心!我正在使用此代码来检测 28x28 图片是否表示没有任何学习算法的凸图。我最终得到了 >99% 的准确率,这还不错。

标签: python python-3.x image algorithm image-processing


【解决方案1】:

如果您有太多操作,请考虑如何减少操作。

对于这个问题,我会使用图像积分。

如果你在图像上卷积一个求和内核(这是 fft 域中的一个非常快速的操作,只有 conv2,imfilter),你知道只有积分等于 5 的位置(在你的情况下)是可能的模式匹配位置。检查那些(即使是你的 4 次旋转)在计算上应该非常快。您的示例图片中符合此模式的位置不能超过 50 个。

我的 python 不是很流利,但这是你在 MATLAB 中的第一张图片的概念证明,我相信翻译这段代码应该不是问题。

% get the same image you have (imgur upscaled it and made it RGB)
I=rgb2gray(imread('https://i.stack.imgur.com/l3u4A.png'));
I=imresize(I,[9 11]);
I=double(I>50);

% Integral filter definition (with your desired size)
h=ones(3,4);

% horizontal and vertical filter (because your filter is  not square)
Ifiltv=imfilter(I,h);
Ifilth=imfilter(I,h');
% find the locations where integral is exactly the value you want
[xh,yh]=find(Ifilth==5);
[xv,yv]=find(Ifiltv==5);

% this is just plotting, for completeness
figure()
imshow(I,[]);
hold on
plot(yh,xh,'r.');
plot(yv,xv,'r.');

这导致要检查 14 个位置。我的标准计算机计算两个图像积分平均需要 230ns,我称之为 fast

GPU 计算也不是 hack :D。这是解决一大堆问题的方法,因为它们拥有巨大的计算能力。例如。 GPU 中的卷积速度非常快。

【讨论】:

  • 您介意解释一下“在图像上卷积求和内核”是什么意思吗?我的理解是,您基本上是在图像上滑动图案作为掩码并计算每个区域的逻辑与的总和,只在内存中保留它等于 5 的点(因为图案中有 5 个白色像素)。我猜你正在对模式的每一种可能变化都这样做,所以这个操作将重复 8 次(因为对称/旋转)。 注意: 我无法理解如何使用这些红点:没有一个允许模式匹配
  • @payne“在图像上滑动图案作为掩码”-> 卷积。然后我选择了总和为 5 的唯一位置(红点)。只有那些可以匹配您的模式,没有其他位置可以。我不是说他们有,我是说他们是唯一有 5 像素白色的人。作为你的模式。我没有给你图案的位置,我给了你一个非常简化的图案可能位置的列表。然后只需使用您之前在哪里做的任何事情来检查它。
  • @payne 不,对数组的所有值求和是 CPU 上的 O(n) 和 GPU 上的 O(log(n))。但是你需要阅读几本书才能更好地理解这一点,这里的评论不会削减它。 GPU 具有同时操作,因此如果您可以同时做一件事和另一件事,复杂性就会降低,因为它与时间有关。
  • @payne 请花一些时间阅读我介绍的一些概念,以便您更好地理解答案。了解图像处理中的“卷积”是什么以及为什么它很快。另请阅读“图像积分”是什么,作为答案的核心。我绝不会使用你的模式,只是做了一个技巧来显着减少你需要检查模式的位置。
  • 我最终实现了您的解决方案:它运行良好。我真的很喜欢阅读这项技术:谢谢分享! (我已经分享了一个包含我的代码的 Gist,作为对 OP 评论者的回复。)
【解决方案2】:

您正在实现的运算是数学形态学中称为hit and miss 的运算符。

它可以作为两个腐蚀的组合非常有效地实现。如果您要检测的形状可以分解为几个简单的几何形状(尤其是矩形可以快速计算),那么操作员的效率会更高。

您会在大多数图像处理库中发现非常有效的侵蚀,例如尝试 OpenCV。 OpenCV 还有一个命中和未命中运算符,here is a tutorial for how to use it


作为预期输出的示例,我生成了一个简单的测试图像(左),应用了一个命中和未命中运算符,其模板与图像中的一个位置完全匹配(中),并再次使用了一个模板不匹配任何地方(右):

我是在 MATLAB 而不是 Python 中完成的,因为我已经打开了它,而且它对我来说最容易使用。这是代码:

se = [1,1,1,1      % Defines the template
      0,0,0,1];
img = [0,0,0,0,0,0 % Defines the test image
       0,1,1,1,1,0
       0,0,0,0,1,0
       0,0,0,0,0,0
       0,0,0,0,0,0
       0,0,0,0,0,0];
img = dip_image(img,'bin');

res1 = hitmiss(img,se);
res2 = hitmiss(img,rot90(se,2));

% Quick-and-dirty display
h = dipshow([img,res1,res2]);
diptruesize(h,'tight',3000)
hold on
plot([5.5,5.5],[-0.5,5.5],'r-')
plot([11.5,11.5],[-0.5,5.5],'r-')

上面的代码使用了我在DIPimage 中实现的命中和未命中操作符。在PyDIPHitAndMiss 中的Python 中也提供了相同的实现(PyDIP 还没有二进制版本,您需要自己编译):

import PyDIP as dip
# ...
res = dip.HitAndMiss(img, se)

【讨论】:

  • 我知道必须有一些与我已经在 OpenCV 中实现的幼稚方法非常相似的东西:感谢您提供的关键字和链接!然而,算法的复杂性是多少?它是否已经实现以便卸载到 GPU(我对 OpenCV 很陌生)?
  • @payne:复杂度是 O(n),但对于大多数图像处理算法来说都是如此,您需要访问每个像素。您的示例形状可分为 3 行(一条黑色,未命中内核,两条白色,一起命中内核)。一条线的腐蚀可以在时间上独立于它的长度来计算,每个图像像素只需 3 次比较。你会这样做 3 次,然后比较输出,每个像素可能 11 或 12 次操作。 — 我不确定 OpenCV 是否以这种方式实现它。但 OpenCV 的许多算法确实有 GPU 实现。
  • @Ander:Morpholog 有很多非常有趣的运算符,它仍然是一个非常活跃的社区,虽然规模很小。而且大多数人认为形态学只适用于二值图像...
  • @payne:关于模式:我确实假设您在那里有一个 2x4 矩阵,但这并不重要,您可以在 Ander 或我的方法中使用任何模式。
  • @payne:Ander 的方法非常有效,尤其是考虑到您需要进行多个定向。您只计算一次的积分图像,您计算两次的最终计数(2x4 和 4x2)。然后这取决于有多少点与计数相匹配,你最终做了多少工作。但这是一个聪明的方法。我想添加这个答案不是因为它更有效,而是因为这是一个标准运算符并且存在可供使用的有效实现。在您证明这是您处理过程中的瓶颈之前,最好不要自己滚动。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-10
  • 1970-01-01
  • 2012-09-17
  • 1970-01-01
  • 2012-12-06
相关资源
最近更新 更多