图像处理:“可口可乐”识别的算法改进


1657

我过去几年中最有趣的项目之一是关于图像处理的项目。目的是开发一个能够识别可口可乐“罐头”的系统(请注意,我强调的是“罐头”一词,稍后您会看到原因)。您可以在下面看到一个示例,该示例在带有刻度和旋转的绿色矩形中可以识别。

模板匹配

对项目的一些限制:

  • 背景可能非常嘈杂。
  • 可以具有任何规模旋转,甚至方向(在合理的限度内)。
  • 图像可能有一定程度的模糊性(轮廓可能不完全笔直)。
  • 图像中可能有可口可乐瓶,该算法只能检测到罐头
  • 图像的亮度可能相差很大(因此您不能“过多”依赖颜色检测)。
  • 可以部分地隐藏在两侧或中间,可能部分地隐藏了一瓶后面。
  • 有可能是没有在所有的图像中,在这种情况下,你必须找到什么,写一条消息这样说。

因此,您可能会遇到如下棘手的事情(在这种情况下,我的算法完全失败了):

总失败

我前一段时间做了这个项目,并且做起来很有趣,并且实现得很好。以下是有关我的实现的一些详细信息:

语言:使用OpenCV库在C ++中完成。

预处理:对于图像预处理,即将图像转换为更原始的形式以提供给算法,我使用了两种方法:

  1. 将颜色域从RGB更改为HSV,并基于“红色”色调进行过滤,饱和度高于特定阈值以避免产生类似橙色的颜色,而对低值进行过滤以避免产生深色。最终结果是一个二进制的黑白图像,其中所有白色像素将代表与该阈值匹配的像素。显然,图像中仍然有很多废话,但这减少了必须处理的尺寸数。 二值化图像
  2. 使用中值滤波进行噪声滤波(获取所有邻居的中值像素值,然后用该值替换像素)以减少噪声。
  3. 经过2个先验步骤后,使用Canny Edge Detection滤镜获取所有项目的轮廓。 轮廓检测

算法:我为此任务选择的算法本身取材于这本很棒的书中有关特征提取的书,并称为通用霍夫变换(与常规霍夫变换完全不同)。它基本上说了几件事:

  • 您可以在不知道其解析方程的情况下描述空间物体(此处就是这种情况)。
  • 它可以抵抗缩放和旋转等图像变形,因为它将基本上测试图像的缩放因子和旋转因子的每种组合。
  • 它使用算法将“学习”的基本模型(模板)。
  • 轮廓图像中剩余的每个像素将投票给另一个像素,根据它从模型中学到的信息,该像素应该是对象的中心(就重力而言)。

最后,您将获得投票的热图,例如,此处罐头轮廓的所有像素都将为其重力中心投票,因此在与像素相对应的同一像素中将有很多投票居中,将会在热图中看到一个峰值,如下所示:

GHT

有了这些功能后,您就可以使用简单的基于阈值的启发式方法来确定中心像素的位置,从中可以得出比例尺和旋转角度,然后在其周围绘制一个小矩形(最终比例尺和旋转系数显然相对于您的原始模板)。理论上至少...

结果:现在,尽管此方法在基本情况下可行,但在某些领域却严重缺乏:

  • 这是非常慢!我的压力还不够。处理30张测试图像几乎需要整整一天的时间,这显然是因为我对旋转和平移具有非常高的缩放系数,因为某些罐非常小。
  • 当瓶子出现在图像中时,它完全丢失了,并且出于某种原因几乎总是找到瓶子而不是罐子(也许是因为瓶子更大,因此像素更多,投票更多)
  • 模糊图像也不是很好,因为投票最终以像素为中心围绕中心的随机位置,从而以非常嘈杂的热图结束。
  • 实现了平移和旋转的不变性,但没有实现定向,这意味着未识别未直接面对相机物镜的罐子。

您是否可以使用专有的OpenCV功能帮助我改善特定算法,以解决上述四个特定问题?

我希望有些人也能从中学到一些东西,毕竟我认为不仅提出问题的人也应该学习。:)


45
可以说,这个问题在dsp.stackexchange.com或stats.stackexchange.com上更合适,并且您当然也应该考虑在这些站点上重新提问。
2012年

49
在这里要做的第一件事是分析为什么发生不同的故障情况。例如,隔离出瓶中奖的地方,图像模糊等的示例,并进行一些统计分析以了解它们的霍夫表示与您希望检测的霍夫表示之间的差异。这里这里
2012年

7
@stacker很好。为了提高速度,您希望获得便宜的计算功能,例如定向梯度的直方图。真正幼稚的第一种方法是在一些训练图像中手动标记一堆can矩形,并使用这些标记和随机否定样本来训练SVM或决策树分类器。训练将花费更长的时间,但是在新颖图像上的执行将更快。当我有更多的空闲时间来包含正确的引用时,我打算编写这种方法。
2012年

9
reCAPTCHA类似的方法怎么样?;)
George Duckett 2012年

39
为什么将其从dsp.stackexchange.com移走?似乎该站点比stackoverflow o_O更为合适
BlueRaja-Danny Pflughoeft 2012年

Answers:


672

另一种方法是使用尺度不变特征变换(SIFT)或加速鲁棒特征(SURF)提取特征(关键点)。

OpenCV 2.3.1中实现。

您可以使用Feature2D + Homography中的功能找到一个不错的代码示例,以查找已知对象

两种算法对于缩放和旋转都是不变的。由于它们可以使用功能,因此您也可以处理遮挡(只要可见足够的关键点)。

在此处输入图片说明

图片来源:教程示例

SIFT的处理过程需要几百毫秒,SURF速度更快,但是不适用于实时应用。ORB使用的FAST在旋转不变性方面较弱。

原始论文


6
我同意@stacker-SIFT是一个很好的选择。它在缩放和旋转操作方面非常强大。它对透视变形有一定的抵抗力(可以按照堆栈器的建议进行改进:具有所需对象不同透视图的模板数据库)。根据我的经验,它的致命弱点是强大的灯光变化和非常昂贵的计算。我不知道任何Java实现。我知道OpenCV的实现,并使用了适合实时性能的GPU c ++ / Windows(SiftGPU)实现。

31
警告说明:尽管我喜欢SIFT / SURF以及他们对我所做的一切,但它们都受到专利的限制。这可能是一个问题,具体取决于许多条件,包括地理位置AFAIK。
阿戈斯2012年

12
因此,请尝试没有专利问题的OpenCV的ORB或FREAK。ORB比SIFT快得多。ORB:根据我的经验,它在缩放比例和光照变化方面有些差,但是请自己进行测试。
Rui Marques 2012年

66
您怎么能接受这作为答案...没有任何一个特征描述符可以区分瓶子和罐头。它们都只查看不变的局部模式描述符。我同意SIFT,SURF,ORB,FREAK等可以帮助您进行特征匹配,但是..您对问题的其他部分(如咬合,Bottle vs Can等)有什么看法。我希望这不是一个完整的解决方案,如果您愿意让GOOGLED解决您的问题,第一个结果可能就是这个答案。
G453 2013年

11
@ G453你绝对正确!可能他对SHIFT的性能着迷,并忘记了特征提取和匹配不是问题……
sepdek 2014年

383

为了加快处理速度,我会利用这样一个事实,即不要求您查找任意图像/对象,而是查找具有可口可乐徽标的对象。这很重要,因为该徽标非常有特色,并且在频域中,尤其是在RGB的红色通道中,应具有特征性的,比例不变的签名。也就是说,水平扫描线(在水平对齐的徽标上训练)遇到的红色到白色到红色的交替图案在穿过徽标的中心轴时将具有独特的“节奏”。该节奏将以不同的比例和方向“加速”或“减速”,但将保持成比例的相等。您可以通过徽标在水平和垂直方向识别/定义数十条这样的扫描线,以及在对角线中识别/定义更多条扫描线,以爆炸形图案。称这些为“签名扫描线”。

签名扫描线

在目标图像中搜索此签名很简单,只需扫描水平条中的图像即可。在红色通道中寻找一个高频(指示从红色区域移到白色区域),一旦找到,请查看它是否跟随训练中确定的频率节律之一。一旦找到匹配项,您将立即知道扫描线在徽标中的方向和位置(如果您在培训期间跟踪这些内容),因此从那里识别徽标的边界很简单。

如果这不是线性高效算法,或者几乎不是线性高效算法,我会感到惊讶。它显然不能解决您的水壶歧视问题,但至少您会拥有徽标。

(更新:为了识别瓶子,我会寻找徽标旁边的可乐(棕色液体),也就是瓶子内部。或者,如果是空瓶子,我会寻找一个总是有盖的瓶盖基本形状,大小和与徽标的距离相同,通常为白色或红色。搜索相对于徽标应有帽子的纯色椭圆形。当然,这并非万无一失,但您的目标是快速找到简单的对象。)

(距离我的图像处理工作已经过去了几年,所以我将建议从总体上讲是概念性的。我认为这可能与人眼的操作方式略有相似,或者至少是我的大脑的操作方式!)


24
这是一个很好的建议,我特别喜欢这个算法应该非常快的事实,即使它可能会有很多假阴性。我的隐藏目标之一是针对机器人技术实时使用此检测,因此这可能是一个不错的折衷方案!
Charles Menguy 2012年

42
是的,在大多数实时,真实世界的建模任务中,经常会(在以精度为特征的领域中)忘记近似算法是必不可少的。(我的论文基于这个概念。)将您的时间限制算法保存在有限的区域中(以修剪误报)。请记住:在机器人技术中,您通常不限于单个图像。假设使用移动机器人,快速的算法可以比复杂的算法花费更少的时间从不同角度搜索数十个图像,从而大大减少了假阴性。
kmote 2012年

29
我喜欢这样的想法:使用相当于条形码的扫描仪来极其快速地检测可口可乐徽标。+1!
叶立昂

8
在这种情况下寻找签名的问题是,如果我们将罐子转向另一侧,即隐藏签名,则算法将无法检测到罐子。
karlphillip 2012年

34
@karlphillip:如果隐藏签名(即徽标),则任何基于查找徽标的方法都将失败。
叶立昂

162

有趣的问题:当我瞥了一眼您的酒瓶图像时,我认为它也是一个罐头。但是,作为一个人类,我所做的与众不同之处在于我后来发现它也是一个瓶子……

因此,要区分罐头和瓶子,首先简单地扫描瓶子怎么样?如果找到一个,在寻找罐子之前先将标签遮盖。

如果您已经在做罐头,则实施起来并不难。真正的缺点是它将处理时间加倍。(但是,考虑到现实世界中的应用程序,您最终还是想做瓶子;-)


5
是的,我也曾考虑过这一点,但没有太多时间去做。由于它的主要部分看起来像缩放罐,您将如何识别它?我当时也在考虑寻找红色插头,看它是否与瓶装中心对齐,但这似乎并不牢固。
Charles Menguy 2012年

42
如果有一个与“可口可乐”平行的红色帽子(或戒指),则很可能是一瓶。
Lukasz Madon '04

@linker您是如何训练罐算法的?你有罐的例子吗?用瓶子的例子训练怎么样?
siamii

1
该算法的优势在于,您只需要训练一个模板,然后应用所有转换将其与其他潜在罐匹配。我正在使用该模板的二值化和基于轮廓的版本进行训练,所以罐子和瓶子之间的唯一区别是塞子,但是我担心它会带来更多的误报,因为重力中心会在边缘的某个位置或在瓶子外面。我猜值得尝试一下。但这将使我的处理时间增加一倍,我会哭泣;)
Charles Menguy 2012年

7
本质上,这是一个合理的方向。我的说法略有不同:首先找到所有候选人,然后为每个候选人确定是瓶子,罐头还是其他东西。
MSalters 2012年

131

即使人类在第二张图像中区分瓶子和罐头也不难(前提是瓶子的透明区域被隐藏了)?

除了很小的区域外,它们几乎是相同的(也就是说,罐头顶部的宽度略小,而瓶子的包装纸的宽度在整个宽度上都是相同的,但是有小的变化吗?)

我想到的第一件事是检查瓶子的红顶。但是,如果瓶子没有顶部,或者部分隐藏(如上所述),仍然是一个问题。

我想到的第二件事是关于瓶子的透明度。OpenCV在查找图像中的透明对象方面有一些工作。检查以下链接。

特别要看一下,以了解他们检测玻璃的准确性:

查看其执行结果:

在此处输入图片说明

他们说这是K. McHenry和J. Ponce在CVPR 2006上发表的论文“用于寻找玻璃的测地线主动轮廓框架”的实现。

这可能对您的情况有所帮助,但是如果装满瓶子问题就会再次出现。

因此,我认为在这里,您可以首先搜索瓶子的透明主体,或者搜索横向连接到两个透明物体(显然是瓶子)的红色区域。(理想工作时,图像如下)。

在此处输入图片说明

现在,您可以删除黄色区域,也就是瓶子的标签,然后运行算法来查找罐头。

无论如何,该解决方案也像其他解决方案一样具有不同的问题。

  1. 仅在您的瓶子为空时才起作用。在这种情况下,您将必须搜索两种黑色之间的红色区域(如果可口可乐液体为黑色)。
  2. 如果覆盖了透明部分,则是另一个问题。

但是无论如何,如果图片中没有上述问题,那似乎是更好的方法。


+1我对此进行了思考,并且正在实施该方法。但是,@ linker应该共享他的图像集,以便我们可以尝试进行更多有根据的猜测。
karlphillip 2012年

是的..我也认为,如果有更多图像,那就太好了。
Abid Rahman K

考虑是否只有瓶/罐的标签,而没有瓶盖或透明性的其他区别因素,或者罐的顶部/底部-瓶的宽度与罐的宽度不同。

如果在瓶子徽标的前面放置一个罐子怎么办?
AlgoRythm

51

我真的很喜欢Darren Cook堆垛机对这个问题的回答。我当时正将自己的想法发表在评论中,但我相信我的方法太过具有答案性,无法离开这里。

简而言之,您已经确定了一种算法,可确定在空间中的特定位置是否存在可口可乐徽标。您现在正在尝试针对任意方向和任意缩放比例确定一种启发式方法,以区分可口可乐与其他对象,包括:与该标志性徽标相关联的瓶子广告牌广告可口可乐用具。您没有在问题陈述中提到很多其他情况,但我认为它们对于算法的成功至关重要。

这里的秘诀之一就是确定视觉特征一个可以包含或通过负空间,特点是本作中不存在对罐外焦产品是什么。为此,当前的最高答案勾勒出了一种基本方法,即当且仅当通过瓶盖,液体或其他类似的视觉启发法未识别出“瓶”时,才选择“罐”。

问题是这崩溃了。例如,瓶子可能是空的,并且没有瓶盖,从而导致误报。或者,它可能是带有其他特征的不完整瓶子,从而再次导致错误检测。不用说,这不是优雅的方法,也不符合我们的目的。

为此,罐的最正确选择标准如下:

  • 正如您在问题中所概述的那样,对象轮廓的形状是否正确?如果是这样,请+1。
  • 如果我们假设存在自然光或人造光,是否会在瓶子上检测到铬轮廓,以表明其是否由铝制成?如果是这样,请+1。
  • 我们是否确定相对于我们的光源,物体的镜面特性是正确的(关于光源检测的说明性视频链接)?如果是这样,请+1。
  • 我们是否可以确定关于对象的其他任何属性,将其标识为罐,包括但不限于徽标的拓扑图像偏斜,对象的方向,对象的并置(例如,在平面上)例如桌子或其他罐头),以及是否有拉片?如果是这样,则每个+1。

然后,您的分类可能如下所示:

  • 对于每个候选匹配项,如果检测到存在可口可乐徽标,则绘制灰色边框。
  • 对于每次+2的比赛,请绘制红色边框。

这在视觉上向用户突出显示了检测到的内容,强调了可以正确检测为扭曲罐的弱阳性。

对每个属性的检测会带来非常不同的时间和空间复杂度,对于每种方法,快速通过http://dsp.stackexchange.com来确定最正确,最有效的算法是合理的。我的意图是纯粹地和简单地强调,通过使候选检测空间的一小部分无效检测某物是否是罐子,并不是解决此问题的最有效或最有效的解决方案,理想情况下,您应该采取适当的措施相应地。

嘿,恭喜《黑客新闻》发布!总体而言,这是一个非常棒的问题,值得其宣传。:)


2
这是一个有趣的方法,至少值得一试,我真的很喜欢您对问题的推理
Charles Menguy 2012年

这就是我的想法:请勿排除某些误报。掌握焦炭罐制造的更多特征。但是我想知道:您如何处理压扁的罐头?我的意思是,如果您踩可乐罐,那还是可乐罐。但是它将不再具有相同的形状。还是AI问题完全解决?
伊恩

41

看形状

在罐子/瓶子红色部分的形状上a一口。请注意,在瓶子标签是笔直的情况下,罐子如何在顶部稍微变细。您可以通过比较红色部分的宽度和长度来区分这两个部分。

看重点

区分瓶子和罐头的一种方法是材料。瓶子是用塑料制成的,而罐子是用铝金属制成的。在光线充足的情况下,查看镜面反射率是从罐头标签分辨出瓶子标签的一种方法。

据我所知,这就是人类如何分辨两种标签之间的区别。如果照明条件较差,则无论如何将两者区分开肯定会有一些不确定性。在这种情况下,您将必须能够检测到透明/半透明瓶本身的存在。


我喜欢这个主意,但似乎您需要一些非常好的照明条件。例如,在有罐子和瓶子的示例图像中,很难区分开。
Charles Menguy 2012年

在您的示例中,请注意,塑料标签的镜面反射性比罐子上的非常亮的斑点散布得多?这就是你可以说的。
tskuzzy 2012年

我知道,在这种情况下,您将使用哪种颜色空间表示形式来捕获算法中的镜面反射?要获得RGB或HSV,这似乎相当困难
Charles Menguy 2012年

2
如果光源在罐子后面怎么办?我认为您不会看到亮点。
Rui Marques 2014年

37

请查看Zdenek Kalal的“ 捕食者”追踪器。它需要一些培训,但是它可以主动学习被跟踪对象如何看待不同方向和比例并实时进行!

源代码可在他的网站上找到。它在MATLAB中,但是社区成员可能已经完成了Java实现。我已经在C#中成功重新实现了TLD的跟踪器部分。如果我没记错的话,TLD正在使用蕨类作为关键点检测器。如果跟踪器丢失了对象,我将改用SURF或SIFT(@stacker已建议)重新获取对象。跟踪器的反馈使您可以轻松地随时间构建动态的筛选/冲浪模板列表,这些模板可以随时间推移以很高的精度重新捕获对象。

如果您对我的C#跟踪器实现感兴趣,请随时提出。


感谢您提供的链接,它看起来很有趣。关于培训,为获得合理结果而合理的培训规模是多少?如果您甚至在c#中也有实现,那也将非常有帮助!
Charles Menguy 2012年

在研究TLD时,我发现另一个正在寻找C#实现的用户-是否有理由不将您的工作放在Github上? stackoverflow.com/questions/29436719/…–
溢出者

2
NB Years,以后,链接现在消失了
J Evans

33

如果您不仅仅局限于一个不受限制的相机,也许您可​​以转向使用Xbox Kinect之类的距离传感器。使用此功能,您可以对图像进行基于深度和颜色的匹配分割。这样可以更快地分离图像中的对象。然后,您可以使用ICP匹配或类似的技术来匹配罐的形状,而不是仅匹配罐的轮廓或颜色,并且鉴于罐是圆柱形的,如果您以前对目标进行了3D扫描,那么这对于任何方向都是有效的选择。这些技术通常很快,特别是在用于解决速度问题的特定目的时。

我还建议,不一定出于准确性或速度的考虑,而是为了娱乐,您可以在色相分割的图像上使用经过训练的神经网络来识别罐子的形状。这些速度非常快,通常可以达到80/90%的准确度。培训将是一个漫长的过程,尽管您必须手动识别每个图像中的罐头。


3
实际上,我在帖子中没有对此进行解释,但是对于此作业,我获得了大约30张图像的集合,并且必须执行一种算法,使其在所述的各种情况下都与它们匹配。当然,最后还保留了一些图像来测试算法。但是我喜欢Kinect传感器的想法,并且我想阅读更多有关该主题的信息!
Charles Menguy 2012年

具有令人满意的结果的神经网络训练集的大小大约是多少?这种方法的好处还在于,我只需要一个模板即可匹配几乎所有内容。
Charles Menguy 2012年

2
如果您的图像集是预定义的且有限的,则仅在
编排中实现

是的,如果我在数据集上进行训练,那么我将对算法进行测试,可以确保获得完美的结果:)但是,例如,对于该作业,该程序最后由教师在一组保留的图像上进行了测试。我想做一些可靠且不会过度适合训练数据的事情。
Charles Menguy 2012年

训练集的数量各不相同,但是您需要注意一些事项:不要过度训练,您可能需要一个测试集来显示您的准确性如何。同样,训练集的数量将取决于您将使用的层数。
神奇先生狐狸先生2012年

24

我会检测到红色矩形:RGB-> HSV,过滤红色->二进制图像,关闭(先膨胀然后腐蚀,imclose在Matlab中称为)

然后从最大到最小浏览矩形。可以删除在已知位置/比例下具有较小矩形的矩形(假定瓶比例是恒定的,较小的矩形将是瓶盖)。

这将使您留下红色矩形,然后您需要以某种方式检测徽标以判断它们是红色矩形还是可乐罐。类似于OCR,但带有已知徽标?


2
就像在移动DSP的短时间内讨论的那样,有些瓶子可能没有塞子;)或塞子可能被部分隐藏了。
Charles Menguy 2012年

22

这可能是一个非常幼稚的想法(或可能根本不起作用),但是所有可乐罐的尺寸都是固定的。因此,如果同一张图片中同时包含一个罐头和一个瓶子,那么您可以通过尺寸考虑因素来区分它们(瓶子会更大)。现在,由于缺少深度(即3D映射到2D映射),瓶子可能会缩水并且大小没有差异。您可以使用立体成像恢复一些深度信息,然后恢复原始大小。


3
其实没有:没有大小或方向(或方向,但我没有真正处理)的限制,因此您可以在背景中放置一个瓶子,在前景中放置一个罐子,然后罐子会更大比瓶子。
Charles Menguy 2012年

我还检查了瓶子的宽高比是否与罐子非常相似,因此也不是一个真正的选择。
Charles Menguy 2012年

标签比例(是商标)是相同的。因此,如果(更大)的瓶子在图片上稍远一些,则其大小将与罐子完全相同。
littleadv 2012年

3
多解释一点。假设罐在z = 0,瓶子在z = -100。由于瓶子远远落后,它看起来会更小。但是,如果我知道瓶子在z = -100处并且罐在z = 0处,那么如果将两者均转换为z = 0,则可以计算出罐/瓶子的预期尺寸。所以现在它们处于相同的深度,因此我可以根据规模做出决策。
莎拉德

2
这只是一个评论,而不是一个答案,但与上面的120票以上的评论即答案相比,它更像是一个答案。
Fattie

22

嗯,实际上,我认为我正在做某事(这是有史以来最有趣的问题-因此,即使找到了可接受的答案,也不能继续尝试找到“完美”的答案是可耻的)。 。

一旦找到徽标,麻烦就解决了一半。然后你只需要搞清楚之间有什么区别周围的标志。另外,我们想做的越少越好。我认为这实际上是这容易的部分...

什么周围的标志?对于罐子,我们可以看到金属,尽管受到照明的影响,金属的基本颜色也不会改变。只要知道标签的角度,我们就可以知道标签正上方的角度,因此我们正在研究它们之间的区别:

在此,徽标上方和下方的内容完全是深色的,颜色一致。在这方面相对容易。

在这里,上方和下方是浅色,但颜色仍然一致。它是全银的,实际上,全银的金属和银色通常看起来很少见。此外,它的外壳较细,滑行距离已经确定的红色足够近,因此您可以在其整个长度上追踪其形状,以计算出可以视为罐中金属环的百分比。确实,您只需要在罐子上的任何一小部分就可以知道它是罐子的一部分,但是您仍然需要找到一个平衡点,以确保它不仅仅是一个空瓶,里面没有金属。

最后,棘手的一个。但是并不是那么棘手,一旦我们只按照我们在红色包装上方(和下方)看到的内容进行操作即可。它是透明的,这意味着它将显示其背后的内容。很好,因为它后面的东西的颜色不可能像罐子里的银色圆形金属一样一致。它后面可能有许多不同的事物,这将告诉我们它是一个空的(或装有透明液体的)瓶子,或者颜色是一致的,这可能意味着它装满了液体,或者该瓶子只是在瓶子的前面纯色。我们正在使用最接近顶部和底部的颜色,正确颜色出现在正确位置的机会相对较小。我们知道这是一个瓶子,因为它没有罐子的关键视觉元素,

(最后一个是我能找到的最大的空可口可乐瓶最好的-有趣的是瓶盖和戒​​指是黄色的,表明瓶盖的红色可能不应该依赖)

在罕见的情况下,即使在塑料被提取后,瓶子后面仍然有相似的银色阴影,或者瓶子以某种方式填充了相同的银色液体阴影,我们可以回过头来大致估算出银的形状-如我所提到的,是圆形的,并遵循罐的形状。但是,即使我在图像处理方面缺乏某些知识,听起来也很慢。更好的是,为什么不通过一次检查徽标的侧面以确保那里没有相同的银色来推断这一点呢?啊,但是如果罐子后面有相同的银色阴影怎么办?然后,我们确实确实必须更加注意形状,再次查看罐的顶部和底部。

取决于所有这些工具的完美程度,它可能会很慢,但是我想我的基本概念是首先检查最简单和最接近的事情。在努力确定其他元素的形状之前,先在已经匹配的形状(无论如何这似乎是最琐碎的部分)周围进行色差检查。要列出它,它去了:

  • 找到主要的吸引力(红色徽标背景,可能还有徽标本身的方向,尽管如果罐头掉了,则您需要专注于红色)
  • 验证形状和方向,再次通过非常独特的红色进行验证
  • 检查形状周围的颜色(因为它既快速又无痛)
  • 最后,如果需要,请验证主要吸引力周围的这些颜色的形状,以确保正确的圆度。

如果您无法做到这一点,则可能意味着罐子的顶部和底部被盖住了,而人类用来可靠地区分罐子和瓶子的唯一可能的方法就是闭塞和反射的就可以了,这将是一个难的战斗过程。但是,要走得更远,您可以使用其他答案中提到的半透明扫描技术,按照罐子/瓶子的角度检查更多类似瓶子的特征。

有趣的其他噩梦可能包括一个可以方便地坐在瓶子后面的罐子,其距离恰好使它的金属恰好显示在标签的上方和下方,只要您沿着红色的整个长度进行扫描,它仍然会失效。标签-实际上更多的是问题,因为您没有在可能的位置检测到罐子,而不是考虑实际上是在检测瓶子,包括偶然发现的罐子。在这种情况下,玻璃杯是空的!


作为免责声明,我对此问题没有经验,也从未考虑过图像处理,但是它很有趣,使我对它进行了深入的思考,在阅读了所有其他答案之后,我认为这可能是最简单最有效的方法来完成它。就我个人而言,我很高兴我实际上不必考虑对此编程!

编辑

MS油漆罐中的不良绘图 此外,请看一下我在MS Paint中所做的这张图……这绝对是可怕的,而且还很不完整,但是仅根据形状和颜色,您就可以猜出它可能是什么样的。从本质上讲,这些是唯一需要扫描的内容。当您看到非常独特的形状和如此紧密的颜色组合时,还可能是什么?我没有画的白色背景应该被认为是“任何不一致的地方”。如果背景透明,则可以覆盖几乎所有其他图像,您仍然可以看到它。


10
红色的特殊阴影主要是主观的,并且受照明和白平衡的影响很大。您可能会对这些更改有多少感到惊讶。例如,考虑一下这种棋盘错觉
章鱼

1
@Octopus发布的链接的更新:persci.mit.edu/gallery/checkershadow
帽子

感知错觉不会影响您的网络摄像头看到的内容(即代码获得的内容),只会影响人眼如何愚弄大脑。
Barny

17

我不了解OpenCV,但从逻辑上看问题,我认为您可以通过更改所要查找的图像(例如可口可乐)来区分瓶子和瓶子。您应该合并到罐子的顶部,以防可口可乐顶部有银衬,如果瓶装没有银衬。

但是很明显,在隐藏罐头顶部的情况下,该算法将失败,但是在这种情况下,即使是人类也无法区分两者(如果仅可见瓶/罐头的可口可乐部分)


1
我也有同样的想法,但是我认为罐子顶部的银色衬线会根据图片上罐子的角度发生巨大变化。它可以是直线或圆。也许他可以将两者用作参考?
亚历克西斯·杜弗雷诺伊

15

我认为我喜欢挑战,并想给出一个解决问题的答案。

  1. 提取徽标的特征(关键点,描述符,例如SIFT,SURF)
  2. 将这些点与徽标的模型图像进行匹配(使用Matcher,例如Br​​ute Force)
  3. 估计刚体的坐标(PnP问题-SolvePnP)
  4. 根据刚体估算盖的位置
  5. 进行反投影并计算瓶盖的图像像素位置(ROI)(我假设您具有相机的固有参数)
  6. 用一种方法检查盖子是否存在。如果在那里,那是瓶子

检测上限是另一个问题。它可以是复杂的,也可以是简单的。如果您是我,我只是简单地检查ROI中的颜色直方图即可。

如果我错了,请给出反馈。谢谢。


13

回答这个问题晚了几年。在过去的5年中,随着CNN的发展水平达到极限,我现在不会使用OpenCV来完成这项任务!(我知道您在问题中特别想要OpenCv功能)我觉得与OpenCV功能相比,诸如Faster-RCNN,YOLO,SSD等对象检测算法将以很大的优势解决这个问题。如果现在(6年后!)解决这个问题,我肯定会使用Faster-RCNN


5
OP表示有30张高分辨率图像,这可能不是训练ConvNets的最佳方案。它们不仅太少(甚至没有增加),高分辨率部分也会破坏ConvNets。
Kostas Mouratidis

11

我喜欢你的问题,无论是否是话题:P

有趣的一面;我刚刚完成了我的学位课程,涵盖了机器人技术和计算机视觉。我们这个学期的项目与您描述的项目非常相似。

我们必须开发一种机器人,该机器人使用Xbox Kinect在各种光照和环境条件下以任何方向检测可乐瓶和易拉罐。我们的解决方案包括在Hue通道上使用带通滤波器并结合霍夫圆变换。我们能够稍微限制环境(我们可以选择将机器人和Kinect传感器放置在何处以及如何放置),否则我们将使用SIFT或SURF转换。

您可以在有关主题的博客文章中了解我们的方法:)


2
有趣的项目,但仅适用于非常特定的设置。
Rui Marques 2014年

10

有许多用于识别对象的颜色描述符,下面的文章对它们进行了比较。当与SIFT或SURF结合使用时,它们特别强大。单独的SURF或SIFT在可乐可成像中不是很有用,因为它们不能识别很多兴趣点,您需要颜色信息来帮助您。我在项目中将BIC(边界/内部像素分类)与SURF一起使用,它在识别物体方面非常有用。

Web图像检索的颜色描述符:对比研究


10

深度学习

收集至少几百个包含可乐罐的图像,将它们周围的边框注释为肯定类别,包括可乐瓶和其他可乐产品将它们标记为否定类别以及随机对象。

除非您收集非常大的数据集,否则请对小数据集执行使用深度学习功能的技巧。理想情况下,将支持向量机(SVM)与深度神经网络结合使用。

将图像输入到之前训练有素的深度学习模型(例如GoogleNet)后,不要使用神经网络的决策(最终)层进行分类,而应使用先前层的数据作为特征来训练您的分类器。

OpenCV和Google Net:http//docs.opencv.org/trunk/d5/de7/tutorial_dnn_googlenet.html

OpenCV和SVM:http//docs.opencv.org/2.4/doc/tutorials/ml/introduction_to_svm/introduction_to_svm.html


9

您需要一个可以从经验中有机地学习和提高分类准确性的程序。

我建议进行深度学习,随着深度学习,这将成为一个琐碎的问题。

您可以在Tensorflow上重新训练Inception v3模型:

如何为新类别重新训练Inception的最终层

在这种情况下,您将训练卷积神经网络将对象分类为可口可乐或不可以。


2
热狗还是不热狗?
YellowPillow

6

作为所有这些不错的解决方案的替代,您可以训练自己的分类器,并使您的应用程序对错误的鲁棒性。例如,您可以使用Haar Training,提供目标的大量正负图像。

仅提取罐头可能有用,并且可以与透明物体的检测结合使用。


3

MVTec有一个名为HALCON的计算机视觉软件包,其演示可以为您提供良好的算法思想。您可以在演示模式下运行许多与您的问题类似的示例,然后查看代码中的运算符,并查看如何从现有的OpenCV运算符实现它们。

我已经使用此软件包为此类问题快速构建了复杂算法的原型,然后找到了如何使用现有的OpenCV功能来实现它们。特别是对于您的情况,您可以尝试在OpenCV中实现嵌入在操作符find_scaled_shape_model中的功能。一些操作员指出了有关算法实现的科学论文,该论文可以帮助发现如何在OpenCV中执行类似的操作。希望这可以帮助...


0

如果您对它的实时性感兴趣,那么您需要添加一个预处理过滤器,以确定使用重型材料进行扫描的内容。一个好的快速,非常实时的预处理过滤器,可以让您扫描到更可能是可口可乐之前的东西,而不是在进行更多琐碎事情之前是这样的:在图像中搜索最大的补丁颜色sqrt(pow(red,2) + pow(blue,2) + pow(green,2))与可口可乐罐具有一定的公差。从非常严格的颜色公差开始,然后逐步降低到更宽的颜色公差。然后,当您的机器人用完指定的时间来处理当前帧时,它会将当前找到的瓶子用于您的目的。请注意,您必须调整中的RGB颜色sqrt(pow(red,2) + pow(blue,2) + pow(green,2))才能使它们正确。

此外,这似乎很愚蠢,但是您确定在-oFast编译C代码时是否打开了编译器优化功能?


0

也许晚了很多年,但是仍然需要尝试一种理论。

红色徽标区域的边界矩形与瓶子/罐的整体尺寸的比率不同。如果是Can,则应为1:1,而瓶子(带或不带盖)的瓶子将有所不同。这应该使区分两者变得容易。

更新:由于罐子和瓶子的尺寸不同,徽标区域的水平曲率会有所不同。如果您的机器人需要拿起罐子/瓶子,并且您可以据此决定抓地力,这可能特别有用。


-1

我要寻找的第一件事是颜色,例如RED,当在图像中进行红眼检测时,要检测到一定的颜色范围,考虑周围区域并考虑其周围区域的特性,例如与另一只眼睛的距离在图像中确实可见。

1:第一个特征是颜色,而红色占主导地位。在检测到可口可乐红色后,有几个有趣的项目1A:该红色区域有多大(是否有足够的数量来确定真罐头-10像素可能不够),1B:是否包含标签的颜色-“可口可乐”或波浪形。1B1:是否有足够的可能性考虑它是否是标签。

项目1是一种捷径-如果图像中不存在鼻子,请进行预处理-继续。

因此,在这种情况下,我便可以利用图像的这一部分,开始从所讨论的区域中稍微放大一下-基本上看一下周围的区域/边缘...

2:在1中给定了上面的图像区域ID-验证所涉物品的周围点[边缘]。A:顶部或底部有罐子-银吗?B:瓶子可能看起来是透明的,但玻璃桌子也可能是透明的-是否有玻璃桌子/架子或透明的区域-如果这样的话,可能会出现多种情况。瓶子可能带有红色的盖子,可能没有,但是它应该具有瓶子顶部/螺纹螺钉的形状或盖子。C:即使A和B失败,也仍然可以是部分罐头。部分罐头的情况更为复杂,因为部分罐头/部分罐头看起来可能相同,因此需要对红色区域边缘进行更多的测量处理到边缘..小瓶可能大小相似..

3:经过上面的分析之后,我才开始看字母和波浪徽标-因为我可以将搜索方向定向到单词中,因为您可能没有全部文本,因为您没有全部可以,该波将在文本的某些点处对齐(距离方向),因此我可以搜索该概率,并知道在距离x处该点应存在哪些字母。


-9

这是我从事的旧项目。MAP图像非常容易与javascript一起使用。我为您提供对象,您阅读并知道如何使用它。我们不需要JQuery和其他系统来使用MAP图像。

    //Copyright Cherif yahiaoui, by ELEBAN.FR

//variables de flottement.
var myInstOne = null;
var globalize = null;

var eleban_preload_images = function (name, imgs, url){
try{
    var oThis = this;
    this.images = new Array();
    this.imageshover = new Array();
    this.imagesNames = new Array(imgs.split(";"));


        for(var i=0; i < this.imagesNames[0].length; i++){
            this.images[i] = new Image();
            this.imageshover[i] = new Image();
        }

    this.url = url;

    this.GetAbsoluteurl = function () {

    var img = new Image(); img.src = url;
    url = img.src; img = null; 
        this.url = url; 

    };

    this.Preload = function () {

        for(var i=0; i < this.imagesNames[0].length; i++){
            this.images[i].src = this.url+("btn-"+this.imagesNames[0][i]+".png");
            this.imageshover[i].src = this.url+("btn-"+this.imagesNames[0][i]+"-hover.png");
        }

    };
    this.GetAbsoluteurl();
    this.Preload();
}
finally {return;}
}

var g_preloaderhover = new eleban_preload_images("loaderhover","menu;malette;reservation;cabine;facebook;map;amis","./images/");


//variable arret flottement
var g_stopflo = false;

var myObjfloater = function(name, idname, itop, differ ) {
var oThis = this; // création d'une référence vers l'objet courant
this.name = name;
this.id =idname;
this.xstep= 0.3;
this.itime = 30;
this.obj = null;
this.y = itop;
this.yadd = 0;
this.up = true;
this.pause = false;
this.differ = differ;
this.coordsimage = null;
this.objimg = null;
this.initimages = false;
this.compteur = 0;
this.over = false;
this.timeoutstop = null;
try{
this.initimage = function(){
var img = this.obj.getElementsByTagName('img')[0];
this.coordsimage = new Array(img.width, img.height);
this.objimg = img;
this.initimages = true;
};


this.myMethod = function() {
if(!g_stopflo){
    if(this.differ != 0){ 
this.differ=this.differ-0.1; 
}else{

if(this.obj){
if(this.over == false){
    this.yadd=this.yadd+0.1; this.itime = this.itime + 10;
this.obj.style.visibility = "hidden";
this.y = ((this.up)? this.y - this.yadd : this.y + this.yadd);
this.obj.style.marginTop = this.y +"%" ;
this.obj.style.visibility = "visible";

if (this.yadd > this.xstep){ 
    this.up = (this.up)? false : true;
    this.yadd = -0.1; this.itime=180;
}
}
}else{
    if (document){
        if(document.getElementById) {
         this.obj = document.getElementById(this.id); 
        //this.y = this.obj.offsetTop;
        }else{
        if(document.getElementByTagName) { this.obj = document.getElementByTagName(this.id); this.y = this.obj.offsetTop;}
        }

    }
}
}
this.timeoutstop=setTimeout(function() { oThis.myMethod(); }, this.itime);
}    
};

this.callDelayed = function() {
    // utilisation de la référence vers l'objet
if(!g_stopflo){
    this.timeoutstop=setTimeout(function() { oThis.myMethod(); }, this.itime);
}
};
}
finally {return;}
};

// special creation des zones AREA
function eleban_createallarea(){
try{
var measur = new Array("w", "h");
measur["w"] = new Array(330,570,185,300,115,390,225);
measur["h"] = new Array(460,570,295,450,100,190,115);
var ititle = new Array("Voir les menus  et nos suggestions","Repas &agrave; emporter","R&eacute;servation d&rsquo;une table","Nous contacter","Nous rejoindre sur FaceBook","Calculer votre trajet","liste des amis");
var ihref = new Array("menus.html","emporter.html","reservation.html","contact.html","likebox.html","google.html","amis.html");
var b_map = new Array(0,1,2,3,4,5,6);
b_map[0] = "71,32,240,32,249,43,289,352,280,366,102,385,90,371,51,38";
b_map[1] = "66,52,95,14,129,56,115,91,100,93,112,273,128,284,122,366,176,343,193,296,191,194,147,189,145,166,201,111,199,84,545,105,532,354,509,388,412,478,32,401,77,383,87,375,82,286,95,269,94,221,24,195,11,165,9,120,89,123,89,94,78,92,77,92,77,93,75,93,77,93,76,93,79,92";
b_map[2] = "19,25,169,38,173,112,161,113,105,103,90,125,91,262,121,269,124,281,96,293,62,289,49,281,56,268,83,264,84,121,71,98,16,90";
b_map[3] = "60,0,216,1,226,20,225,403,168,421,42,410,45,10";
b_map[4] = "31,7,72,10,82,18,88,45,88,71,76,81,29,80,17,68,16,18";
b_map[5] = "91,40,141,38,178,27,184,4,211,5,223,24,240,23,386,135,229,121,103,180,6,156,49,94";
b_map[6] = "6,32,69,18,79,6,118,7,141,2,149,10,211,17,202,28,209,30,189,62,195,70,178,74,180,90,164,90,154,107,68,101,34,104,34,98,18,97,28,84,15,84,30,65";

if (document.getElementById){
for (var i=0; i<b_map.length;i++){
var obj = document.getElementById("pc_menu"+i);
    if(obj){
    var ct = '<img class=\"pc_menu\" src=\"'+g_preloaderhover.images[i].src+'\" alt=\"\" width=\"'+measur["w"][i]+'\" height=\"'+measur["h"][i]+'\" usemap=\"#MAP_INDEX'+i+'\" \/>';
    ct+='<map name=\"MAP_INDEX'+i+'\">';
    ct+='<area shape=\"poly\" coords=\"'+b_map[i]+'\" title=\"'+ititle[i]+'\" href=\"'+ihref[i]+'\" \/>';
    ct+='<\/map>';
    obj.innerHTML = ct;
    }
}
}
}
finally {return;}
}

//preload, creation et gestion de tous les evenements


var image_resizer = function(g_layer){


    b_org_elm = new Array("w",  "h");
    b_org_elm["w"] = new Array(330,570,185,300,115,390,225);
    b_org_elm["h"] = new Array(460,570,295,450,100,190,115);

    b_map = new Array(0,1,2,3,4,5,6);
    b_map[0] = new Array(71,32,240,32,249,43,289,352,280,366,102,385,90,371,51,38);
    b_map[1] = new Array(66,52,95,14,129,56,115,91,100,93,112,273,128,284,122,366,176,343,193,296,191,194,147,189,145,166,201,111,199,84,545,105,532,354,509,388,412,478,32,401,77,383,87,375,82,286,95,269,94,221,24,195,11,165,9,120,89,123,89,94,78,92,77,92,77,93,75,93,77,93,76,93,79,92);
    b_map[2] = new Array(19,25,169,38,173,112,161,113,105,103,90,125,91,262,121,269,124,281,96,293,62,289,49,281,56,268,83,264,84,121,71,98,16,90);
    b_map[3] = new Array(60,0,216,1,226,20,225,403,168,421,42,410,45,10);
    b_map[4] = new Array(31,6,70,10,78,18,84,23,88,44,88,70,78,80,75,81,33,82,23,76,18,69,16,22,21,13);
    b_map[5] = new Array(91,40,141,38,178,27,184,4,211,5,223,24,240,23,386,135,229,121,103,180,6,156,49,94);
    b_map[6] = new Array(6,32,69,18,79,6,118,7,141,2,149,10,211,17,202,28,209,30,189,62,195,70,178,74,180,90,164,90,154,107,68,101,34,104,34,98,18,97,28,84,15,84,30,65);


    b_layer = g_layer;

//gere mouseover
    this.mouseover = function(e){
        if (!e) var e = window.event;
        var tg = (window.event) ? e.srcElement : e.target
            if (tg.nodeName){
                if(tg.nodeName == "AREA"){
                var divpar = (tg.parentNode)? tg.parentNode.parentNode : tg.parentElement.parentElement;
                    if (divpar){
                        if(divpar.nodeName == "DIV"){
                            var iiobjimg = divpar.getElementsByTagName('img');
                                if (iiobjimg){
                                    ii = parseInt(divpar.id.substring(divpar.id.length-1,divpar.id.length));
                                    iiobjimg[0].src = g_preloaderhover.imageshover[ii].src;
                                }
                        }
                    }
                }
            }
    };

//gere mouseout
    this.mouseout = function(e){
        if (!e) var e = window.event;
        tg = (window.event) ? e.srcElement : e.target
            if (tg.nodeName){
                if(tg.nodeName == "AREA"){
                divpar = (tg.parentNode)? tg.parentNode.parentNode : tg.parentElement.parentElement;
                    if (divpar){
                        if(divpar.nodeName == "DIV"){
                            var iiobjimg = divpar.getElementsByTagName('img');
                                if (iiobjimg){
                                    ii = parseInt(divpar.id.substring(divpar.id.length-1,divpar.id.length));
                                    iiobjimg[0].src = g_preloaderhover.images[ii].src;
                                }
                        }
                    }
                }
            }
    };

//ajout evenements entree sortie à la page web lors du chargement de la page
    this.init = function () {

        for(var i=0; i<b_org_elm["w"].length;i++){
            w = document.getElementById("pc_menu"+i).offsetWidth;
            h = document.getElementById("pc_menu"+i).offsetHeight;

            xa = w/parseFloat(b_org_elm["w"][i]);
            ya = h/parseFloat(b_org_elm["h"][i]);

            area = document.getElementById("pc_menu"+i).getElementsByTagName('area')[0];

            b_map2 = area.coords.split(",");
            yswitch = true;
                for(m=0; m<b_map2.length;m++){
                b_map2[m] = Math.round(parseFloat(b_map[i][m]) * ((yswitch)? xa: ya));
                yswitch = (yswitch)? false :  true;
                }
            area.coords = b_map2.join(',');
        }
    }; 


    this.resize = function () {
    clearTimeout(myInstOne.timeoutstop);
    g_stopflo=true;

    globalize.init();
    g_stopflo=false;
    myInstOne.obj = null;
    myInstOne.callDelayed();
    };


    nar = document.getElementsByTagName('area').length;

        for(var i=0; i<nar;i++){
            var elem = document.getElementsByTagName('area')[i];
            if (elem.addEventListener){
                    elem.addEventListener("onmouseover",this.mouseover,true);
                elem.addEventListener("onmouseout",this.mouseout,true);
            }else if (elem.attachEvent) {
                    elem.attachEvent("onmouseover", this.mouseover);
                    elem.attachEvent("onmouseout", this.mouseout);
            }else{
                    elem["onmouseover"] = this.mouseover;
                    elem["onmouseout"] = this.mouseout;
            }
        }

            window.onresize = this.resize;
        window.onmouseover = this.mouseover;
        window.onmouseout = this.mouseout;
}


//permet de temporiser et éviter les erreurs de chargement des objets
function temporise_Init(Lastdiv){
if(document.getElementById){
    if(document.getElementById(Lastdiv)){

    eleban_createallarea();

    myInstOne = new myObjfloater('b_menumap11', 'pc_menu1', 1, 0);

    globalize = new image_resizer(document.getElementById('pc_redim'));
    globalize.init();
        globalize.resize();



    }else{
    setTimeout(temporise_Init(Lastdiv), 30);
    }
}
}


window.onload = function () {
temporise_Init("pc_bandeau");
}

2
这是做什么的?
user8408080
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.