PiscCode使用光流法计算漂浮物位移速度
光流法不仅可以帮助我们跟踪图像中的物体,还能够准确估算物体的运动速度。通过将位移计算与速度换算分开,我们可以更清晰地理解并应用光流法。在实际应用中,光流法广泛应用于运动检测、目标跟踪、水文气象检测等场景中。
在计算机视觉中,光流法(Optical Flow)是一种常用的技术,通过分析连续图像帧之间的像素运动,推测物体的运动情况。光流法可以用于目标跟踪、运动分析等任务,广泛应用于机器人导航、视频分析等领域。
本文将介绍如何使用光流法分析物体的运动速度。我们将这个过程分成两个阶段:计算位移像素 和 根据像素计算速度。下面是详细的实现过程。

光流法原理
光流法的核心思想是通过比较连续帧中相同位置的像素点,计算它们的位移。这个过程通常依赖于一些特征点(如角点),在两帧之间跟踪这些点的位移。最终,我们可以推算出物体的速度。
步骤一:计算像素位移
首先,我们使用光流法来计算图像中每个特征点的位移。这里我们采用 Lucas-Kanade 光流算法,这是最常用的光流估计方法之一。
-
角点检测:在第一帧图像中检测角点,这些角点将在后续帧中被追踪。
-
光流计算:在后续帧中,通过光流算法追踪这些角点,计算每个点的位移。
代码实现如下:
import cv2
import numpy as np
class OpticalFlow:
def __init__(self):
# 设置 Lucas-Kanade 光流法的参数
self.lk_params = dict(winSize=(15, 15),
maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 创建随机数生成器
self.rng = np.random.default_rng()
def do(self, frame, device):
# 转为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 在第一帧中检测角点(角点是光流的起点)
if not hasattr(self, 'prev_gray'):
self.prev_gray = gray
# 使用 goodFeaturesToTrack 来获取10个角点
self.prev_points = cv2.goodFeaturesToTrack(gray,
maxCorners=10, # 最大角点数设置为10
qualityLevel=0.05, # 角点质量阈值
minDistance=7, # 最小角点间距
blockSize=7) # 用于计算角点的邻域大小
# 为每个特征点分配一个唯一的颜色,确保饱和度最大
self.colors = [tuple(int(c) for c in color) for color in self.rng.integers(200, 256, (len(self.prev_points), 3))]
return frame
# 计算光流(在当前帧中跟踪前一帧的角点)
next_points, status, _ = cv2.calcOpticalFlowPyrLK(self.prev_gray, gray, self.prev_points, None, **self.lk_params)
# 选出跟踪成功的点
good_new = next_points[status == 1]
good_old = self.prev_points[status == 1]
# 计算位移并准备显示信息
displacements = []
# 绘制光流轨迹
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
# 计算位移
displacement = np.sqrt((a - c) ** 2 + (b - d) ** 2)
displacements.append(f"Point {i+1}: {displacement:.2f} px")
# 获取对应的颜色
color = self.colors[i]
# 绘制轨迹
frame = cv2.line(frame, (int(a), int(b)), (int(c), int(d)), color, 2)
frame = cv2.circle(frame, (int(a), int(b)), 5, color, -1)
# 在右下角显示位移信息,确保每个信息的颜色与点的颜色一致
y_offset = frame.shape[0] - 10
for i, displacement in enumerate(displacements):
color = self.colors[i]
cv2.putText(frame, displacement, (10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2, cv2.LINE_AA) # 字体增大,饱和度增加
y_offset -= 40 # 每个信息之间间隔40个像素
# 更新上一帧数据
self.prev_gray = gray.copy()
self.prev_points = good_new.reshape(-1, 1, 2)
return frame
步骤二:根据像素和帧率计算速度
计算完每个特征点的位移后,我们接下来要根据实际情况(如帧率和像素与实际距离的换算)计算物体的运动速度。
-
位移换算:1厘米 = 10像素,这个换算关系用于将像素单位的位移转换为实际物理单位。
-
速度计算:通过已知的帧率(例如 25 帧/秒)和像素位移,可以计算物体的速度。
假设视频的帧率为 25 帧/秒,1厘米 = 10像素,我们可以使用以下公式来计算速度:
速度=位移10×帧率\text{速度} = \frac{\text{位移}}{10} \times \text{帧率}速度=10位移×帧率
这里的 位移 为像素单位,帧率 为每秒的帧数,10 是像素与厘米之间的换算系数。
import cv2
import numpy as np
class OpticalFlow:
def __init__(self):
# 设置 Lucas-Kanade 光流法的参数
self.lk_params = dict(winSize=(15, 15),
maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 创建随机数生成器
self.rng = np.random.default_rng()
def do(self, frame, device):
# 转为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 在第一帧中检测角点(角点是光流的起点)
if not hasattr(self, 'prev_gray'):
self.prev_gray = gray
# 使用 goodFeaturesToTrack 来获取10个角点
self.prev_points = cv2.goodFeaturesToTrack(gray, maxCorners=10, # 最大角点数设置为10
qualityLevel=0.05, # 角点质量阈值
minDistance=7, # 最小角点间距
blockSize=7) # 用于计算角点的邻域大小
# 为每个特征点分配一个唯一的颜色,确保饱和度最大
self.colors = [tuple(int(c) for c in color) for color in self.rng.integers(200, 256, (len(self.prev_points), 3))]
return frame # 在第一帧中返回原始图像
# 计算光流(在当前帧中跟踪前一帧的角点)
next_points, status, _ = cv2.calcOpticalFlowPyrLK(self.prev_gray, gray, self.prev_points, None, **self.lk_params)
# 选出跟踪成功的点
good_new = next_points[status == 1]
good_old = self.prev_points[status == 1]
# 计算位移并准备显示信息
displacements = []
speeds = []
# 计算位移、速度并绘制光流轨迹
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
# 计算位移
displacement = np.sqrt((a - c) ** 2 + (b - d) ** 2)
displacements.append(f"Point {i+1}: {displacement:.2f} px")
# 计算速度(1秒25帧,1厘米 = 10像素)
frame_rate = 25 # 帧率:25帧/秒
displacement_cm = displacement / 10 # 1厘米 = 10像素
speed = displacement_cm * frame_rate # 速度单位为cm/s
speeds.append(f"Speed: {speed:.2f} cm/s")
# 获取对应的颜色
color = self.colors[i]
# 绘制轨迹
frame = cv2.line(frame, (int(a), int(b)), (int(c), int(d)), color, 2)
frame = cv2.circle(frame, (int(a), int(b)), 5, color, -1)
# 在右下角显示位移信息,确保每个信息的颜色与点的颜色一致
y_offset = frame.shape[0] - 10 # 初始Y坐标在底部
for i, (displacement, speed) in enumerate(zip(displacements, speeds)):
color = self.colors[i]
# 在表格中显示每个点的位移和速度
cv2.putText(frame, f"{displacement} {speed}", (10, y_offset),
cv2.FONT_HERSHEY_SIMPLEX, 1, color, 2, cv2.LINE_AA)
y_offset -= 40 # 每行之间间隔40个像素
# 更新上一帧数据
self.prev_gray = gray.copy()
self.prev_points = good_new.reshape(-1, 1, 2)
return frame
完整流程
-
计算位移像素:通过光流算法,检测并追踪特征点,计算出每个特征点在两帧之间的位移,单位为像素。
-
根据像素计算速度:根据给定的帧率和像素与实际距离的换算关系,计算每个特征点的运动速度,单位为厘米/秒。
总结
光流法不仅可以帮助我们跟踪图像中的物体,还能够准确估算物体的运动速度。通过将位移计算与速度换算分开,我们可以更清晰地理解并应用光流法。在实际应用中,光流法广泛应用于运动检测、目标跟踪、水文气象检测等场景中。
对 PiscTrace or PiscCode感兴趣?更多精彩内容请移步官网看看~🔗 PiscTrace
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐


所有评论(0)