引言:
光流估计是计算图像序列中物体运动的方法之一。在计算机视觉和图像处理中,光流被用来估计图像中像素的运动方向和速度。它是通过比较两帧图像中相邻像素的亮度值来实现的。
那么会实现什么样子的功能呢?我们来看一下效果
上图就是某视频中截取到的一帧画面,那么我们接下来看一下光流估计可以达到什么样子的效果
我们可以看见上图中有很多的光点,以及出现了很多绿色的轨迹,这些绿色的轨迹就是视频中人物运动的轨迹。通过这些轨迹我们可以更好的识别图像中的运动物体,并对它们进行分割。通过检测物体的运动模式,可以更准确地对物体进行识别和跟踪。
那么我们接下来看一下如何通过代码实现
import cv2
import numpy as np
# 打开视频文件
cap = cv2.VideoCapture(r'C:\Users\35173\Desktop\test.avi')
# 获取第一帧
ret, first_frame = cap.read()
if not ret:
print("Error: Could not read the first frame.")
cap.release()
exit()
# 将第一帧转换为灰度图像
prev_gray = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)
第一部分:
导入必要的库,开始读取文件,视频文件用VideoCapture来进行读取,同时逐帧的读取视频的读取视频,如果读取不到就释放,这个时候要考虑是不是文件地址没选成功,或者视频损坏
# 将第一帧转换为灰度图像
prev_gray = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)
# 角点检测参数
feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
# Lucas-Kanade光流法参数
lk_params = dict(winSize=(15, 15), maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 使用Shi-Tomasi角点检测器检测第一帧中的角点
prev_points = cv2.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
# 创建用于绘制轨迹的掩膜
mask = np.zeros_like(first_frame)
第二部分:
首先将第一帧转化为灰度图这一步很重要用于减少计算量,然后设置以下角点检测的参数。其中,各参数含义如下所示:
maxCorners
: 最大角点数,表示算法尝试找到的角点的最大数量。qualityLevel
: 质量水平,一个介于0和1之间的值,用于评估角点的可靠性。较高的值意味着更严格的质量评估。minDistance
: 角点之间的最小欧氏距离,用于确保角点的分布。blockSize
: 用于提取角点的图像块的大小,影响角点检测的精度。
然后再设置一些光流检测的参数,各参数的含义如下:
winSize
: 光流计算中使用的窗口大小。较大的窗口可以提供更平滑的光流场,但可能会降低精度。maxLevel
: 金字塔的最大层数,用于多尺度光流估计。较高的值可以提供更好的性能,但会增加计算量。criteria
: 终止条件,由三部分组成:cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT
: 表示当满足以下任一条件时停止迭代:达到指定的精度(EPS
)或迭代次数(COUNT
)。10
: 迭代次数的阈值。0.03
: 精度的阈值,即算法认为已经找到足够好的匹配时的误差阈值。
然后使用角点检测器来检测第一帧的角点,同时返回的prev_points
是一个数组,包含了检测到的角点的坐标。然后创建了一个大小和第一帧相似的全零矩阵作为掩膜。
while True:
ret, frame = cap.read()
if not ret:
print("Error: Could not read frame.")
break
# 将当前帧转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 计算光流
next_points, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, gray, prev_points, None, **lk_params)
# 选择好的跟踪点
good_new = next_points[status == 1]
good_old = prev_points[status == 1]
# 绘制跟踪结果
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
a, b, c, d = int(a), int(b), int(c), int(d) # 将坐标转换为整数
mask = cv2.line(mask, (a, b), (c, d), (0, 255, 0), 2)
frame = cv2.circle(frame, (a, b), 5, (0, 0, 255), -1)
img = cv2.add(frame, mask)
# 显示结果
cv2.imshow('Optical Flow', img)
第三部分:
在逐帧的读取视频并转化为灰度图之后开始计算光流,
使用cv2.calcOpticalFlowPyrLK
函数计算前一帧(prev_gray
)和当前帧(gray
)之间的光流。prev_points
是前一帧中检测到的角点。该函数返回三个值:
next_points
: 当前帧中角点的预测位置。status
: 一个布尔数组,表示每个角点是否成功跟踪。err
: 一个数组,包含每个角点跟踪的误差。
根据status
数组,选择成功跟踪的角点。status
为True
(即数值为1)表示角点被成功跟踪。紧接着遍历成功跟踪的角点,new
是当前帧中角点的位置,old
是前一帧中角点的位置。接着将角点坐标降维成一维之后从浮点数转换为整数,因为图像坐标必须是整数。然后使用cv2.line
函数在掩膜mask
上绘制从旧位置到新位置的绿色线条,以可视化角点的移动轨迹。同时在当前帧的角点上绘制新的圆圈,表示角点的新位置。将当前帧和掩膜合并之后显示它们的结果。
k = cv2.waitKey(30) & 0xff
if k == 27:
break
# 更新前一帧和前一帧的角点
prev_gray = gray.copy()
prev_points = good_new.reshape(-1, 1, 2)
# 释放视频捕获对象并关闭所有窗口
cap.release()
cv2.destroyAllWindows()
第四部分:
作为结尾部分,其中cv2.waitKey
函数会等待用户按下一个键盘按键,参数30
表示等待时间(毫秒)。如果30毫秒内没有按键被按下,函数返回-1,如果用户按下了Esc键(ASCII码为27),则k
的值将等于27。如果检测到这个值,循环将通过break
语句被终止,这意味着视频处理和光流估计将停止。同时更新光流估计中使用的前一帧和前一帧的角点。最后释放掉获取到的资源,并且关闭掉所有窗口,避免对系统造成不必要的负担。
完整代码:
import cv2
import numpy as np
# 打开视频文件
cap = cv2.VideoCapture(r'C:\Users\35173\Desktop\test.avi')
# 获取第一帧
ret, first_frame = cap.read()
if not ret:
print("Error: Could not read the first frame.")
cap.release()
exit()
# 将第一帧转换为灰度图像
prev_gray = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)
# 角点检测参数
feature_params = dict(maxCorners=100, qualityLevel=0.3, minDistance=7, blockSize=7)
# Lucas-Kanade光流法参数
lk_params = dict(winSize=(15, 15), maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03))
# 使用Shi-Tomasi角点检测器检测第一帧中的角点
prev_points = cv2.goodFeaturesToTrack(prev_gray, mask=None, **feature_params)
# 创建用于绘制轨迹的掩膜
mask = np.zeros_like(first_frame)
while True:
ret, frame = cap.read()
if not ret:
print("Error: Could not read frame.")
break
# 将当前帧转换为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 计算光流
next_points, status, err = cv2.calcOpticalFlowPyrLK(prev_gray, gray, prev_points, None, **lk_params)
# 选择好的跟踪点
good_new = next_points[status == 1]
good_old = prev_points[status == 1]
# 绘制跟踪结果
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
a, b, c, d = int(a), int(b), int(c), int(d) # 将坐标转换为整数
mask = cv2.line(mask, (a, b), (c, d), (0, 255, 0), 2)
frame = cv2.circle(frame, (a, b), 5, (0, 0, 255), -1)
img = cv2.add(frame, mask)
# 显示结果
cv2.imshow('Optical Flow', img)
k = cv2.waitKey(30) & 0xff
if k == 27:
break
# 更新前一帧和前一帧的角点
prev_gray = gray.copy()
prev_points = good_new.reshape(-1, 1, 2)
# 释放视频捕获对象并关闭所有窗口
cap.release()
cv2.destroyAllWindows()
标签:视频,gray,frame,cv2,角点,估计,之光流,prev,points
From: https://blog.csdn.net/2301_77444219/article/details/139423023