树莓派4B_OpenCv学习笔记12:OpenCv颜色追踪_画出轨迹

今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi)

 本人所用树莓派4B 装载的系统与版本如下:

 版本可用命令 (lsb_release -a) 查询:

 Opencv 版本是4.5.1:

今日尝试使用倒叙的方式来学习Open'CV颜色追踪,尝试一种新的笔记写法吧......

代码是根据创乐博(MAKEROBO)的视频代码学习的 ,我只是查阅资料学习与解释

贴出的解释部分是由百度文心一言AI生成的...

文章提供测试代码讲解,整体代码贴出、测试效果图

目录

测试视频效果展示:

完整实例代码贴出:

代码小结:

创建掩膜/图像的膨胀腐蚀:

开运算 与 闭运算: 

掩膜(mask)上寻找各个轮廓(cnts)函数:

众多轮廓中(cnts)找到最大轮廓(C)函数:

计算轮廓(C)最小包围圆:

计算轮廓(C)的质心(center):

OpenCv画圆函数:

双端队列:

网上查阅资料贴出:


测试视频效果展示:

OpenCv颜色追踪

 

完整实例代码贴出:

这个代码例程实现了使用最小圆来圈出识别追踪到的颜色物体,并画出轨迹

颜色可以在代码中初始化的部分更改:(注意先将BGR转换为HSV色彩空间)

之后会详细解释其中的一些关键函数:

完整代码与解释如下:

# -*- coding: utf-8 -*-# 导入必要的库
from collections import deque  # 导入双端队列,用于存储物体的中心点  
import numpy as np             # 导入numpy库,用于数学和数组操作  
import argparse                # 导入argparse库,用于处理命令行参数 
import imutils
import cv2                     # 导入OpenCV库,用于视频和图像处理 ap = argparse.ArgumentParser() # 创建命令行参数解析器 
# 添加命令行参数:--video,可选的视频文件路径  
ap.add_argument("-v", "--video",help="path to the (optional) video file")
# 添加命令行参数:--buffer,双端队列的最大长度,默认为64  
ap.add_argument("-b", "--buffer", type=int, default=64,help="max buffer size")
# 解析命令行参数,并将结果存储在args字典中  
args = vars(ap.parse_args())# 定义HSV颜色空间中的颜色范围,用于追踪颜色物体
colorLower = (18, 100, 100)
colorUpper = (38, 255, 255)# 初始化双端队列,用于存储物体的中心点,最大长度为args["buffer"] 
pts = deque(maxlen=args["buffer"])# 如果没有提供视频文件,则使用默认的摄像头
if not args.get("video", False):camera = cv2.VideoCapture(0)
# 否则,使用提供的视频文件路径
else:camera = cv2.VideoCapture(args["video"])# 无限循环,读取和处理视频帧 
while True:(grabbed, frame) = camera.read() # 读取一帧图像 # 如果是视频文件,并且没有成功读取帧(即视频结束),则退出循环if args.get("video") and not grabbed:breakframe = imutils.resize(frame, width=600) # 调整帧的大小为600像素宽,保持其原始宽高比 hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)# 将帧从BGR颜色空间转换为HSV颜色空间 mask = cv2.inRange(hsv, colorLower, colorUpper)# 创建一个只包含指定颜色范围内的像素的掩码 # 对掩码进行腐蚀和膨胀操作,以减少噪声并平滑形状(开运算)mask = cv2.erode(mask, None, iterations=2)mask = cv2.dilate(mask, None, iterations=2)# 在掩码上查找轮廓cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2]center = None  # 初始化中心点变量  # 如果找到了轮廓  if len(cnts) > 0:c = max(cnts, key=cv2.contourArea)#找到最大的轮廓 ((x, y), radius) = cv2.minEnclosingCircle(c)#计算该轮廓的最小包围圆 M = cv2.moments(c)#计算轮廓的质心center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))## 如果最小包围圆的半径大于10像素,绘制圆和质心 if radius > 10:cv2.circle(frame, (int(x), int(y)), int(radius),(0, 255, 255), 2)cv2.circle(frame, center, 5, (0, 0, 255), -1)# 将质心添加到双端队列的左端 pts.appendleft(center)# 遍历双端队列中的点(除了第一个),绘制它们之间的线  for i in range(1, len(pts)):if pts[i - 1] is None or pts[i] is None:continue# 根据点在队列中的位置动态调整线的粗细thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5)cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness)cv2.imshow("Frame", frame)key = cv2.waitKey(1) & 0xFFif key == ord("q"):breakcamera.release()
cv2.destroyAllWindows()

 

代码小结:

在仔细研究代码后,个人将其分为以下步骤:

1、定义命令行解释器(这是用于选择视频源是文件还是摄像头)

2、定义HSV色彩空间范围(确定识别的目标颜色)

接下来是while True:循环中:

1、调整帧大小

2、掩膜 通过cv2.inRange()函数,指定一个HSV颜色空间中的颜色范围,

      从而确定并提取图像中特定颜色范围内的所有像素区域 

      之后对掩膜进行“开运算”  ——  腐蚀后立即膨胀

3、寻找掩膜的轮廓(可能不止一个,因为符合颜色的像素矩阵区域不止一个),

      并根据所有的轮廓,找到最大的那个Numpy矩阵 C

      再通过C计算出包裹它所需的最小包围圆

      计算C轮廓的质心,记录质心坐标到 center 变量

 4、如果最小包围圆半径大于10,就绘制圆与质心

 5、将质心添加到双端队列左端

       遍历双端队列中记录的点,绘制它们之间的线

       根据点在队列中的位置,动态调整线的粗细  

创建掩膜/图像的膨胀腐蚀:

相关图像处理之前文章中提到过,这里不多解释,但讲一下 开运算 与 闭运算:

文章地址贴出:

树莓派4B_OpenCv学习笔记6:OpenCv识别已知颜色_运用掩膜-CSDN博客 

树莓派4B_OpenCv学习笔记9:图片的腐蚀与膨胀-CSDN博客

 

开运算 与 闭运算: 

 在调用cv2.erode()之后立即调用cv2.dilate()(或相反的顺序)是一种常见的技术,称为开运算(opening)或闭运算(closing)。

开运算通常用于消除小的对象(在噪声中)

而闭运算则用于连接小的断裂或填充小的孔洞

代码中,先腐蚀后膨胀的组合会稍微平滑对象的形状并减少噪声。

 

掩膜(mask)上寻找各个轮廓(cnts)函数:

cv2.findContours 函数用于检测图像中的轮廓

实例代码中是这样使用的:

cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[-2]

cnts  接受到的数据类型是一个列表(list),其中每个元素都是一个NumPy数组(numpy.ndarray)。这些NumPy数组代表检测到的轮廓,每个轮廓由一系列的点(边界上的像素坐标)组成,这些点以(x, y)的形式表示。

# 假设 cnts 是一个包含两个轮廓的列表:

(实际可能不止一俩个轮廓、图片中掩膜包含颜色范围不止一个,根据像素点来的)

cnts = [

             array([[x1_1, y1_1], [x1_2, y1_2], ..., [x1_n, y1_n]]), # 第一个轮廓的点集      

             array([[x2_1, y2_1], [x2_2, y2_2], ..., [x2_m, y2_m]]) # 第二个轮廓的点集

            ]

传入参数解释:

  1. image:输入图像,通常是二值图像(即,黑白图像)。这是因为轮廓检测通常在二值图像上执行,其中白色区域表示目标对象,黑色区域表示背景。

    • 类型:numpy.ndarray
    • 示例:经过阈值处理或Canny边缘检测等步骤后得到的二值图像。
  2. mode:轮廓检索模式。它定义了轮廓检索的方式,例如是否只检索最外部的轮廓,是否建立轮廓的层次结构等。

    • 类型:整数
    • 取值:
      • cv2.RETR_EXTERNAL:只检索最外层的轮廓。
      • cv2.RETR_LIST:检索所有的轮廓,但不建立它们之间的层次关系。
      • cv2.RETR_CCOMP:检索所有的轮廓,并建立两个层次。外层的轮廓包含它们的内层轮廓(例如,如果一个白色对象在另一个白色对象内部,那么外部对象的轮廓和内部对象的轮廓都会被检索到)。
      • cv2.RETR_TREE:建立一个轮廓的层次结构。
  3. method:轮廓近似方法。它定义了轮廓点的近似方式。

    • 类型:整数
    • 取值:
      • cv2.CHAIN_APPROX_NONE:保存轮廓的所有点。
      • cv2.CHAIN_APPROX_SIMPLE:只保存轮廓的端点(即,曲线的开始和结束点)和拐点。
      • cv2.CHAIN_APPROX_TC89_L1cv2.CHAIN_APPROX_TC89_KCOS:这些是其他轮廓近似方法,但通常cv2.CHAIN_APPROX_SIMPLE就足够了。

众多轮廓中(cnts)找到最大轮廓(C)函数:

c = max(cnts, key=cv2.contourArea)

cv2.contourArea 是一个用于计算轮廓面积的函数,它接受一个轮廓(通常是一个点的数组)作为输入,并返回该轮廓的面积。 

key 参数接受一个函数,该函数被用于从 cnts 列表中的每个元素(即每个轮廓)提取一个用于比较的值。在这个例子中,cv2.contourArea 被用作这个提取函数。

c 接收到的数据类型是一个 NumPy 数组(numpy.ndarray),这个数组表示了检测到的最大轮廓的点集。具体来说,这个数组通常是一个二维数组(或称为矩阵),其中每一行代表轮廓上的一个点,每个点由两个或三个整数坐标组成(对于二维图像通常是 (x, y),对于三维图像可能是 (x, y, z),但通常轮廓处理是在二维图像上进行的)。

轮廓通常是由一系列的点组成的,并且 OpenCV 的 findContours 函数返回的是点集的列表(每个列表代表一个轮廓),因此 c 的确切形状可能会稍有不同,但它总是包含多个表示轮廓上点的二维数组。此外,每个点的坐标通常是以单个数组 [x, y] 的形式出现的,但在轮廓数组中,这些点可能又被封装在一个额外的数组中(如上所示),或者可能是“扁平化”的(即没有额外的数组封装)。

计算轮廓(C)最小包围圆:

((x, y), radius) = cv2.minEnclosingCircle(c)

函数会计算轮廓 c 的最小包围圆,并返回圆心和半径。

圆心的坐标是一个元组 (x, y),其中 xy 是浮点型数值,表示圆心的坐标位置。

radius 也是浮点型数值,表示圆的半径。

计算轮廓(C)的质心(center):

 M = cv2.moments(c)

 cv2.moments(c) 函数会计算轮廓 c 的图像矩。

图像矩可以帮助计算有关轮廓的一些属性,比如面积、质心等。

M接受到的是一个字典(dict)类型的数据,这个字典包含了轮廓c的多个图像矩。这些图像矩是通过对轮廓上的点进行积分运算得到的,可以用来描述轮廓的形状和特性。

M字典中的键(key)是字符串类型,代表不同类型的图像矩,而对应的值(value)是浮点数类型(如float),表示该图像矩的具体数值。OpenCV中计算得到的图像矩主要包括以下几种:

  • 'm00': 零阶矩,表示轮廓内的像素总数(即轮廓的面积)。
  • 'm10', 'm01': 一阶矩,与轮廓的质心位置有关。
  • 'm20', 'm02', 'm11': 二阶矩,与轮廓的旋转和缩放特性有关。
  • 更高阶的矩:OpenCV也支持计算更高阶的图像矩,但通常在实际应用中,低阶矩(尤其是零阶矩和一阶矩)已经足够描述轮廓的基本特性。

M字典的具体形式如下:

例如M['m00']会返回轮廓的面积,M['m10']M['m01']可以用来计算轮廓的质心位置。

注意:在计算质心位置时,需要确保M['m00'](即轮廓面积)不为0,以避免除以零的错误。如果M['m00']为0,表示轮廓为空或者没有正确地检测到轮廓。

文心大模型3.5生成

轮廓的质心(也称为重心)可以通过以下公式计算:

因此代码中的center如下计算:

center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))

OpenCv画圆函数:

cv2.circle(img, center_coordinates, radius, color, thickness)

  • img:这是你要在其上绘制圆的图像。在这个例子中,它是frame,即你可能正在处理的视频帧或图像。
  • center_coordinates:这是圆的中心坐标,由两个值(x, y)组成,分别代表横坐标和纵坐标。在这个例子中,(int(x), int(y))是第一个圆的中心坐标,而center(它应该是一个元组,例如(cx, cy))是第二个圆的中心坐标(即质心)。
  • radius:这是圆的半径,以像素为单位。在这个例子中,int(radius)是第一个圆的半径,而第二个圆(质心)的半径是由thickness参数控制的(因为我们传递了-1作为thickness,所以它会绘制一个填充的圆)。
  • color:这是圆的颜色。它是一个BGR元组,即蓝色、绿色和红色的组合。在这个例子中,(0, 255, 255)代表青色(因为没有红色,蓝色和绿色都是最大值),而(0, 0, 255)代表蓝色。
  • thickness:这是圆的线条厚度。如果它是正数,那么它就代表线条的粗细(以像素为单位)。如果它是负数(如-1),那么圆就会被填充。在这个例子中,第一个圆有一个线条厚度为2的轮廓,而第二个圆(质心)是填充的。
  • 文心大模型3.5生成

双端队列:

双端队列(Deque,全称为“double-ended queue”)是一种具有队列和栈的性质的数据结构。它支持从两端弹出和插入元素的操作,因此得名“双端”。具体来说,双端队列允许我们在其前端(front)和后端(rear)进行添加(append/push)和删除(pop)操作。

在Python中,collections.deque 是一个双端队列的实现。与列表(list)相比,deque 在从两端添加和删除元素时具有更高的效率,因为列表在列表中间进行插入或删除操作需要移动其他元素,而 deque 不需要。

在你给出的代码片段中,pts 可能是一个 collections.deque 类型的对象,用于存储一系列质心点。这些质心点可能是从图像中检测到的对象(如移动物体)的中心点。

代码解释:

  1. pts.appendleft(center):将新检测到的质心 center 添加到双端队列 pts 的左端(即前端)。

  2. 循环遍历 pts 中的点(除了第一个),绘制它们之间的线:

    • for i in range(1, len(pts)): 遍历从索引1开始到最后一个元素的点。
    • if pts[i - 1] is not None and pts[i] is not None: 确保两个相邻的点都不是 None。这可能是因为某种原因(如检测失败)某些点可能未被设置。
    • thickness = int(np.sqrt(args["buffer"] / float(i + 1)) * 2.5):这里动态计算线的粗细。其中 args["buffer"] 可能是一个预设的缓冲值,用于控制线的粗细变化。随着 i 的增加(即距离队列开始的位置越远),线的粗细会逐渐减小。
    • cv2.line(frame, pts[i - 1], pts[i], (0, 0, 255), thickness):在图像 frame 上绘制从 pts[i - 1]pts[i] 的红色线,线的粗细为前面计算得到的 thickness

通过这种方式,你可以看到随着时间的推移(或随着更多质心被添加到队列中),这些质心点之间的线会逐渐变细,从而可能提供一种视觉上的“轨迹衰减”效果,使得最近的点之间的线更粗,而较早的点之间的线更细。

文心大模型3.5生成

网上查阅资料贴出:

[树莓派基础]8.树莓派OpenCV颜色追踪讲解_哔哩哔哩_bilibili

文心一言

 

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

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

相关文章

Claude 3.5 强势出击:解析最新AI模型的突破与应用

近年来,人工智能领域的发展迅猛,各大科技公司纷纷推出了自家的高级语言模型。在这场技术竞赛中,Anthropic的Claude系列模型凭借其强大的性能和创新的功能脱颖而出。最近,Anthropic发布了Claude 3.5 Sonnet模型,引起了广…

如何设置Excel单元格下拉列表

如何设置Excel单元格下拉列表 在Excel中设置单元格下拉列表可以提高数据输入的准确性和效率。以下是创建下拉列表的步骤: 使用数据验证设置下拉列表: 1. 选择单元格: 选择你想要设置下拉列表的单元格或单元格区域。 2. 打开数据验证&…

高斯算法的原理及其与常规求和方法的区别

高斯算法的原理 高斯算法的原理源于数学家卡尔弗里德里希高斯在他少年时期发现的一种求和方法。当时老师让学生们计算1到100的和,高斯发现了一种快速计算的方法。 高斯注意到,如果将序列的首尾两数相加,结果总是相同的。例如: …

DVWA 靶场 JavaScript 通关解析

前言 DVWA代表Damn Vulnerable Web Application,是一个用于学习和练习Web应用程序漏洞的开源漏洞应用程序。它被设计成一个易于安装和配置的漏洞应用程序,旨在帮助安全专业人员和爱好者了解和熟悉不同类型的Web应用程序漏洞。 DVWA提供了一系列的漏洞场…

【Python】从基础到进阶(一):了解Python语言基础以及变量的相关知识

🔥 个人主页:空白诗 文章目录 引言一、Python简介1.1 历史背景1.2 设计哲学1.3 语言特性1.4 应用场景1.5 为什么选择Python 二、Python语言基础2.1 注释规则2.1.1 单行注释2.1.2 多行注释2.1.3 文件编码声明注释 2.2 代码缩进2.3 编码规范2.3.1 命名规范…

AlphaMissense:预测错义变异的致病性

错义变异或错义变体(missense variation或missense variants)是改变蛋白质氨基酸序列的遗传变异。致病性错义变异会严重破坏蛋白质功能,在目前观察到的400多万个错义变异中,只有2%在临床上被确定为致病或良性。对剩下的类型不明的…

力扣随机一题 模拟+字符串

博客主页:誓则盟约系列专栏:IT竞赛 专栏关注博主,后期持续更新系列文章如果有错误感谢请大家批评指出,及时修改感谢大家点赞👍收藏⭐评论✍ 1910.删除一个字符串中所有出现的给定子字符串【中等】 题目: …

【数据结构与算法】树的遍历,森林遍历 详解

树的先根遍历、后根遍历对应其二叉树的哪种遍历 树的先根遍历对应其二叉树的先序遍历(根-左-右)。树的后根遍历对应其二叉树的中序遍历(左-根-右)。 森林的先根遍历、中根遍历对应其二叉树的哪种遍历? 森林的先根遍历对应其二…

Elasticsearch 数据提取 - 最适合这项工作的工具是什么?

作者:来自 Elastic Josh Asres 了解在 Elasticsearch 中为你的搜索用例提取数据的所有不同方式。 对于搜索用例,高效采集和处理来自各种来源的数据的能力至关重要。无论你处理的是 SQL 数据库、CRM 还是任何自定义数据源,选择正确的数据采集…

《Deep learning practice》learning notes

学习笔记: 【公开课】旷视x北大《深度学习实践》(28课时全) R Talk | 旷视科技目标检测概述:Beyond RetinaNet and Mask R-CNN 文章目录 Lecture 1: Introduction to Computer Vision and Deep Learning(孙剑&#x…

系统架构师考点--数据库系统

大家好。今天我来总结一下数据库系统的相关考点。本考点一般情况下上午场考试占3-5分,下午场案例分析题也会出现。 一、数据库系统 数据:数据库中存储的基本对象,是描述事物的符号记录。数据的种类:文本、图形、图像、音频、视频、学生的档…

AI/ML 数据湖参考架构架构师指南

这篇文章的缩写版本于 2024 年 3 月 19 日出现在 The New Stack 上。 在企业人工智能中,主要有两种类型的模型:判别模型和生成模型。判别模型用于对数据进行分类或预测,而生成模型用于创建新数据。尽管生成式人工智能最近占据了新闻的主导地…

【PyTorch】【机器学习】图片张量、通道分解合成和裁剪

一、导入所需库 from PIL import Image import torch import numpy as np import matplotlib.pyplot as plt二、读取图片 pic np.array(Image.open(venice-boat.jpg))上述代码解释:先用Image.open()方法读取jpg格式图片,再用np.array()方法将图片转成…

DBeaver 数据结果集设置不显示逗号(太丑了)

从Navicat切换过来使用DBeaver,发现类似bigint 这种数据类型在结果集窗口中显示总是给我加上一个逗号,看着很不习惯,也比较占空间,个人觉得这种可读性也不好。 于是我在网上尝试搜索设置方法,可能我的关键词没命中&…

【ARMv8/ARMv9 硬件加速系列 2.4 -- ARM NEON Q寄存器与V寄存器的关系】

文章目录 Q 与 V 的关系向量寄存器 v 的使用赋值操作寄存器赋值总结Q 与 V 的关系 在ARMv8/v9架构中,v寄存器和q寄存器实际上是对相同的物理硬件资源的不同称呼,它们都是指向ARM的SIMD(单指令多数据)向量寄存器。这些寄存器用于高效执行向量和浮点运算,特别是在多媒体处理…

EM算法数学推导

EM算法可以看李航老师的《机器学习方法》、机器学习白板推导、EM算法及其推广进行学习。下文的数学推导出自“南瓜书”,记录在此只为方便查阅。

新手充电-boost升压电路解析

1.boost升压电路解析 本篇文章从充放电两个方面来对Boost电路的原理进行了讲解。并在最后补充了一些书本上没有的知识,整体属于较为新手向的文章,希望大家在阅读过本篇文章之后,能对Boost电路的基本原理有进一步了解。 Boost电路是一种开关直流升压电路,它能够使输出电压高…

HTTP/2 头部压缩 Header Compress(HPACK)详解

文章目录 1. HPACK 的工作原理1.1 静态表1.2 动态表 2. 压缩过程2.1 编码过程2.2 解码过程 3. HPACK 的优势 在HTTP1.0中,我们使用文本的形式传输header,在header中携带cookie的话,每次都需要重复传输几百到几千的字节,这着实是一…

尚品汇-(三)

maven之packaging标签 (1)项目创建父模块 首先设置 下Maven Maven:仓库地址:这里是腾讯云仓库 作为父模块,src没用,干掉src 这里我们是Maven创建的项目,想要项目变成SpringBoot的项目&#xf…

程序猿大战Python——面向对象——继承基础

定义类的几种语法 目标:了解定义类的标准语法。 我们知道,可以使用class关键字定义类。 在类的使用中,定义方式有三种: (1)【类名】 (2)【类名()】 (3)【…