基于OpenCV的Python颜色追踪实现:绿萝树叶识别与轨迹绘制

Dylan
Dylan
发布时间:2026-10-15| 4人浏览过
原创

本文介绍如何使用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实现对指定颜色物体追踪的介绍完成。更多相关内容可参考本站以往文章或继续浏览后续内容,感谢支持。

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。