Python 视频分析代码详解:使用巴特沃斯滤波器检测运动
这段代码是一个 Python 视频分析函数,它利用巴特沃斯滤波器来检测视频中的运动,并通过绘制曲线和标记状态来直观展示分析结果。
analyze(self, video_path:str): 定义名为analyze的方法,接受视频路径作为参数。self.video_path = video_path: 将传入的视频路径赋值给类属性video_path。print('Processing ', os.path.basename(video_path), '...'): 打印正在处理的视频文件名。x, means_by_frame, centers_by_frame = self.detect_and_record(video_path): 调用detect_and_record方法,返回视频的帧数、每帧的像素均值和每个块的中心坐标。means_by_frame_filtered = np.array([sg.filtfilt(*self.butter_filter, mean_by_frame) for mean_by_frame in means_by_frame]): 对每帧的像素均值应用巴特沃斯滤波器,得到滤波后的结果。arg_maxima = sg.argrelmax(means_by_frame_filtered, axis=-1): 找到滤波后的均值序列中的局部极大值的索引。arg_minima = sg.argrelmin(means_by_frame_filtered, axis=-1): 找到滤波后的均值序列中的局部极小值的索引。arg_extremas = []和derivatives = []: 初始化空列表,用于存储每个块的极值索引和导数值。for block in range(self.num_blocks):: 遍历每个块的索引。arg_extrema_by_block = np.append(arg_maxima[1][arg_maxima[0]==block], arg_minima[1][arg_minima[0]==block]): 将当前块的局部极大值和局部极小值的索引合并为一个数组。if len(arg_extrema_by_block)>0:: 如果当前块存在极值。arg_extrema_by_block.sort(): 对当前块的极值索引进行排序。arg_extremas.append(np.append(np.array([0]), arg_extrema_by_block)): 将当前块的极值索引添加到arg_extremas列表中,前面添加一个 0 作为起始索引。x0 = arg_extrema_by_block: 将当前块的极值索引赋值给x0。x1 = np.append(x0, np.array([-1]))[-len(x0):]: 将x0数组的最后一个元素替换为 -1,然后取与x0相同长度的子数组赋值给x1。y0 = means_by_frame[block][x0]: 根据当前块和x0的索引,从均值序列中取出对应的均值赋值给y0。y1 = means_by_frame[block][x1]: 根据当前块和x1的索引,从均值序列中取出对应的均值赋值给y1。derivative_by_block = - (y1 - y0) / (x1 - x0): 计算当前块的导数。derivatives.append(np.append(np.array([0]), derivative_by_block)): 将当前块的导数添加到derivatives列表中,前面添加一个 0 作为起始导数。plt.ioff(): 关闭交互模式。plt.subplots_adjust(hspace=1.0): 调整子图之间的垂直间距。for idx, means in enumerate(means_by_frame_filtered):: 遍历滤波后的均值序列及其索引。plt.subplot(4, 2, idx+1): 创建一个 4 行 2 列的子图网格,并选择当前子图。plt.scatter(x, means, c=COLORS[0], s=1): 绘制散点图,x 轴为帧数,y 轴为均值,颜色为COLORS[0],点的大小为 1。plt.scatter(x[arg_maxima[1][arg_maxima[0]==idx]], means[arg_maxima[1][arg_maxima[0]==idx]], c=COLORS[-1], s=2): 在局部极大值的位置上绘制大点,颜色为COLORS[-1],点的大小为 2。plt.scatter(x[arg_minima[1][arg_minima[0]==idx]], means[arg_minima[1][arg_minima[0]==idx]], c=COLORS[-1], s=2): 在局部极小值的位置上绘制大点,颜色为COLORS[-1],点的大小为 2。plt.title('block {}'.format(idx+1)): 设置子图的标题为当前块的编号。plt.savefig(save_path.replace('.mp4', '_curves.png'), dpi=200): 保存当前子图为图片文件。
接下来,代码开始处理视频帧,将分析结果叠加到视频帧上,并保存为新的视频文件。
-
cap = cv2.VideoCapture(video_path): 打开视频文件。 -
fps = int(cap.get(cv2.CAP_PROP_FPS)): 获取视频的帧率。 -
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)): 获取视频的宽度。 -
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)): 获取视频的高度。 -
down_sample_ratio = 2: 设置下采样比例。 -
maxf = max(int(cap.get(cv2.CAP_PROP_FRAME_COUNT)), 1): 获取视频的总帧数。 -
save_path = '/'.join(video_path.split('/')[:-1]) + '/processed_videos': 构建保存处理后视频的文件夹路径。 -
if not os.path.exists(save_path):: 如果保存路径不存在。 -
os.mkdir(save_path): 创建保存路径。 -
file_type = video_path.split('/')[-1].split('.')[-1]: 获取视频文件的扩展名。 -
file_name = video_path.split('/')[-1][:-len(file_type)-1]: 获取视频文件名。 -
save_path = save_path + '/' + file_name + '_process.mp4': 构建保存处理后视频的文件路径。 -
videoWriter = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps//down_sample_ratio, (width, height), True): 创建视频写入器,指定输出视频的参数。 -
block_state = ['O'] * self.num_blocks: 创建一个长度为num_blocks的列表,用于存储每个块的状态。 -
block_color = [(100,100,100)] * self.num_blocks: 创建一个长度为num_blocks的列表,用于存储每个块的颜色。 -
value_by_block = [0] * self.num_blocks: 创建一个长度为num_blocks的列表,用于存储每个块的导数值。 -
for frame_idx in tqdm(np.arange(maxf)):: 遍历视频的每一帧。 -
ret, frame = cap.read(): 读取一帧视频。 -
if ret:: 如果成功读取到一帧。 -
output_frame = frame.copy(): 复制当前帧作为输出帧。 -
for block_idx in range(self.num_blocks):: 遍历每个块的索引。 -
if frame_idx in x:: 如果当前帧的索引在x中。 -
x_idx = int(np.where(x==frame_idx)[0][0]): 找到当前帧在x中的索引。 -
center = centers_by_frame[block_idx][min(x_idx, len(centers_by_frame[block_idx])-1)]: 根据当前块和x_idx的索引,从中心坐标序列中取出对应的中心坐标。 -
if x_idx in arg_extremas[block_idx]:: 如果x_idx在当前块的极值索引中。 -
value_by_block[block_idx] = derivatives[block_idx][arg_extremas[block_idx]==x_idx]: 将当前块的导数值赋值给value_by_block列表中对应的元素。 -
if np.abs(value_by_block[block_idx]) > self.der_th:: 如果当前块的导数值的绝对值大于阈值。 -
if value_by_block[block_idx]>0:: 如果当前块的导数值大于 0。 -
block_state[block_idx] = u'>': 将当前块的状态设置为 '>'。 -
block_color[block_idx] = (0,0,255): 将当前块的颜色设置为红色。 -
else:: 否则。 -
block_state[block_idx] = u'<': 将当前块的状态设置为 '<'。 -
block_color[block_idx] = (0,255,0): 将当前块的颜色设置为绿色。 -
else:: 否则。 -
block_state[block_idx] = u'-': 将当前块的状态设置为 '-'。 -
block_color[block_idx] = (100,100,100): 将当前块的颜色设置为灰色。 -
if frame_idx>=x.min():: 如果当前帧的索引大于等于x中的最小值。 -
cv2.putText(output_frame, block_state[block_idx], center[[1,0]], cv2.FONT_HERSHEY_SIMPLEX, 1, block_color[block_idx], 2): 在输出帧上绘制当前块的状态。 -
if frame_idx % down_sample_ratio != 0:: 如果当前帧不是下采样的帧。 -
continue: 跳过当前循环,继续下一次循环。 -
else:: 否则。 -
videoWriter.write(output_frame.astype('uint8')): 将输出帧写入视频文件。 -
cap.release(): 释放视频文件。 -
videoWriter.release(): 释放视频写入器。
这段代码利用巴特沃斯滤波器对视频帧的像素均值进行平滑处理,然后找到局部极值并计算导数,从而判断每个块的运动状态。通过在视频帧上绘制状态标记,可以直观地展示分析结果,这在运动分析、行为识别等领域具有应用价值。
原文地址: https://www.cveoy.top/t/topic/fRbx 著作权归作者所有。请勿转载和采集!