亲测window,linux环境下都可使用
本人主做嵌入式自学的视觉,学习分享,能力有限,望大佬们指点
参考文献:1.光流法运动目标检测 - 小宅博客 (bilibili996.com)
背景:想做的是能够追踪一个目标点,在三维空间下的坐标
想上深度学习,但是感觉有点费算力,其次没系统学习人为只能作为识别,不知道深度学习对点的追踪效果咋样,希望大拿可以说下。所以最后用了OPENCV给了个简单的展现
一.环境搭建
主要环境是python3.7
以及下几个主要的Python库
- OpenCV (cv2): 用于图像处理和计算光流。
- Matplotlib: 用于绘制3D图表可视化光流跟踪结果。
- NumPy: 用于处理数组数据
可以通过以下命令来检测环境是否已经安装
pip install opencv-python matplotlib numpy
检查是否有相应的Python库
sudo apt-get install python3-opencv python3-matplotlib python3-numpy
好了那就没有了,废话不多说上代码
二.二维坐标下光流显示
效果图如上
代码主要实现方式是这样的
- 初始化摄像头并设置回调函数,以便通过鼠标点击指定跟踪的关键点。
- 使用ShiTomasi角点检测初始化关键点,或者通过鼠标回调获取用户指定的关键点。
- 在每一帧中使用Lucas-Kanade方法计算光流,跟踪关键点的运动。
- 使用Matplotlib在3D图表中显示关键点的轨迹,其中X、Y、Z分别表示关键点的横纵坐标和帧数。
- 在用户按下 '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)