【使用OpenCV进行目标分割与计数的代码实例详解】
文章目录
- 概要
- 实例一:硬币分割计数
- 实例二:玉米粒分割计数
概要
在当今数字图像处理领域,图像分割技术是一项至关重要的任务。图像分割旨在将图像中的不同目标或区域准确地分开,为计算机视觉、图像识别和机器学习等领域提供了坚实的基础。在图像分割的广泛应用中,二值化、形态学预处理、距离变换以及分水岭算法等技术被广泛探讨和应用。
首先,二值化技术通过将灰度图像转化为黑白图像,为分割算法提供了清晰的背景和前景。其次,形态学预处理通过腐蚀、膨胀等操作,清除噪声、连接物体,为后续处理提供了更加准确的图像。接着,距离变换技术能够量化地描述图像中各个像素点与目标的距离关系,为图像分析提供了重要依据。最后,分水岭算法则是一种高度智能的分割技术,通过模拟水流形成分割边界,解决了复杂目标重叠和交叉的挑战。
实例一:硬币分割计数
导入必要的库:
from skimage.feature import peak_local_max
from skimage.morphology import watershed
from scipy import ndimage
import numpy as np
import argparse
import imutils
import cv2
加载并预处理图像:
image = cv2.imread("1.jpg")
shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)
cv2.imshow("Input", image)
这里使用了均值迁移滤波(Mean Shift Filtering)来平滑图像,使得图像中的区域更加集中,有助于后续的阈值处理。
将图像转换为灰度图,然后进行二值化处理:
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("Thresh", thresh)
这里使用了Otsu的阈值处理方法,将灰度图转换为二值图。
计算距离变换并找到峰值:
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, indices=False, min_distance=10, labels=thresh)
这一步计算了二值化图像的距离变换(Euclidean Distance Transform),然后找到了距离图中的峰值点。
应用分水岭算法进行图像分割:
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
labels = watershed(-D, markers, mask=thresh)
这里使用了分水岭算法,通过标记(markers)和掩码(mask)将图像分割成不同的区域。
分割结果的后处理:
for label in np.unique(labels):# if the label is zero, we are examining the 'background'# so simply ignore itif label == 0:continue# otherwise, allocate memory for the label region and draw# it on the maskmask = np.zeros(gray.shape, dtype="uint8")mask[labels == label] = 255# detect contours in the mask and grab the largest onecnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)c = max(cnts, key=cv2.contourArea)# draw a circle enclosing the object((x, y), r) = cv2.minEnclosingCircle(c)cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2)cv2.putText(image, "{}".format(label), (int(x) - 10, int(y)),cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
在这个循环中,对分水岭算法得到的每个区域进行处理,找到每个区域的轮廓,然后用圆圈标注出物体的轮廓,并在标注中显示区域的标签。
显示最终的分割结果:
cv2.imshow("Output", image)cv2.waitKey(0)cv2.destroyAllWindows()
最终,代码将显示带有分割结果的原始图像。
这段代码演示了一个完整的图像分割流程,包括图像预处理、距离变换、分水岭算法的应用,以及对分割结果的后处理和可视化。
全部代码:
# import the necessary packages
from skimage.feature import peak_local_max
from scipy import ndimage
import numpy as np
import argparse
import imutils
import cv2
from skimage.morphology import watershed
# load the image and perform pyramid mean shift filtering
# to aid the thresholding step
image = cv2.imread("img.png")
shifted = cv2.pyrMeanShiftFiltering(image, 21, 51)
cv2.imshow("Input", image)# convert the mean shift image to grayscale, then apply
# Otsu's thresholding
gray = cv2.cvtColor(shifted, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
cv2.imshow("Thresh", thresh)# compute the exact Euclidean distance from every binary
# pixel to the nearest zero pixel, then find peaks in this
# distance map
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, indices=False, min_distance=10,labels=thresh)# perform a connected component analysis on the local peaks,
# using 8-connectivity, then appy the Watershed algorithm
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))# loop over the unique labels returned by the Watershed
# algorithm
for label in np.unique(labels):# if the label is zero, we are examining the 'background'# so simply ignore itif label == 0:continue# otherwise, allocate memory for the label region and draw# it on the maskmask = np.zeros(gray.shape, dtype="uint8")mask[labels == label] = 255# detect contours in the mask and grab the largest onecnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cnts = imutils.grab_contours(cnts)c = max(cnts, key=cv2.contourArea)# draw a circle enclosing the object((x, y), r) = cv2.minEnclosingCircle(c)cv2.circle(image, (int(x), int(y)), int(r), (0, 255, 0), 2)cv2.putText(image, "{}".format(label), (int(x) - 10, int(y)),cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)# show the output image
cv2.imshow("Output", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
使用时候将图片放在同级目录,修改文件名字即可:
img.png,11行修改即可。
硬币图片自己随便找,复制图像截屏使用都可以:
使用结果:
三张图片:
注意:
导入库函数的部分,这个skimage库函数的没有,需要下载全部名字。
在环境下载库函数
pip install scikit-image -i https://pypi.tuna.tsinghua.edu.cn/simple
如果导入成功,但是运行报错:
D:\anaconda\envs\yolov5\python.exe E:\yolo项目\Opencv-project-main\Opencv-project-main\CVZone\光流\11.py
Traceback (most recent call last):File "E:\yolo项目\Opencv-project-main\Opencv-project-main\CVZone\光流\11.py", line 26, in <module>localMax = peak_local_max(D, indices=False, min_distance=10,
TypeError: peak_local_max() got an unexpected keyword argument 'indices'Process finished with exit code 1
说明使用的peak_local_max函数的参数中含有indices,但该函数在较新的版本中已经没有该参数了。
这可能是由于scikit-image库版本过高导致的。检查scikit-image库版本是否为0.17.2或更高版本,如果是,可以将该库回退到0.16.2版本:
pip install scikit-image==0.16.2 -i https://pypi.tuna.tsinghua.edu.cn/simple
如果依然想要使用最新的scikit-image库,将indices参数删除并改用默认值即可,例如:
localMax = peak_local_max(D, min_distance=10,threshold_abs=threshold)
这样可以避免indices参数引起的错误。
实例二:玉米粒分割计数
导入必要的库:
import numpy as np
import cv2
from matplotlib import pyplot as plt
读取图像并进行灰度化处理:
img = cv2.imread('5.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
二值化处理:
ret, thresh = cv2.threshold(gray, 245, 255, cv2.THRESH_BINARY)
这一步将灰度图像转换为二值图像,其中灰度值大于等于245的像素被设为255(白色),小于245的像素被设为0(黑色)。
图像膨胀:
k = cv2.getStructuringElement(cv2.MORPH_RECT, (13, 13))
dilate = cv2.dilate(thresh, k, iterations=3)
通过膨胀操作,将二值图像中的物体区域扩大,便于后续处理。
距离变换:
cv2.bitwise_not(dilate, dilate)
dist_transform = cv2.distanceTransform(dilate, cv2.DIST_L2, 3)
dist = cv2.normalize(dist_transform, dist_transform, 0, 1.0, cv2.NORM_MINMAX)
这一步计算了图像中每个像素点到最近的背景像素的距离,得到了距离变换图。在这个图像中,物体的中心部分距离背景较远,而边缘部分距离背景较近。
二值化距离变换图:
dist = cv2.convertScaleAbs(dist)
ret2, morph = cv2.threshold(dist, 0.99, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
这一步将距离变换图二值化,得到了分割后的图像。
形态学开运算:
k2 = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 5))
sure_fg = cv2.morphologyEx(morph, cv2.MORPH_OPEN, k2, iterations=1)
这一步通过形态学开运算去除小的噪点,保留大的物体区域。
寻找轮廓并标注:
thresh, contours, hierarchy = cv2.findContours(sure_fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for i in range(0, len(contours)):(x, y, w, h) = cv2.boundingRect(contours[i])cv2.circle(img, (x + int(w / 2), y + int(h / 2)), 20, (0, 0, 255), -1, cv2.LINE_AA)cv2.putText(img, str(i + 1), (x + int(w / 2) - 15, y + int(h / 2) + 5), font, 0.8, (0, 255, 0), 2)
这一步使用cv2.findContours函数找到图像中的轮廓,然后绘制圆圈和文本标注在图像上,表示找到的物体区域。
显示和保存结果:
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
最后,通过cv2.imshow显示处理后的图像。
全部代码:
import numpy as np
import cv2
from matplotlib import pyplot as pltfont = cv2.FONT_HERSHEY_SIMPLEXimg = cv2.imread('img_2.png')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 245, 255, cv2.THRESH_BINARY)
cv2.imshow("threshold", thresh)k = cv2.getStructuringElement(cv2.MORPH_RECT, (13, 13))
dilate = cv2.dilate(thresh, k, iterations=3)
cv2.imshow("dilate", dilate)cv2.bitwise_not(dilate, dilate)
dist_transform = cv2.distanceTransform(dilate, cv2.DIST_L2, 3)
dist = cv2.normalize(dist_transform, dist_transform, 0, 1.0, cv2.NORM_MINMAX)
cv2.imshow("distance", dist)
cv2.imwrite("dis.jpg", dist)# dist = np.uint8(dist)
dist = cv2.convertScaleAbs(dist)
ret2, morph = cv2.threshold(dist, 0.99, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# ret2, morph = cv2.threshold(dist,0,255,cv2.THRESH_BINARY_INV)
cv2.imshow("morph", morph)k2 = cv2.getStructuringElement(cv2.MORPH_RECT, (11, 5))
sure_fg = cv2.morphologyEx(morph, cv2.MORPH_OPEN, k2, iterations=1) # 形态开运算cv2.imshow("result", sure_fg)thresh, contours, hierarchy = cv2.findContours(sure_fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for i in range(0, len(contours)):(x, y, w, h) = cv2.boundingRect(contours[i])# cv2.drawContours(img,contours,i,(0,255,0),5)cv2.circle(img, (x + int(w / 2), y + int(h / 2)), 20, (0, 0, 255), -1, cv2.LINE_AA)cv2.putText(img, str(i + 1), (x + int(w / 2) - 15, y + int(h / 2) + 5), font, 0.8, (0, 255, 0), 2)cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
原图:
结果:
opencv版本不适配可能报错:
D:\anaconda\envs\yolov5\python.exe E:\yolo项目\Opencv-project-main\Opencv-project-main\CVZone\光流\22.py
Traceback (most recent call last):File "E:\yolo项目\Opencv-project-main\Opencv-project-main\CVZone\光流\22.py", line 33, in <module>thresh, contours, hierarchy = cv2.findContours(sure_fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
ValueError: not enough values to unpack (expected 3, got 2)Process finished with exit code 1
解决办法:
降低版本参考:
降低版本参考:
替换:
contours, _ = cv2.findContours(sure_fg, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
替换:
相关文章:

【使用OpenCV进行目标分割与计数的代码实例详解】
文章目录 概要实例一:硬币分割计数实例二:玉米粒分割计数 概要 在当今数字图像处理领域,图像分割技术是一项至关重要的任务。图像分割旨在将图像中的不同目标或区域准确地分开,为计算机视觉、图像识别和机器学习等领域提供了坚实…...

npm ERR! exited with error code: 128
1.遇到的问题 报错信息:npm ERR! E:\tools\Gitt\Git\cmd\git.EXE ls-remote -h -t https://github.com/nhn/raphael.git npm ERR! npm ERR! fatal: unable to access https://github.com/nhn/raphael.git/: OpenSSL SSL_read: Connection was reset, errno 10054 …...
Spark---数据输出
1. 输出为Python对象 collect算子:将RDD各个分区内的数据,统一收集到Driver中,形成一个List对象 reduce算子:对RDD数据集按照传入的逻辑进行聚合 take算子:取RDD的前N个元素,组合成list返回给你 count…...

虹科干货 | Redis Enterprise 自动分层技术:大数据集高性能解决方案
文章来源:虹科云科技 阅读原文:https://mp.weixin.qq.com/s/5ik-WLHwEmPn42f1FissQw 越来越多的应用程序依赖于庞大的数据集合,而这些应用程序必须快速响应。借助自动分层,Redis Enterprise 7.2 帮助开发人员轻松创建超快的应用程…...

信息系统项目管理师第四版学习笔记——组织通用治理
组织战略 组织战略是组织高质量发展的总体谋略,是组织相关干系方就其发展达成一致认识的重要基础。组织战略是指组织针对其发展进行的全局性、长远性、纲领性目标的策划和选择。 战略目标是组织在一定的战略期内总体发展的总水平和总任务。它决定了组织在该战略期…...
安装zip扩展(PHP)
记录一次 安装zip扩展的最优方案 (备注 网上以及Ai提供的很乱不能很快解决) 首先搜索zip包 yum search zip选择自己合适的php版本 比如我的php是7.4.33的 我就用php74-php-pecl-zip 如果没有的话 先添加软件源 sudo yum install epel-release sudo yu…...

深度学习YOLOv4环境配置
软件安装 1、什么是CUDA CUDA(ComputeUnified Device Architecture),是显卡厂商NVIDIA推出的运算平台。 CUDA是一种由NVIDIA推出的通用并行计算架构,该架构使GPU能够解决复杂的计算问题。 CUDA下载地址为CUDA Toolkit Archive | NVIDIA Developer 版…...
从0到1:云计算工程师入门指南
华为职业认证覆盖ICT全领域,致力于提供领先的人才培养体系和认证标准培养数字化时代的新型ICT人才,构建良性的ICT人才生态。 华为推荐职业认证进阶路线 根据ICT从业者的学习和进阶需求华为职业认证分为工程师级别、高级工程师级别和专家级别三个认证等…...

【微信小程序】6天精准入门(第3天:小程序flex布局、轮播图组件及mock运用以及综合案例)附源码
一、flex布局 布局的传统解决方案,基于[盒状模型],依赖display属性 position属性 float属性 1、什么是flex布局? Flex是Flexible Box的缩写,意为”弹性布局”,用来为盒状模型提供最大的灵活性。任何一个容器都可以…...

Hadoop3教程(二十五):Yarn的多队列调度器使用案例
文章目录 (136)生产环境多队列创建&好处(137)容量调度器多队列提交案例如何创建多个队列如何向指定队列提交任务 (138)容量调度器任务优先级(139)公平调度器案例参考文献 &#…...
【SA8295P 源码分析 (四)】27 - QNX Ethernet MAC 驱动 之 emac_tx_thread_handler 数据发送线程 源码分析
【SA8295P 源码分析】27 - QNX Ethernet MAC 驱动 之 emac_tx_thread_handler 数据发送线程 源码分析 系列文章汇总见:《【SA8295P 源码分析 (四)】网络模块 文章链接汇总 - 持续更新中》 本文链接:《【SA8295P 源码分析 (四)】27 - QNX Ethernet MAC 驱动 之 emac_tx_thread…...

思维模型 上瘾模型(hook model)
本系列文章 主要是 分享 思维模型,涉及各个领域,重在提升认知。你到底是怎么上瘾(游戏/抖音)的?我们该如何“积极的上瘾”?让我们来一切揭晓这背后的秘密。 1 上瘾模型的应用 1.1上瘾模型的积极应用 1 学…...

中文编程开发语言工具编程实际案例:美发店会员管理系统软件编程实例
中文编程开发语言工具编程实际案例:美发店会员管理系统软件编程实例 中文编程开发语言工具编程实际案例:美发店会员管理系统软件编程实例。 软件功能: 1、系统设置:参数设定,账号及权限设置,系统初始化&a…...
【27】c++设计模式——>迭代器模式(1)
迭代器实现通常包含两个主要组件:迭代器和聚合对象,聚合对象一般是vector,list,set,map等,迭代器负责在聚合对象上进行遍历,并提供了一种统一的访问元素的方法。聚合对象用来存储,并…...
table的展开折叠按钮操作
按钮 <el-buttontype"info"plainicon"el-icon-sort"size"mini"click"toggleExpandAll">展开/折叠</el-button>table: default-expand-all“isExpandAll” <el-tablev-if"refreshTable"v-loading"loadi…...

计算机毕业设计 基于SpringBoot智慧养老中心管理系统的设计与实现 Javaweb项目 Java实战项目 前后端分离 文档报告 代码讲解 安装调试
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...
Android-Framework 不允许应用商场下载的应用安装,adb 、pm 可以正常安装
一、环境 高通865 Android10 二、情景 从framework层禁止应用商场下载的应用安装,adb 、pm 可以正常安装 三、代码实现 frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java -35,6 35,7 import android.os.Build;…...

面试 4
1、作用域 w3scholl中定义:作用域指的是您有权访问的变量集合。 作用域是指在程序中定义变量的区域,该位置决定了变量的生命周期。通俗理解,作用域就是变量与函数的可访问范围,即作用域控制着变量和函数的可见性和生命周期。 在…...

【AI视野·今日Robot 机器人论文速览 第五十五期】Mon, 16 Oct 2023
AI视野今日CS.Robotics 机器人学论文速览 Mon, 16 Oct 2023 Totally 27 papers 👉上期速览✈更多精彩请移步主页 Interesting: 📚***AcTExplore, 对于未知物体的主动触觉感知。基于强化学习自动探索物体的表面形貌,增量式重建。(from 马里兰…...
交换机基础(一)
一、物理接口 配置物理接口需要分别指定接口类型、框号、插槽号、交换机端口号。常见接口类型如表所示。 插槽号:插槽号是交换机模块号,非模块化交换机则不用标识插槽号或者使用0编号。 端口号:交换机端口总是从1开始。 接口类型 接口配置…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...

C++_核心编程_多态案例二-制作饮品
#include <iostream> #include <string> using namespace std;/*制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料 利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶*//*基类*/ class AbstractDr…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》
这段 Python 代码是一个完整的 知识库数据库操作模块,用于对本地知识库系统中的知识库进行增删改查(CRUD)操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 📘 一、整体功能概述 该模块…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill
视觉语言模型(Vision-Language Models, VLMs),为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展,机器人仍难以胜任复杂的长时程任务(如家具装配),主要受限于人…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...