基于OpenCV的Python颜色追踪实现:绿萝树叶识别与轨迹绘制
本文介绍如何使用OpenCV和Python实现指定颜色的物体追踪。通过解析绿萝树叶的HSV色彩范围,结合高斯模糊、形态学操作及轮廓查找算法,实时定位目标。程序支持视频流处理,利用双端队列记录质心坐标并绘制运动轨迹,同时提供视频保存功能,适用于基础计算机视觉项目。
OpenCV颜色追踪项目概述
本文旨在演示如何利用OpenCV实现对特定颜色物体的实时追踪,实验对象选取了绿萝的树叶。
脚本创建与库导入
首先创建脚本文件ball_tracking.py,并编写相关代码。
import argparse
from collections import deque
import cv2
import numpy as np
导入所需的库文件,并定义辅助函数。
def grab_contours(cnts):
# 如果 cv2.findContours 返回的轮廓元组的长度为“2”,那么我们使用的是 OpenCV v2.4、v4-beta 或 v4-official
if len(cnts) == 2:
cnts = cnts[0]
# 如果轮廓元组的长度为“3”,那么我们使用的是 OpenCV v3、v4-pre 或 v4-alpha
elif len(cnts) == 3:
cnts = cnts[1]
else:
raise Exception(("Contours tuple must have length 2 or 3, "
"otherwise OpenCV changed their cv2.findContours return "
"signature yet again. Refer to OpenCV's documentation "
"in that case"))
return cnts
def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
dim = None
(h, w) = image.shape[:2]
# 如果高和宽为None则直接返回
if width is None and height is None:
return image
# 检查宽是否是None
if width is None:
# 计算高度的比例并并按照比例计算宽度
r = height / float(h)
dim = (int(w * r), height)
# 高为None
else:
# 计算宽度比例,并计算高度
r = width / float(w)
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation=inter)
# return the resized image
return resized
其中grab_contours函数针对OpenCV不同版本进行了兼容性处理,以适配findContours返回格式的差异。
resize函数用于等比例调整图片尺寸,确保处理效率。
命令行参数
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to video")
ap.add_argument("-b", "--buffer", type=int, default=64, help="max buffer size")
args = vars(ap.parse_args())
# 绿色树叶的HSV色域空间范围
greenLower = (29, 86, 6)
greenUpper = (64, 255, 255)
pts = deque(maxlen=args["buffer"])
vs = cv2.VideoCapture(0)
fps = 30 #保存视频的FPS,可以适当调整
size=(600,450)
fourcc=cv2.VideoWriter_fourcc(*'XVID')
videowrite=cv2.VideoWriter('output.avi',fourcc,fps,size)
参数配置与初始化
接下来定义程序运行所需的参数。
--video参数用于指定视频文件路径或摄像头ID。
--buffer参数设定deque的最大长度,用于维护被追踪球体(或物体)前序(x, y)坐标的列表。该双端队列用于绘制物体的运动“轨迹”,反映其历史位置;较小的队列值会产生较短的尾部,而较大的值则生成较长的轨迹线。
定义HSV色彩空间中绿色范围的上限和下限值。
启动摄像头0以获取视频流。
最后初始化VideoWriter对象,实现将处理后的视频帧写入文件的功能。
视频流处理与图像预处理
while True:
ret_val, frame = vs.read()
if ret_val is False:
break
frame = resize(frame, width=600)
# 通过高斯滤波去除掉一些高频噪声,使得重要的数据更加突出
blurred = cv2.GaussianBlur(frame, (11, 11), 0)
# 将图片转为HSV
hsv = cv2.cvtColor(blurred, cv2.COLOR_BGR2HSV)
# inRange的作用是根据阈值进行二值化:阈值内的像素设置为白色(255),阈值外的设置为黑色(0)
mask = cv2.inRange(hsv, greenLower, greenUpper)
# 腐蚀(erode)和膨胀(dilate)的作用:
# 1. 消除噪声;
# 2. 分割(isolate)独立的图像元素,以及连接(join)相邻的元素;
# 3. 寻找图像中的明显的极大值区域或极小值区域
mask = cv2.erode(mask, None, iterations=2)
mask = cv2.dilate(mask, None, iterations=2)
开启一个主循环,该循环持续运行直到满足以下任一条件:(1) 用户按下q键终止脚本,或 (2) 视频文件播放完毕且无更多帧可用。
读取视频的一帧,该操作返回两个参数:第一个布尔值表示读取是否成功,第二个参数为读取到的图像帧。
若读取失败(返回False),则跳出循环。
对图像进行预处理。首先,将帧宽度调整为600像素。缩小帧尺寸能减少图像数据量,从而提升处理速度并提高FPS(每秒帧数)。接着,对帧应用高斯模糊以抑制高频噪声,使算法更专注于结构物体(如球体或树叶)。最后,将图像从BGR格式转换为HSV颜色空间。
调用cv2.inRange函数在帧中定位绿色物体。首先设定绿色的HSV下限,随后设定上限。cv2.inRange的输出结果是一个二进制掩码。

轮廓检测与轨迹绘制
# 寻找轮廓,不同opencv的版本cv2.findContours返回格式有区别,所以调用了一下imutils.grab_contours做了一些兼容性处理
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = grab_contours(cnts)
center = None
# only proceed if at least one contour was found
if len(cnts) > 0:
# find the largest contour in the mask, then use it to compute the minimum enclosing circle
# and centroid
c = max(cnts, key=cv2.contourArea)
((x, y), radius) = cv2.minEnclosingCircle(c)
M = cv2.moments(c)
# 对于01二值化的图像,m00即为轮廓的面积, 一下公式用于计算中心距
center = (int(M["m10"] / M["m00"]), int(M["m01"] / M["m00"]))
# only proceed if the radius meets a minimum size
if radius > 10:
# draw the circle and centroid on the frame, then update the list of tracked points
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 either of the tracked points are None, ignore them
if pts[i - 1] is None or pts[i] is None:
continue
# compute the thickness of the line and draw the connecting line
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)
videowrite.write(frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
videowrite.release()
vs.release()
cv2.destroyAllWindows()
计算图像中对象的轮廓。在后续代码中,将球的中心(x, y)坐标初始化为None。
检查掩码中是否至少存在一个轮廓。假设已找到轮廓,则在cnts列表中找到面积最大的轮廓,计算该blob的最小包围圆,并得出中心(x, y)坐标(即“质心”)。
快速验证最小包围圆的半径是否足够大。若半径通过测试,则在帧上绘制两个圆圈:一个围绕物体本身,另一个标记物体的质心。
随后,将计算出的质心添加到pts列表中。
遍历pts列表中的每个点。若当前点或前一个点为None(表示该帧未成功检测到物体),则忽略当前索引并继续遍历。
若两个点均有效,则计算轨迹线的粗细,并在帧上绘制连接线。
至此,关于通过OpenCV实现对指定颜色物体追踪的介绍完成。更多相关内容可参考本站以往文章或继续浏览后续内容,感谢支持。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













