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

2024-01-06

过去几年我做过的最有趣的项目之一是一个关于图像处理 https://en.wikipedia.org/wiki/Image_processing。目标是开发一个能够识别可口可乐的系统'cans'(请注意,我强调“罐头”这个词,您很快就会明白原因)。您可以看到下面的示例,其中可以识别罐头绿色矩形具有缩放和旋转功能。

项目的一些限制:

  • 背景可能非常嘈杂。
  • The can可以有任何scale or rotation甚至方向(在合理的范围内)。
  • 图像可能有一定程度的模糊性(轮廓可能不完全笔直)。
  • 图像中可能存在可口可乐瓶,算法应该只检测can!
  • 图像的亮度可能会有很大变化(因此您不能“过多”依赖颜色检测)。
  • The can可能部分隐藏在侧面或中间,也可能部分隐藏在瓶子后面。
  • 不可能有can图像中根本没有,在这种情况下,您必须什么也找不到并写一条消息来说明这一点。

所以你最终可能会遇到这样棘手的事情(在这种情况下我的算法完全失败):

我不久前做了这个项目,做起来很有趣,而且我有一个不错的实现。以下是我的实施的一些细节:

Language:使用 C++ 完成OpenCV http://opencv.org图书馆。

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

  1. Changing color domain from RGB to HSV http://en.wikipedia.org/wiki/HSL_and_HSV and filtering based on "red" hue, saturation above a certain threshold to avoid orange-like colors, and filtering of low value to avoid dark tones. The end result was a binary black and white image, where all white pixels would represent the pixels that match this threshold. Obviously there is still a lot of crap in the image, but this reduces the number of dimensions you have to work with. Binarized image
  2. 噪声过滤使用中值滤波(取所有邻居的中值像素值并用该值替换像素)来减少噪声。
  3. Using Canny Edge Detection Filter http://en.wikipedia.org/wiki/Canny_edge_detector to get the contours of all items after 2 precedent steps. Contour detection

算法:我为此任务选择的算法本身取自this https://rads.stackoverflow.com/amzn/click/com/0123725380关于特征提取的很棒的书,被称为广义霍夫变换 http://en.wikipedia.org/wiki/Generalised_Hough_transform(与常规霍夫变换非常不同)。它基本上说了几件事:

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

最后,你会得到一个投票热图,例如这里罐头轮廓的所有像素都会投票给它的重心,所以你会在与中心,并将在热图中看到一个峰值,如下所示:

一旦你有了这个,一个简单的基于阈值的启发式方法就可以给你中心像素的位置,从中你可以导出比例和旋转,然后在它周围绘制你的小矩形(最终的比例和旋转系数显然与你的原始模板)。理论上至少...

Results:现在,虽然这种方法在基本情况下有效,但在某些领域严重缺乏:

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

你能帮助我提高我的水平吗specific算法,使用独家 OpenCV特点,解决四具体提到的问题?

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


另一种方法是使用提取特征(关键点)尺度不变特征变换 https://en.wikipedia.org/wiki/Scale-invariant_feature_transform(SIFT)或加速的强大功能 https://en.wikipedia.org/wiki/Speeded_up_robust_features (SURF).

你可以找到一个不错的OpenCV代码示例在Java, C++, and Python在本页面:Features2D + 单应性查找已知对象 https://docs.opencv.org/3.4/d7/dff/tutorial_feature_homography.html

两种算法对于缩放和旋转都是不变的。由于它们与功能一起使用,因此您还可以处理闭塞 http://en.wikipedia.org/wiki/Ambient_occlusion(只要有足够的关键点可见)。

图片来源:教程示例

SIFT 的处理需要几百毫秒,SURF 稍快一些,但不适合实时应用。 ORB 使用 FAST,其旋转不变性较弱。

原始论文

  • SURF:加速的强大功能 http://www.vision.ee.ethz.ch/%7Esurf/eccv06.pdf
  • 独特的图像特征 来自尺度不变关键点 http://www.cs.ubc.ca/%7Elowe/papers/ijcv04.pdf
  • ORB:SIFT 或 SURF 的有效替代方案 http://www.willowgarage.com/sites/default/files/orb_final.pdf
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

图像处理:“可口可乐罐”识别的算法改进 的相关文章

  • 创建 DirectoryEntry 实例以供测试使用

    我正在尝试创建 DirectoryEntry 的实例 以便可以使用它来测试将传递 DirectoryEntry 的一些代码 然而 尽管进行了很多尝试 我还是找不到实例化 DE 并初始化它的 PropertyCollection 的方法 我有
  • Signalr 在生产服务器中总是陷入长轮询

    当我在服务器中托管应用程序时 它会检查服务器端事件并始终回退到长轮询 服务器托管环境为Windows Server 2012 R1和IIS 7 5 无论如何 我们是否可以解决这个问题 https cloud githubuserconten
  • 模板类的不明确多重继承

    我有一个真实的情况 可以总结为以下示例 template lt typename ListenerType gt struct Notifier void add listener ListenerType struct TimeListe
  • 在 Xamarin Android 中将图像从 URL 异步加载到 ImageView 中

    我有一个包含多个项目的 ListView 列表中的每个项目都应该有一个与之关联的图像 我创建了一个数组适配器来保存每个列表项并具有我希望加载的图像的 url 我正在尝试使用 Web 请求异步加载图像 并设置图像并在加载后在视图中更新它 但视
  • C++ 求二维数组每一行的最大值

    我已经设法用这个找到我的二维数组的每一行的最小值 void findLowest int A Cm int n int m int min A 0 0 for int i 0 i lt n i for int j 0 j lt m j if
  • fgets() 和 Ctrl+D,三次才能结束?

    I don t understand why I need press Ctrl D for three times to send the EOF In addition if I press Enter then it only too
  • SSH 主机密钥指纹与模式 C# WinSCP 不匹配

    我尝试通过 WinSCP 使用 C 连接到 FTPS 服务器 但收到此错误 SSH 主机密钥指纹 与模式不匹配 经过大量研究 我相信这与密钥的长度有关 当使用 服务器和协议信息 下的界面进行连接时 我从 WinSCP 获得的密钥是xx xx
  • 为什么 POSIX 允许在只读模式下超出现有文件结尾 (fseek) 进行搜索

    为什么寻找文件结尾很有用 为什么 POSIX 让我们像示例中那样在以只读方式打开的文件中进行查找 c http en cppreference com w c io fseek http en cppreference com w c io
  • 将字符串从非托管代码传递到托管

    我在将字符串从非托管代码传递到托管代码时遇到问题 在我的非托管类中 非托管类 cpp 我有一个来自托管代码的函数指针 TESTCALLBACK FUNCTION testCbFunc TESTCALLBACK FUNCTION 接受一个字符
  • 使用 Google Analytics API 在 C# 中显示信息

    我一整天都在寻找一个好的解决方案 但谷歌发展得太快了 我找不到有效的解决方案 我想做的是 我有一个 Web 应用程序 它有一个管理部分 用户需要登录才能查看信息 在本节中 我想显示来自 GA 的一些数据 例如某些特定网址的综合浏览量 因为我
  • HttpClient 像浏览器一样请求

    当我通过 HttpClient 类调用网站 www livescore com 时 我总是收到错误 500 可能服务器阻止了来自 HttpClient 的请求 1 还有其他方法可以从网页获取html吗 2 如何设置标题来获取html内容 当
  • 基于范围的 for 循环中的未命名循环变量?

    有没有什么方法可以不在基于范围的 for 循环中 使用 循环变量 同时也避免编译器发出有关未使用它的警告 对于上下文 我正在尝试执行以下操作 我启用了 将警告视为错误 并且我不想进行像通过在某处毫无意义地提及变量来强制 使用 变量这样的黑客
  • 为什么模板不能位于外部“C”块内?

    这是一个后续问题一个答案 https stackoverflow com questions 4866433 is it possible to typedef a pointer to extern c function type wit
  • 线程、进程和 Application.Exit()

    我的应用程序由主消息循环 GUI 和线程 Task Factory 组成 在线程中我调用一些第三方应用程序var p new Process 但是当我调用Application Exit 在消息循环中 我可以看到在线程中启动的进程仍在内存中
  • 初始化变量的不同方式

    在 C 中初始化变量有多种方法 int z 3 与 int 相同z 3 Is int z z 3 same as int z z 3 您可以使用 int z z 3 Or just int z 3 Or int z 3 Or int z i
  • 像“1$”这样的位置参数如何与 printf() 一起使用?

    By man I find printf d width num and printf 2 1 d width num 是等价的 但在我看来 第二种风格应该与以下相同 printf d num width 然而通过测试似乎man是对的 为什
  • 窗体最大化时自动缩放子控件

    有没有办法在最大化屏幕或更改分辨率时使 Windows 窗体上的所有内容自动缩放 我发现手动缩放它是正确的 但是当切换分辨率时我每次都必须更改它 this AutoScaleDimensions new System Drawing Siz
  • C++ 成员函数中的“if (!this)”有多糟糕?

    如果我遇到旧代码if this return 在应用程序中 这种风险有多严重 它是一个危险的定时炸弹 需要立即在应用程序范围内进行搜索和销毁工作 还是更像是一种可以悄悄留在原处的代码气味 我不打算writing当然 执行此操作的代码 相反
  • 将 viewbag 从操作控制器传递到部分视图

    我有一个带有部分视图的 mvc 视图 控制器中有一个 ActionResult 方法 它将返回 PartialView 因此 我需要将 ViewBag 数据从 ActionResult 方法传递到 Partial View 这是我的控制器
  • 不同类型的指针可以互相分配吗?

    考虑到 T1 p1 T2 p2 我们可以将 p1 分配给 p2 或反之亦然吗 如果是这样 是否可以不使用强制转换来完成 或者我们必须使用强制转换 首先 让我们考虑不进行强制转换的分配 C 2018 6 5 16 1 1 列出了简单赋值的约束

随机推荐