基于OPENCV的光流检测(坐标点输出二维与伪三维)

亲测window,linux环境下都可使用

本人主做嵌入式自学的视觉,学习分享,能力有限,望大佬们指点

参考文献:1.光流法运动目标检测 - 小宅博客 (bilibili996.com)

                2. 请登录后下载 - 小宅博客

背景:想做的是能够追踪一个目标点,在三维空间下的坐标

想上深度学习,但是感觉有点费算力,其次没系统学习人为只能作为识别,不知道深度学习对点的追踪效果咋样,希望大拿可以说下。所以最后用了OPENCV给了个简单的展现

一.环境搭建

主要环境是python3.7

以及下几个主要的Python库

  1. OpenCV (cv2): 用于图像处理和计算光流。
  2. Matplotlib: 用于绘制3D图表可视化光流跟踪结果。
  3. NumPy: 用于处理数组数据

可以通过以下命令来检测环境是否已经安装

pip install opencv-python matplotlib numpy

检查是否有相应的Python库

sudo apt-get install python3-opencv python3-matplotlib python3-numpy

好了那就没有了,废话不多说上代码

二.二维坐标下光流显示

效果图如上

代码主要实现方式是这样的

  1. 初始化摄像头并设置回调函数,以便通过鼠标点击指定跟踪的关键点。
  2. 使用ShiTomasi角点检测初始化关键点,或者通过鼠标回调获取用户指定的关键点。
  3. 在每一帧中使用Lucas-Kanade方法计算光流,跟踪关键点的运动。
  4. 使用Matplotlib在3D图表中显示关键点的轨迹,其中X、Y、Z分别表示关键点的横纵坐标和帧数。
  5. 在用户按下 'q' 键时退出跟踪。

各个函数的解释

1.tracking 函数:

cap.read() 从摄像头中读取一帧图像,返回布尔值 ret 表示读取是否成功,以及图像帧 frame。
图像帧经过灰度转换,使用 ShiTomasi 角点检测找到角点,这些角点作为初始关键点。
进入追踪循环,不断读取新的图像帧,计算光流,更新关键点位置,绘制轨迹,并在 Matplotlib 图表中显示关键点的 3D 轨迹。
用户通过按 'q' 键中断追踪。
2.mouse_callback 函数:

当鼠标左键按下时,获取当前鼠标位置 (x, y),将其作为新的初始关键点。
3.主程序:

打开摄像头 cap = cv2.VideoCapture(0)。
创建一个窗口,并设置鼠标回调函数 cv2.namedWindow('frame') 和 cv2.setMouseCallback('frame', mouse_callback)。
调用 tracking 函数开始跟踪。
4.Matplotlib 部分:

创建一个 3D 图表 fig = plt.figure() 和 ax = fig.add_subplot(111, projection='3d')。
在循环中不断更新 3D 图表,使用 ax.scatter 显示关键点的 3D 轨迹,并通过 ax.quiver 添加 XYZ 轴的箭头。
使用 plt.pause(0.01) 添加延迟,使 Matplotlib 能够及时更新图表。

2D完整代码如下 

import numpy as np
import cv2
import matplotlib.pyplot as plt

points1 = None

def tracking(frame, point_to_track):
    global points1

    # 转换为灰度图像
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 设置 ShiTomasi 角点检测参数
    feature_params = dict(maxCorners=100,
                          qualityLevel=0.3,
                          minDistance=7,
                          blockSize=7)

    # 获取角点,并将其转换为浮点数
    if point_to_track is None and points1 is None:
        points1 = cv2.goodFeaturesToTrack(gray, mask=None, **feature_params)
        if points1 is not None:
            points1 = np.float32(points1)
        else:
            # 处理角点为空的情况
            return
    elif point_to_track is not None:
        points1 = np.float32([point_to_track])

    # 初始化跟踪器状态
    mask = np.zeros_like(frame)
    color = np.random.randint(0, 255, (3,))

    # 创建用于显示坐标的 Matplotlib 图表
    fig, ax = plt.subplots()

    # 跟踪特征点
    while True:
        # 读取下一帧图像
        ret, frame = cap.read()
        if not ret:
            break
        gray_prev = gray.copy()
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 计算光流
        points2, st, err = cv2.calcOpticalFlowPyrLK(gray_prev, gray, points1, None)

        # 检查是否有可追踪的点
        if points2 is None:
            continue

        # 更新状态和特征点
        status = st.flatten() == 1
        points2 = points2[status].reshape(-1, 2)

        # 绘制轨迹
        for new, old in zip(points2, points1):
            a, b = new.ravel()
            c, d = old.ravel()
            mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)), color.tolist(), 2)
            frame = cv2.circle(frame, (int(a), int(b)), 5, color.tolist(), -1)

        # 更新特征点
        points1 = points2.reshape(-1, 1, 2)

        # 显示跟踪结果
        cv2.imshow('frame', frame)

        # 将关键点的坐标传输到 Matplotlib 图表中进行显示
        ax.clear()
        ax.plot(points1[:, 0, 0], points1[:, 0, 1], 'ro')
        ax.set_xlim(0, frame.shape[1])
        ax.set_ylim(frame.shape[0], 0)
        plt.pause(0.01)

        # 等待用户点击鼠标,生成关键点并重新开始跟踪
        if cv2.waitKey(1) == ord('q'):
            break

    # 清理资源
    cap.release()
    cv2.destroyAllWindows()

def mouse_callback(event, x, y, flags, param):
    global points1
    if event == cv2.EVENT_LBUTTONDOWN:
        points1 = np.float32([[x, y]])

if __name__ == '__main__':
    # 打开摄像头
    cap = cv2.VideoCapture(0)

    # 指定要跟踪的关键点
    points1 = None

    # 设置鼠标回调函数
    cv2.namedWindow('frame')
    cv2.setMouseCallback('frame', mouse_callback)

    # 开始跟踪
    tracking(cap.read()[1], points1)

三.伪三维空间坐标下光流显示

这里为什么我说是伪三维呢,因为单个摄像头的话他是就只能测出XY坐标,你距离摄像头的深度它是无法体现出来的,你视觉在二维上面的远近体现只有大小变化,所以单目摄像头很难搞出来,所以这个时候就要用双目算法了(我正在学,学完发)

这个代码逻辑其实就是加了一个Z轴,因为没有深度距离这类吗,所以我就令刚才的坐标为z=0这个时候的横截面的坐标。但是看起来是个三维立体图(如下)

3D完整代码如下

import numpy as np
import cv2
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

points1 = None

def tracking(frame, point_to_track):
    global points1

    # 转换为灰度图像
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # 设置 ShiTomasi 角点检测参数
    feature_params = dict(maxCorners=100,
                          qualityLevel=0.3,
                          minDistance=7,
                          blockSize=7)

    # 获取角点,并将其转换为浮点数
    if point_to_track is None and points1 is None:
        points1 = cv2.goodFeaturesToTrack(gray, mask=None, **feature_params)
        if points1 is not None:
            points1 = np.float32(points1)
        else:
            # 处理角点为空的情况
            return
    elif point_to_track is not None:
        points1 = np.float32([point_to_track])

    # 初始化跟踪器状态
    mask = np.zeros_like(frame)
    color = np.random.randint(0, 255, (3,))

    # 创建用于显示 3D 坐标的 Matplotlib 图表
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')

    # 跟踪特征点
    while True:
        # 读取下一帧图像
        ret, frame = cap.read()
        if not ret:
            break
        gray_prev = gray.copy()
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 计算光流
        points2, st, err = cv2.calcOpticalFlowPyrLK(gray_prev, gray, points1, None)

        # 检查是否有可追踪的点
        if points2 is None:
            continue

        # 更新状态和特征点
        status = st.flatten() == 1
        points2 = points2[status].reshape(-1, 2)

        # 绘制轨迹
        for new, old in zip(points2, points1):
            a, b = new.ravel()
            c, d = old.ravel()
            mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)), color.tolist(), 2)
            frame = cv2.circle(frame, (int(a), int(b)), 5, color.tolist(), -1)

        # 更新特征点
        points1 = points2.reshape(-1, 1, 2)

        # 显示跟踪结果
        cv2.imshow('frame', frame)

        # 将关键点的坐标传输到 Matplotlib 图表中进行 3D 显示
        ax.clear()
        ax.scatter(points1[:, 0, 0], points1[:, 0, 1], np.zeros_like(points1[:, 0, 0]), c='r', marker='o')

        # 添加 XYZ 轴的箭头
        arrow_length = 50
        ax.quiver(0, 0, 0, arrow_length, 0, 0, color='b', label='X')
        ax.quiver(0, 0, 0, 0, arrow_length, 0, color='g', label='Y')
        ax.quiver(0, 0, 0, 0, 0, arrow_length, color='r', label='Z')

        ax.set_xlim(0, frame.shape[1])
        ax.set_ylim(frame.shape[0], 0)
        ax.set_zlim(0, 600)  # 设置 Z 轴范围
        plt.pause(0.01)

        # 等待用户点击鼠标,生成关键点并重新开始跟踪
        if cv2.waitKey(1) == ord('q'):
            break

    # 清理资源
    cap.release()
    cv2.destroyAllWindows()

def mouse_callback(event, x, y, flags, param):
    global points1
    if event == cv2.EVENT_LBUTTONDOWN:
        points1 = np.float32([[x, y]])

if __name__ == '__main__':
    # 打开摄像头
    cap = cv2.VideoCapture(0)

    # 指定要跟踪的关键点
    points1 = None

    # 设置鼠标回调函数
    cv2.namedWindow('frame')
    cv2.setMouseCallback('frame', mouse_callback)

    # 开始跟踪
    tracking(cap.read()[1], points1)

  • 42
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值