翻译:陈之炎
校对:张一然、林夕本文约4400字,建议阅读10分钟本文为大家系统地介绍了OpenCV官方教程。

目标

在这里将寻求以下问题的答案:

  • 如何遍历图像的各个像素?

  • OpenCV的矩阵值是如何存储的?

  • 如何衡量算法的性能?

  • 什么是查找表,为什么要使用查找表?

测试案例

首先来考虑一个简单的减色方法。利用C和C ++的无符号字符(unsigned char)数据类型来存储矩阵项,像素的一个通道可以具备256个不同的值。对于一幅三通道的图像来说,可以构造出多种色彩(色彩数量可达16,000,000种)。数量众多的颜色会给算法的性能带来沉重的负担。然而,有些时候,往往利用较少的色彩数便能够获得同样的结果。

在这种情况下,常见的做法是减少色彩空间(color space reduction)。这意味着,将色彩空间的当前值除以一个新的输入值,从而减少颜色的数量。例如:将零和九之间的每一个值设置为零,十和十九之间的值设置为十等等。

当一个UCHAR 值(无符号字符unsigned char -0到255之间的值)除以INT值之后,其结果也将是一个数据类型为char的值,并且相除之后的结果值只能为char数据类型的值,小数部分将被舍去。利用C和C ++的这一优势,对 UCHAR域的操作可以表示为:

简单的减色算法将该公式应用于图像矩阵中的每个像素,值得一提的是:我们进行了一次除法和一次乘法运算,这两种运算会耗费昂贵的系统开销。如果可能的话,可以用一些开销相对来说比较小的操作来取代它们,如一些减法, 加法或者一些简单的赋值运算操作。此外,需要注意的是,上述操作的输入值的数量是有限的,对于UCHAR数据类型,准确地来讲,输入值的数量为256。

对于较大的图像,则是通过使用查找表,将事先计算好所有可能的值在赋值阶段直接进行赋值操作。查找表是具有一个或多个维度的简单数组,对于给定的输入值对应一个确定的输出值。它的优势在于:无需进行计算,便能读取到结果。

测试例程(和下述代码示例)将执行以下操作:利用命令行参数传递读取图像(可以是彩色图像或灰度图像),对给定命令行参数的整数值进行减色。在OpenCV中,主要有三种方式遍历图像的每个像素。为了增加实验的趣味性,会利用这三种方式扫描图像,并打印出各自花费的时长。

可以在这里下载完整的源代码,或者查看OpenCV的cpp教程示例代码的核心部分。其基本用法是:

最后一个参数是可选项,除非加载的是给定的图像的灰度格式,否则默认使用BGR色彩空间。首先,需要做的第一件事是计算查找表。

首先,利用C ++的stringstream类将第三个命令行参数由文本格式转换为整数格式。然后,利用一个看似简单的公式计算查找表。此时,没有涉及到OpenCV的具体内容。

接下来的问题是如何测量时间?OpenCV提供了cv::getTickCount()和cv::getTickFrequency() 这两个简单的函数来实现时间的测量。第一个函数cv::getTickCount()返回返回某个事件(如启动系统)之后系统CPU 的嘀嗒(Tick)数量。第二个函数cv::getTickFrequency() 返回CPU每秒钟发出多少次嘀嗒声。有了这两个函数之后,便很容易测量出两个操作之间的时间间隔:

https://docs.opencv.org/4.5.2/db/de0/group__core__utils.html

如何在内存中存储图像矩阵?

在上一节Mat-基本图像容器教程中,讲到像素矩阵的大小取决于所使用的色彩系统。更准确地说,取决于所使用的色彩通道数。灰度图像的情况是这样的:

多通道图像的列包含许多子列,子列的数目即通道的数量。例如:BGR色彩系统图像的情况是这样的:

注意,在这里通道的顺序是相反的:在这里是BGR ,而不是RGB。因为在大多数情况下,内存足够大,可以一行接一行顺序存储,形成一个单一的长行,有助于加快扫描的速度。可以使用 cv::Mat::isContinuous()函数查询矩阵是否以这种方式存储。请继续阅读下一节中的示例。

cv::Mat::isContinuous()

https://docs.opencv.org/4.5.2/d3/d63/classcv_1_1Mat.html

最为有效的方法

通过经典C风格操作符(指针)的方式来获取数据是性能最好的方法,因此对于赋值我们推荐的最高效的方法是:

在这里,只需要获取每一行起始的指针,然后遍历到最后一行。在某些特殊情况下,像素矩阵以连续的方式存储,只需要一次“请求指针”的操作,便能一路到底遍历所有的像素。对于彩色图像有三个色彩通道,每一行需要遍历三次。

还有另一种方式:Mat 对象的数据成员data 会返回指向第一行、第一列的指针。如果这个指针为空,则这一对象中不存在有效的输入。利用这种简单的方法,可以检查图像是否成功加载。如果像素存储是连续的,我们可以用它来遍历所有的数据指针。如果是灰度图像, 代码应该是这样的:

上述两种方法会得出相同的结果。然而,这段代码阅读起来会困难得多。如果你有更高级的技术,它阅读起来会变得更加困难。此外,在实践中,得到的性能结果却是相同的(因为大多数现代编译器会自动对代码进行优化)。

迭代器(安全的)方法

在上述所讲的方法中,你要确保传入正确数量的uchar数据类型值,并跳过行与行之间的间隙,对于用户来说,迭代器方法(iterator method)被视为是一种更安全的方式, 因为它从用户那里接管了这些任务。利用迭代器方法,只需要找出图像矩阵的起始行和结尾行,从起始行开始迭代,直到到达结尾行。使用*运算符获取迭代器指向的值(在迭代器前添加该符号)。

对于彩色图像来说,每一列包含三个UCHAR数据项,可以将这三个数据项视为一个 UCHAR数据类型的短向量,在 OpenCV中,称之为 Vec3b。用简单的操作符[]访问第n个子列。需要记住的重点是:OpenCV的迭代器遍历这些列,并会自动跳到下一行。因此,在彩色图像的情况下,如果采用一个简单的UCHAR迭代器,只能访问到蓝色通道的值。

利用引用返回值计算即时地址

不推荐采用最后一种方法扫描图像。利用这种方法可以访问或修改图像中的随机像素,基本的用法是:指定需要访问元素所在的行数和列数。在前面所述的扫描方法中,需要指定数据类型,在这里同样如此,在自动查找之前,需要手动指定使用什么数据类型。你可以在以下源代码的灰度图像的情况下观察这一点(用到了+ cv::Mat::at() 函数)

该函数根据输入的数据类型和坐标,计算出查询项的地址,然后返回这个地址的引用值。当get 这个引用值时,会获得一个常量,当set 这个引用值,它是一个非常量。为了安全起见,仅在调试模式*,可以检查输入坐标是否有效,是否确实存在。如果不是在调试模式下,会有标准错误输出流的错误提示。相比于正式发布模式,二者唯一的区别是:对于图像的每一个元素,你将获得一个新的行指针,用于我们使用 C 运算符 [] 获取列元素的内容。

如果需要使用该方法对图像做多次查找时,输入数据类型和坐标的操作会相当麻烦和费时。为解决这一问题,OpenCV添加了 cv::Mat_ 数据类型,它与Mat类似,但额外需要在定义时通过要查看的数据矩阵的内容来指定数据类型,但好处是你可以使用()操作符快速访问矩阵值。更好的是,Mat和cv::Mat数据类型之间的可以很方便的进行转换。在上述示例中,可以看到这个函数在彩色图像中的应用。然而,需要注意的是:cv::Mat::at函数中已经包含了相同的操作(具有相同的运行速度)。它只是一个偷懒的编程技巧。

cv::Mat_

https://docs.opencv.org/4.5.2/df/dfc/classcv_1_1Mat__.html

cv::Mat::at

https://docs.opencv.org/4.5.2/d3/d63/classcv_1_1Mat.html

核心功能

这是在图像中修改查找表的一个额外奖励的方法。在图像处理中, 用户常常会希望将给定的图像值修改为其他值。OpenCV提供一个函数,利用这个函数,无需写入图像的扫描逻辑,便可修改图像的像素值。在这里,用到核心模块的cv::LUT() 函数。首先,创建一个Mat类型的查找表:

cv::LUT()

https://docs.opencv.org/4.5.2/d2/de8/group__core__array.html

然后调用函数,(I是输入图像, J是输出):

性能差异对比

编译并运行程序以获得最佳结果。为使差别更加明晰,我用了一个相当大(2560 X 1600)的彩色图像。此处介绍的性能适用于彩色图像. 为了得到更准确的结果,我对上百次函数调用的结果做了平均。

可以得出以下结论:尽可能使用(而不是彻底改造已有函数)OpenCV已有的函数。LUT函数是最快的方法,因为OpenCV库可以通过英特尔线程构建模块启用多线程。然而,如果需要编写一个简单的图像扫描方法可选择指针方法,迭代器是一个更加安全的选择,但是速度相对来说要慢一些。在调试模式下,使用引用返回值访问方法扫描全图的代价最高;在正式发布模式下,可能会优于迭代方法,但它以牺牲迭代器的安全特性为代价。

最后,可以观看YouTube频道上发布的程序运行视频。

https://www.youtube.com/watch?v=fB3AN5fjgwc

编辑:王菁

校对:林亦霖

下一小节:1.3 矩阵的掩膜操作

往期回顾:

独家|OpenCV 1.1 Mat - 基本图像容器(附链接)

译者简介

陈之炎,北京交通大学通信与控制工程专业毕业,获得工学硕士学位,历任长城计算机软件与系统公司工程师,大唐微电子公司工程师,现任北京吾译超群科技有限公司技术支持。目前从事智能化翻译教学系统的运营和维护,在人工智能深度学习和自然语言处理(NLP)方面积累有一定的经验。业余时间喜爱翻译创作,翻译作品主要有:IEC-ISO 7816、伊拉克石油工程项目、新财税主义宣言等等,其中中译英作品“新财税主义宣言”在GLOBAL TIMES正式发表。能够利用业余时间加入到THU 数据派平台的翻译志愿者小组,希望能和大家一起交流分享,共同进步

翻译组招募信息

工作内容:需要一颗细致的心,将选取好的外文文章翻译成流畅的中文。如果你是数据科学/统计学/计算机类的留学生,或在海外从事相关工作,或对自己外语水平有信心的朋友欢迎加入翻译小组。

你能得到:定期的翻译培训提高志愿者的翻译水平,提高对于数据科学前沿的认知,海外的朋友可以和国内技术应用发展保持联系,THU数据派产学研的背景为志愿者带来好的发展机遇。

其他福利:来自于名企的数据科学工作者,北大清华以及海外等名校学生他们都将成为你在翻译小组的伙伴。

点击文末“阅读原文”加入数据派团队~

转载须知

如需转载,请在开篇显著位置注明作者和出处(转自:数据派ID:DatapiTHU),并在文章结尾放置数据派醒目二维码。有原创标识文章,请发送【文章名称-待授权公众号名称及ID】至联系邮箱,申请白名单授权并按要求编辑。

发布后请将链接反馈至联系邮箱(见下方)。未经许可的转载以及改编者,我们将依法追究其法律责任。

点击“阅读原文”拥抱组织

独家|OpenCV 1.2 如何用OpenCV扫描图像、查找表和测量时间(附链接)相关推荐

  1. 独家 | Bamboolib:你所见过的最有用的Python库之一(附链接)

    作者:Ismael Araujo 翻译:王可汗 校对:欧阳锦本文约3200字,建议阅读5分钟 本文介绍了Python数据分析的一个利器--Bamboolib,它无需编码技能,能够自动生成pandas代 ...

  2. 独家 | 数据科学家对可复用Python代码的实用管理方法(附链接)

    作者:Matthew Mayo, KDnuggets翻译:殷之涵校对:欧阳锦本文约3000字,建议阅读5分钟本文为大家介绍了四种关于复用Python代码的管理方法,以提高代码的效率及可读性等. 标签: ...

  3. 独家 | 神经网络的对抗性攻击:快速梯度符号方法的探索(附链接)

    作者:Patrycja Jenkner 翻译:陈之炎 校对:欧阳锦 本文约2300字,建议阅读8分钟 本文将尝试一种非常流行的攻击:快速梯度符号方法,来证明神经网络的安全漏洞. 标签:对抗性攻击,神经 ...

  4. 独家 | 解析Tansformer模型—理解GPT-3, BERT和T5背后的模型(附链接)

    作者:Dale Markowitz 翻译:王可汗 校对:和中华 本文约3800字,建议阅读5分钟 本文为大家介绍自然语言处理中当下最流行的语言模型--Transformer模型. 标签:自然语言处理 ...

  5. 独家 | 人工神经网络中发现了人类大脑拥有的多模态神经元(附链接)

    作者:Gabriel Goh, Chelsea Voss, Daniela Amodei, Shan Carter, Michael Petrov, Justin Jay Wang, Nick Cam ...

  6. 独家 | 送你12个关于数据科学学习的关键提示(附链接)

    来源:Artinspiring/Dreamstime.com 翻译:国相洁 校对:丁楠雅 本文约3000字,建议阅读6分钟. 本文为你介绍了作为数据科学家需要掌握沟通能力和其它软技能方面知识. 小结: ...

  7. 独家 | 一份数据工程师必备的学习资源,干货满满(附链接)

    作者:PRANAV DAR 翻译:张玲 校对:车前子 本文约6500字,建议阅读15分钟. 本文首先详细介绍了数据工程的职责.与数据科学家之间的差别以及其不同的工作角色,然后重点列出了很多与核心技能相 ...

  8. 独家 | 带你认识几种最流行的Python编辑器/IDEs(附链接)

    作者:By Gregory Piatetsky 格雷戈里·皮亚特斯基,KDnuggets. 翻译:赵雪尧 校对:丁楠雅 本文约1200字,建议阅读5+分钟. 我们投票选出了几种最流行的IDEs和编辑器 ...

  9. 独家 | 盘点9个适用所有学科的R数据可视化包(附链接)

    作者:Asha Hill 翻译:王雨桐 校对:蒋雨畅 本文约1900字,建议阅读8分钟. 本文将简要盘点R中常用的9个可视化包,并通过简要介绍包的特点和相关案例来帮助读者深入理解可视化包. 如果最近浏 ...

最新文章

  1. CPU访问计算机各组件周期
  2. Tomcat项目部署
  3. python 实现感知器(一)
  4. matlab数字滤波器设计函数汇总(转载)
  5. 10篇写给Git初学者的最佳教程
  6. linux sed工具,玩转Linux - 神级工具 sed awk
  7. 力扣-150 逆波兰表达式求值
  8. Leetcode 刷题笔记(三十) ——动态规划篇之子序列问题:回文
  9. 软件系统设计-12-软件设计体系结构概述
  10. Java能用来做什么的?
  11. 数据耦合的代码例子c语言,代码耦合的处理
  12. 腾讯开发者登录不上去
  13. 计算机网络能进国企吗,毕业之后想进国企?选这几个专业,录取几率更大,没毕业就签合同...
  14. 软考之软件设计师——数据库技术基础
  15. 【opencv】Camshift目标跟踪
  16. 发力小程序社交电商,礼物说完成C1轮1亿人民币融资
  17. 基于组合权重优化的风格中性多因子选股策略_数量化专题之五十七
  18. AS400相关资料整理(初学者)
  19. 数据化分析简单实例——销售预测
  20. 当工作迷茫的时候该怎么办?

热门文章

  1. 7-27 冒泡法排序 (C语言)
  2. 过滤驱动加密文件(代码)
  3. mysql跟踪和日志
  4. 如何解决MySQL连接超时关闭
  5. 艾伟也谈项目管理,IT项目管理的六种错误思维
  6. ASA防火墙与路由器R直连且直连能ping通
  7. Delphi 与 DirectX 之 DelphiX(89): TDIB.DrawAlphaMask();
  8. Delphi Access violations 问题的解决之道
  9. 卡尔曼滤波 -- 从推导到应用(一) 转有关键思想 K增益是最小 贺一佳博士
  10. Python字符串中含有某子字符串的个数