【使用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.mzph.cn/news/114019.shtml

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

相关文章

[C语言]排序的大乱炖——喵喵的成长记

宝子&#xff0c;你不点个赞吗&#xff1f;不评个论吗&#xff1f;不收个藏吗&#xff1f; 最后的最后&#xff0c;关注我&#xff0c;关注我&#xff0c;关注我&#xff0c;你会看到更多有趣的博客哦&#xff01;&#xff01;&#xff01; 喵喵喵&#xff0c;你对我真的很重要…

【微服务 SpringCloud】实用篇 · Ribbon负载均衡

微服务&#xff08;4&#xff09; 文章目录 微服务&#xff08;4&#xff09;1. 负载均衡原理2. 源码跟踪1&#xff09;LoadBalancerIntercepor2&#xff09;LoadBalancerClient3&#xff09;负载均衡策略IRule4&#xff09;总结 3. 负载均衡策略3.1 负载均衡策略3.2 自定义负载…

C++前缀和算法的应用:向下取整数对和 原理源码测试用例

本文涉及的基础知识点 C算法&#xff1a;前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 题目 向下取整数对和 给你一个整数数组 nums &#xff0c;请你返回所有下标对 0 < i, j < nums.length 的 floor(nums[i] / nums[j]) 结果之和。由于答案可能会…

Kubeadm部署k8s集群 kuboard

目录 主机准备 主机配置 修改主机名&#xff08;三个节点分别执行&#xff09; 配置hosts&#xff08;所有节点&#xff09; 关闭防火墙、selinux、swap、dnsmasq(所有节点) 安装依赖包&#xff08;所有节点&#xff09; 系统参数设置(所有节点) 时间同步(所有节点) 配…

(转)c 多张图片生成avi视频

https://www.cnblogs.com/songhe364826110/p/7619949.html 修改了几个参数&#xff0c;可以生成视频了。下载主要是为了学习avi视频格式。最后编一个摄像头生成视频的程序。 本程序把标准avi 视频格式的各种数据结构自定义在文件头(JpegAVI.h)中&#xff0c;所以就不用去下载…

Java面试题-UDP\TCP\HTTP

UDP UDP特性 &#xff08;1&#xff09;UDP是无连接的&#xff1a;发送数据之前不需要像TCP一样建立连接&#xff0c;也不需要释放连接&#xff0c;所以减少了发送和接收数据的开销 &#xff08;2&#xff09;UDP 使用尽最大努力交付&#xff1a;即不保证可靠交付 &#xff0…

Java开发规范记录

不要使用 count(column)或 count(1)来替代 count(*)&#xff0c;count(*)是 SQL92 定义的 标准统计行数的语法&#xff0c;跟数据库无关&#xff0c;跟 NULL 和非 NULL 无关。 注意&#xff1a;count(*)会统计值为 NULL 的行&#xff0c;而 count(列名)不会统计此列为 NULL 值的…

高级深入--day35

反反爬虫相关机制 Some websites implement certain measures to prevent bots from crawling them, with varying degrees of sophistication. Getting around those measures can be difficult and tricky, and may sometimes require special infrastructure. Please consi…

Java面向对象(基础)--package和import关键字的使用

文章目录 一、package关键字的使用1. 说明2. 包的作用3. JDK中主要的包 二、import关键字的使用 一、package关键字的使用 1. 说明 package:包package用于指明该文件中定义的类、接口等结构所在的包。语法格式 举例&#xff1a;pack1\pack2\PackageTest.java package pack1.…

Xshell+screen解决ssh连接 服务器掉线的问题

Linux screen命令解决SSH远程服务器训练代码断开连接后运行中断_linux screen ssh-CSDN博客 Linux命令之screen命令_linux screen_恒悦sunsite的博客-CSDN博客 使用教程&#xff1a; 这里粗略介绍一下 &#xff08;1&#xff09;xshell xftp&#xff08;xshell点这个&#…

Git合并某个分支上的某个提交

1. 首先&#xff0c;确保你当前所在的分支是你要合并分支的父分支。你可以使用以下命令切换到父分支&#xff1a; git checkout <父分支名称> 2. 确保你要合并的分支是可用的。你可以使用以下命令查看所有可用的分支&#xff1a; git branch -a 这将显示所有本地和远程…

组合数(递推版)的初始化

初始考虑为将第一列数和斜对角线上的数进行初始化。 橙色方块由两个绿色方块相加而来&#xff0c;一个为1&#xff0c;一个为0&#xff0c;所以斜对角线都为1&#xff0c;可以通过计算得来&#xff0c;不需要初始化&#xff0c;需要与码蹄集盒子与球 第二类Stirling数&#xf…

Sobel算子详解及例程

Sobel算子是一种经典的边缘检测算子&#xff0c;被广泛应用于图像处理领域。它基于图像亮度的变化率来检测边缘的位置&#xff0c;主要通过计算图像中像素点的梯度来实现。 Sobel算子分为水平和垂直两个方向的算子&#xff0c;记作Gx和Gy。它们分别对图像进行水平和垂直方向的…

Go并发编程之一

一、前言 新年学新语言Go系列文章已经完结&#xff0c;用了最简单的例子去了解Go基础语法&#xff0c;但Go最牛B的是它对并发的友好支持&#xff0c;每一门语言都有它自己独特的优势&#xff0c;如Java适合大型工程化项目&#xff0c;Python适合做数据分析及运维脚本&#xff0…

✔ ★【备战实习(面经+项目+算法)】 10.21学习时间表(总计学习时间:5h30min)(算法刷题:7道)

✔ ★【备战实习&#xff08;面经项目算法&#xff09;】 坚持完成每天必做如何找到好工作1. 科学的学习方法&#xff08;专注&#xff01;效率&#xff01;记忆&#xff01;心流&#xff01;&#xff09;2. 每天认真完成必做项&#xff0c;踏实学习技术 认真完成每天必做&…

工具篇之Axure RP 10的使用

引言 最近在学习原型图&#xff0c;针对画原型图的工具&#xff0c;反复对比墨刀、Axure、xiaopiu后&#xff0c;最终选择Axure。 接下来&#xff0c;我便从Axure RP 10的下载、安装、中文字体、授权等几个方面&#xff0c;来介绍Axure。 一、背景 Axure是一款强大的原型设计…

分布式缓存选型比较:Memcache VS Redis

分布式缓存比较&#xff1a;Memcache VS Redis 1、Redis不仅仅支持简单的k/v类型的数据&#xff0c;同时还提供list&#xff0c;set&#xff0c;zset&#xff0c;hash等数据结构的存储。而memcache只支持简单数据类型&#xff0c;需要客户端自己处理复杂对象 2、Redis支持数据的…

华为OD技术面试-最短距离矩阵(动态规划、广度优先)

背景 记录2023-10-21 晚华为OD三面的手撕代码题&#xff0c;当时没做出来&#xff0c;给面试官说了我的想法&#xff0c;评价&#xff1a;解法复杂了&#xff0c;只是简单的动态规范 或 广度优先算法&#xff0c;事后找资料记录实现方式。 题目 腐烂的橘子 问题描述&#xff…

[SQL | MyBatis] MyBatis 简介

目录 一、MyBatis 简介 1、MyBatis 简介 2、工作流程 二、入门案例 1、准备工作 2、示例 三、Mapper 代理开发 1、问题简介 2、工作流程 3、注意事项 4、测试 四、核心配置文件 mybatis-config.xml 1、environment 2、typeAilases 五、基于 xml 的查询操作 1、…

EtherCAT主站SDO写报文抓包分析

0 工具准备 1.EtherCAT主站 2.EtherCAT从站&#xff08;本文使用步进电机驱动器&#xff09; 3.Wireshark1 抓包分析 1.1 报文总览 本文设置从站1的对象字典&#xff0c;设置对象字典主索引为0x2000&#xff0c;子索引为0x00&#xff0c;设置值为1500。主站通过发送SDO写报文…