PiscCode使用光流法计算漂浮物位移速度
在计算机视觉中,光流法(Optical Flow)是一种常用的技术,通过分析连续图像帧之间的像素运动,推测物体的运动情况。光流法可以用于目标跟踪、运动分析等任务,广泛应用于机器人导航、视频分析等领域。
本文将介绍如何使用光流法分析物体的运动速度。我们将这个过程分成两个阶段:计算位移像素 和 根据像素计算速度。下面是详细的实现过程。
光流法原理
光流法的核心思想是通过比较连续帧中相同位置的像素点,计算它们的位移。这个过程通常依赖于一些特征点(如角点),在两帧之间跟踪这些点的位移。最终,我们可以推算出物体的速度。
步骤一:计算像素位移
首先,我们使用光流法来计算图像中每个特征点的位移。这里我们采用 Lucas-Kanade 光流算法,这是最常用的光流估计方法之一。
-
角点检测:在第一帧图像中检测角点,这些角点将在后续帧中被追踪。
-
光流计算:在后续帧中,通过光流算法追踪这些角点,计算每个点的位移。
代码实现如下:
import cv2
import numpy as npclass 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, # 最大角点数设置为10qualityLevel=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] - 10for 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 npclass 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, # 最大角点数设置为10qualityLevel=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/sspeeds.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