基于opencv的大米计数统计(详细处理流程+代码)

在我每周的标准作业清单中,有一项是编写计算机视觉算法来计算该图像中米粒的数量:

  • 因此,当我的一个好朋友M给我发了一张纸上的扁豆照片(显然是受到上述转发的启发),请我帮他数一下谷物的数量时,它勾起了我怀旧的回忆。因此,我在我的旧硬盘上寻找很久以前编写的代码作为上述问题的参考解决方案。花了一些时间才找到他们。

  • 旧代码是用C 编写的,并使用现已过时的 OpenCV 1.x API。我当前的 PC 中不再安装旧的库版本,而且由于 Python 现在很流行,我决定使用最新的 OpenCV API 将逻辑移植到 Python 3 代码。

  • 在这篇文章中,我将演示实现上述解决方案的非常简单的步骤,解释所做出的一些算法选择、此处介绍的解决方案的一些替代方案和局限性。请注意,这是一个纯粹的计算机视觉算法解决方案。联系qq1309399183

简化问题

  • 图像中的像素可以取很大范围的值,甚至是代表相同或相似对象的像素。
  • 这给使用经典计算机视觉算法步骤解决图像理解任务带来了特殊的障碍。在我们的输入灰度图像中,值表示 8 位图像表示的各种灰度范围从 0 到255。
  • 将一种类型的对象与另一种类型的对象分开的值的边界并不总是清晰的,并且由于图像采集期间的照明条件,可能具有显着的范围重叠。标准方法是将值减少为几个不同的值,每个值代表有意义的对象。这称为分段。在这种情况下,图像中只有两种类型的区域。一个是属于米粒的像素组,另一个属于背景虚空的像素组。
  • 这是一种非常常见的特殊情况,称为二进制分割,即将输入灰度图像转换为纯黑白形式。纯白色描绘米粒的像素,纯黑色描绘普遍背景。一旦完成,问题就归结为仅仅计算图像中不同的纯白色物体的数量。有多种不同的方法可以实现上述目的。

在这里插入图片描述

正如您可能已经推断出的,这假设可能存在一个特定值,使得所有米粒像素都比该值更亮,而所有背景像素都更暗。为简单起见,我们认为本例中的值为 127(0-255 范围的中间)。但查看输出,我们确实意识到相当多的背景像素已被标记为白色,而许多米粒像素已被标记为黑色。现在的问题是,我们是否必须通过反复试验得出一个合适的阈值,或者是否有更结构化的方法来做到这一点?

thresh, output_binthresh = cv.threshold(input_rice, 127, 255, cv.THRESH_BINARY)
print("固定阈值", thresh)
cv.imshow("二进制阈值(固定)", output_binthresh)

在这里插入图片描述

使用 Otsu 方法进行二元阈值处理
Nobuyuki Otsu 的阈值选择方法(更广泛地称为Otsu 方法)通过最大化类间方差来统计计算合适的阈值。我们可以使用相同的方法来获得更好质量的二值分割结果。

thresh, output_otsuthresh = cv.threshold(input_rice, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
print("大津阈值", thresh)
cv.imshow("二进制阈值 (otsu)", output_otsuthresh)

在这里插入图片描述

  • 上面的结果肯定比以前干净,但仍然不够好。我们可以做得更好吗?答案是肯定的。
  • 如果我们返回并观察原始输入图像,我们可以看到整个图像的照明并不均匀。
  • 中心区域当然是最亮的。它向底部变暗,在某种程度上向顶部和角落也变暗。
  • 由于点光源,这是一种常见的情况。因此,整个图像上的单一阈值可能不是解决二值分割问题的最佳方法。

局部自适应阈值

改变照明的解决方案是需要确定仅适合图像的有限部分的不同阈值。局部自适应阈值的作用是根据像素周围有限矩形区域内像素阴影的分布,为每个像素找到统计上合适的阈值。这抵消了照明梯度。

output_adapthresh = cv.adaptiveThreshold(input_rice,255.0,cv.ADAPTIVE_THRESH_MEAN_C,cv.THRESH_BINARY,51,-20.0)
cv.imshow("自适应阈值", output_adapthresh)

矩形区域的大小是一个可调参数。有一些直观的过程可以让它正确。我们选择的区域大小为每个像素周围的 51X51 像素,大约是 512X512 图像尺寸的 10%。这似乎适用于我们的输入样本中的缓慢照明变化。对于快速变化的照明,需要较小的区域。
在这里插入图片描述

后期处理

即使使用局部自适应阈值,似乎可以很好地执行二进制分割,但我们在输出中仍然存在一些小问题。在应该有背景的地方有随机的明亮像素斑点。另外,有些谷物对象是连体的,这可能会导致计数结果出现偏差。我们需要清理分割的图像,使其更适合准确计数。

形态侵蚀

侵蚀是一种几何变换,旨在减少又名侵蚀前景形状。我们使用 5 像素矩形算子腐蚀图像。这有助于去除外围斑点。也可以分离出连体的颗粒物体。

kernel = np.ones((5,5),np.uint8)
output_erosion = cv.erode(output_adapthresh, kernel)
cv.imshow(“Morphological Erosion”, output_erosion)

由此产生的输出现在已完全准备好用于我们的计数算法。

数着谷物

正如我之前提到的,通过适当的简化和后处理,看似复杂的问题已简化为仅计算不同纯白色物体的数量。我将提出两种替代方案来完成同样的任务。

连接组件

label_image = output_erosion.copy()
label_count = 0
rows, cols = label_image.shape
for j in range(rows):for i in range(cols):pixel = label_image[j, i]if 255 == pixel:label_count += 1cv.floodFill(label_image, None, (i, j), label_count)
print("Number of foreground objects", label_count)
cv.imshow("Connected Components", label_image)

在这里插入图片描述

  • 上述逻辑首先系统地寻找白色像素,为它们分配一个唯一的标签 ID(1 到
    254),并递归地为所有相邻的白色像素分配相同的标签,直到覆盖并标记整个不同的对象。
  • 然后继续寻找下一个可用的白色像素 (255),并重复该过程,直到整个图像已处理完毕。分配的唯一标签的数量与不同的前景对象(即米粒)相同。
_, contours, _ = cv.findContours(output_erosion, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
output_contour = cv.cvtColor(input_rice, cv.COLOR_GRAY2BGR)
cv.drawContours(output_contour, contours, -1, (0, 0, 255), 2)
print(“Number of detected contours”, len(contours))
cv.imshow(“Contours”, output_contour)

在这里插入图片描述

  • 轮廓检测逻辑的工作原理是找到前景/背景边界像素,然后执行边界跟随逻辑,以链码的形式对外部轮廓形状进行编码.

  • 执行此操作直到覆盖所有前景对象的边界。检测到的独特外部轮廓的数量与米粒的数量相同。

局限性

上述算法在给定的输入图像上完成其工作,但仍然需要手动调整一些参数。相同的解决方案可能不足以概括到不同的输入或不同的照明条件下。此外,如果图像中颗粒的密度较高,即大多数颗粒彼此相邻或相互遮挡,则这也会严重失败。

最后,↓↓↓↓

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/411463.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

new Handler(getMainLooper())与new Handler()的区别

Handler 在Android中是一种消息处理机制。 new Handler(); 创建handler对象,常用在已经初始化了 Looper 的线程中调用这个构造函数(即非主线程),如果感觉不好理解,可以把Handler handler new Handler() 理解为常用在…

【C++】异常机制

异常 一、传统的处理错误的方式二、C异常概念三、异常的使用1. 异常的抛出和捕获(1)异常的抛出和匹配原则(2)在函数调用链中异常栈展开匹配原则 2. 异常的重新抛出3. 异常安全4. 异常规范 四、自定义异常体系五、C 标准库的异常体…

JVM工作原理与实战(十五):运行时数据区-程序计数器

专栏导航 JVM工作原理与实战 RabbitMQ入门指南 从零开始了解大数据 目录 专栏导航 前言 一、运行时数据区 二、程序计数器 总结 前言 JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供…

Cloudflare cdn 基本使用

个人版免费试用,一个邮箱账号只能缓存一个网站cdn。 地址:cloudflare.com 创建站点 在网站创建站点,填上你的域名 点击进入网站 缓存全局配置 可清除缓存,设置浏览器缓存时间 我设置了always online,防止服务器经常不稳定 缓…

电影《潜行》中说的蜜罐是什么(网络安全知识)

近期刘德华、彭于晏主演的电影《潜行》在网上掀起了轩然大波,电影中有提到网络蜜罐,这引起了很多观众的疑问,蜜罐到底是什么? 从字面意思上来看,蜜罐就是为黑客设下的诱饵。这是一种具有牺牲性质的计算机系统&#xff…

编程笔记 html5cssjs 045 网页布局

编程笔记 html5&css&js 045 网页布局 一、网页布局二、头部区域三、菜单导航区域三、内容区域四、不相等的列五、底部区域六、box-sizingbox-sizing 属性可以被用来调整这些表现:属性值content-boxborder-box 六、响应式网页布局小结 网页布局有很多种方式&…

Chatopera 云服务支持大语言模型对话(LLM),定制您的聊天机器人

2024 年,Chatopera 云服务继续不断完善,为开发者提供最好的定制聊天机器人的工具。在过去的一年,用户们反映最多的建议是 Chatopera 云服务内置大语言模型的对话,今天 Chatopera 云服务完成了产品升级,满足了这个诉求。…

Android Studi安卓读写NDEF智能海报源码

本示例使用的发卡器&#xff1a;https://item.taobao.com/item.htm?id615391857885&spma1z10.5-c.w4002-21818769070.11.1f60789ey1EsPH <?xml version"1.0" encoding"utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmln…

七陌API对接实战:外呼接口及通话记录推送

通过白码低代码开发平台对接七陌外呼接口&#xff0c;实现选择客户进行外呼&#xff0c;并保存通话记录的功能。 外呼接口实现&#xff1a; 官方接口文档&#xff1a;http://developer.7moor.com/v2docs/dialout/ 1、对接数据查询 向七陌商务索取到七陌用户中心账号密码&a…

Servlet 预览pdf

一、背景 上篇文章介绍了图片的预览&#xff0c;这篇我们介绍下 pdf 文件的预览&#xff0c;pdf 预览在实际开发中用的还是比较多的&#xff0c;比如很多文件协议、合同都是用pdf 格式&#xff0c;协议预览就需要我们做 pdf 预览了。 二、实操 其实在上篇文章最后已经说了常用…

django后台手机号加密存储

需求&#xff1a; 1 &#xff1a;员工在填写用户的手机号时&#xff0c;直接填写&#xff0c;在django后台中输入 2&#xff1a;当员工在后台确认要存储到数据库时&#xff0c;后台将会把手机号进行加密存储&#xff0c;当数据库被黑之后&#xff0c;手机号字段为加密字符 3&am…

新能源汽车智慧充电桩解决方案:智慧化综合管理与数字化高效运营

一、方案概述 TSINGSEE青犀&触角云新能源汽车智慧充电桩解决方案基于管理运营平台&#xff0c;覆盖业务与应用、数据传输与梳理、多端开发、搭建等模块&#xff0c;融合AI、5G、Wi-Fi 、移动支付等技术&#xff0c;实现充电基础设施由数字化向智能化演进&#xff0c;通过构…