【使用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)

替换:在这里插入图片描述

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

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

相关文章

kr 第三阶段(一)16 位汇编

为什么要学习 16 位汇编&#xff1f; 16 位汇编包含了大部分 32 位汇编的知识点。有助于在学习内核的两种模式。 实模式&#xff1a;访问真实的物理内存保护模式&#xff1a;访问虚拟内存 有助于提升调试能力&#xff0c;调试命令与 OllyDbg 和 WinDebug 通用。可以学习实现反…

linux性能分析(三)查看系统的性能指标

一 查看系统的性能指标 这里简略的将系统监控指标分为&#xff1a;cpu、memory、disk、network、os 五大类强调&#xff1a; 这五类命令的输出与/proc虚目录下的文件信息强相关说明&#xff1a; 后续专门讲解这五大类的系统命令,尽可能全面罗列每个工具的使用场景补充&#x…

基于LSTM的天气预测 - 时间序列预测 计算机竞赛

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 机器学习大数据分析项目 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng-senior/po…

React函数式写法和类式写法的区别(以一个计数器功能为例子)

函数式写法更加简洁和函数式编程思维导向&#xff0c;适用于无状态、UI纯粹的组件&#xff0c;且可以使用Hooks处理副作用。而类式写法适用于有内部状态、生命周期方法和复杂交互逻辑的组件&#xff0c;提供了更多的灵活性和控制力。 文章目录 一、计数器功能演示 1.函数式写法…

uniapp(uncloud) 使用生态开发接口详情5(云公共模块)

1.uniCloud官网 云对象中云公共模块: 网站: https://uniapp.dcloud.net.cn/uniCloud/cf-common.html // 官网介绍 cloudfunctions├─common // 云函数公用模块目录| └─hello-common // 云函数公用模块| ├─package.json| └─index.js // 公用模块代码&#xff0…

R语言处理数量很大(千万级及以上)的数据时的拆分策略-案例一

主要是用R中的groupb_by和 summarise分组查询和统计数据量

vlookup函数踩坑(wps)

使用wps的朋友看过来 vlookup函数踩坑&#xff0c;vlookup&#xff08;查找值&#xff0c;查找范围&#xff0c;返回值的索引&#xff0c;精确查找or模糊查找&#xff09; 我们要查找的数据的那一列&#xff0c;必须是查找范围的第一列&#xff01; 案例&#xff0c;看下面的…

Jmeter用于接口测试中,关联如何实现

Jmeter用于接口测试时&#xff0c;后一个接口经常需要用到前一次接口返回的结果&#xff0c;应该如何获取前一次请求的结果值&#xff0c;应用于后一个接口呢&#xff0c;拿一个登录的例子来说明如何获取。 1、打开jmeter, 使用的3.3的版本&#xff0c;新建一个测试计划&#…

【MyBatis】mvc模式以及Mapper文件中的namespace以及ORM思想

目录 什么是MVC三层架构&#xff0c;初步了解&#xff1f; namespace的作用是什么&#xff1f; Mapper文件中的namespace&#xff1f; ORM思想&#xff08;对象关系映射思想&#xff09; 其中提供了一套映射规则和API 什么是MVC三层架构&#xff0c;初步了解&#xff1f; 三…

以哈战争或推动美国「屠杀」比特币

摘要&#xff1a;加密货币公司急于在国会对以色列被袭引发的审查之前采取行动。 作者&#xff1a;JASPER GOODMAN 来源&#xff1a;POLITICO 编译&#xff1a;WEEX Exchange 原文标题&#xff1a;How Elizabeth Warren is making Hamas crypto’s latest Washington woe 编者按…

TODO Vue typescript forEach的bug,需要再核實

forEach 一個string[]&#xff0c;只有最後一個匹配條件有效&#xff0c;其它條件無效。 所以&#xff0c;只能替換成普通的for循環。 console.log(taskList)// for (const _task of taskList.value) {// if (_task invoiceSendEmail) {// form.value.invoiceSendEmail…

RESR开发

REST简介 REST(Representation State Transfer),表现形式状态转换 优点 隐藏资源的访问行为&#xff0c;无法通过地址得知对方资源是何种操作书写简化 按照REST风格访问资源时使用行为动作区分对资源进行了何种操作。 根据REST风格对资源进行访问是RESTFUL REST风格 Restfu…

java如何导入导出excel

在Java中&#xff0c;可以使用多种方式导入和导出Excel文件。下面将详细介绍几种常见的方法及其实现步骤&#xff1a; 1. Apache POI库&#xff1a; Apache POI是一个开源的Java库&#xff0c;提供了许多类和方法用于处理Microsoft Office格式的文档&#xff0c;包括Excel文件…

图像处理软件Photoshop 2023 mac新增功能 ps 2023中文版

​Photoshop 2023 mac是一款功能强大、易用且灵活的图像编辑软件&#xff0c;旨在满足专业设计师和摄影师的需求。无论您是处理照片、制作图形还是进行艺术创作&#xff0c;Photoshop 2023 都能为您提供丰富的工具和效果&#xff0c;帮助您实现创意想法。Photoshop还支持多种文…

Protocols/面向协议编程, DependencyInjection/依赖式注入 的使用

1. Protocols 定义实现协议&#xff0c;面向协议编码 1.1 创建面向协议实例 ProtocolsBootcamp.swift import SwiftUI/// 颜色样式协议 protocol ColorThemeProtocol {var primary: Color { get }var secondary: Color { get }var tertiary: Color { get } }struct DefaultCol…

Springboot结合Mockito写单元测试实践和原理

文章目录 前言一、使用最佳实践使用场景SpyBean失效场景解决Mock失效的问题避免FactoryBean的实现方式使用MockBean&#xff0c;但是要指定name 个人推荐 二、原理1. MockBean2.SpyBean方法调用 总结 前言 相信看我博客的都是javaer&#xff0c;工作中一般都是使用Springboot框…

已经有多人中招,不要被AI换脸技术骗了!

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

汽车辅助系统

目录 一&#xff0c;项目描述 二&#xff0c;项目 功能 三&#xff0c;代码实现 &#xff08;1&#xff09;倒车雷达 (2)AD&#xff08;对 雨滴与光敏电阻传感器进行AD采集&#xff09; &#xff08;3&#xff09;雨刷 &#xff08;4&#xff09;灯光 最后总结&#xf…

vue 树状结构数据渲染 (java 处理 list ->树状)

树状结构 Element ui https://element.eleme.cn/#/zh-CN/component/tree <el-tree :data"data" :props"defaultProps" node-click"handleNodeClick"></el-tree><script>export default {data() {return {data: [{label: 一级…

功能集成,不占空间,同为科技TOWE嵌入式桌面PDU超级插座

随着现代社会人们生活水平的不断提高&#xff0c;消费者对生活质量有着越来越高的期望。生活中&#xff0c;各式各样的电气设备为我们的生活带来了便利&#xff0c;在安装使用这些用电器时&#xff0c;需要考虑电源插排插座的选择。传统的插排插座设计多暴露于空间之中&#xf…