这段代码是一个 Python 视频分析函数,它利用巴特沃斯滤波器来检测视频中的运动,并通过绘制曲线和标记状态来直观展示分析结果。

  1. analyze(self, video_path:str): 定义名为 analyze 的方法,接受视频路径作为参数。
  2. self.video_path = video_path: 将传入的视频路径赋值给类属性 video_path
  3. print('Processing ', os.path.basename(video_path), '...'): 打印正在处理的视频文件名。
  4. x, means_by_frame, centers_by_frame = self.detect_and_record(video_path): 调用 detect_and_record 方法,返回视频的帧数、每帧的像素均值和每个块的中心坐标。
  5. means_by_frame_filtered = np.array([sg.filtfilt(*self.butter_filter, mean_by_frame) for mean_by_frame in means_by_frame]): 对每帧的像素均值应用巴特沃斯滤波器,得到滤波后的结果。
  6. arg_maxima = sg.argrelmax(means_by_frame_filtered, axis=-1): 找到滤波后的均值序列中的局部极大值的索引。
  7. arg_minima = sg.argrelmin(means_by_frame_filtered, axis=-1): 找到滤波后的均值序列中的局部极小值的索引。
  8. arg_extremas = []derivatives = []: 初始化空列表,用于存储每个块的极值索引和导数值。
  9. for block in range(self.num_blocks):: 遍历每个块的索引。
  10. arg_extrema_by_block = np.append(arg_maxima[1][arg_maxima[0]==block], arg_minima[1][arg_minima[0]==block]): 将当前块的局部极大值和局部极小值的索引合并为一个数组。
  11. if len(arg_extrema_by_block)>0:: 如果当前块存在极值。
  12. arg_extrema_by_block.sort(): 对当前块的极值索引进行排序。
  13. arg_extremas.append(np.append(np.array([0]), arg_extrema_by_block)): 将当前块的极值索引添加到 arg_extremas 列表中,前面添加一个 0 作为起始索引。
  14. x0 = arg_extrema_by_block: 将当前块的极值索引赋值给 x0
  15. x1 = np.append(x0, np.array([-1]))[-len(x0):]: 将 x0 数组的最后一个元素替换为 -1,然后取与 x0 相同长度的子数组赋值给 x1
  16. y0 = means_by_frame[block][x0]: 根据当前块和 x0 的索引,从均值序列中取出对应的均值赋值给 y0
  17. y1 = means_by_frame[block][x1]: 根据当前块和 x1 的索引,从均值序列中取出对应的均值赋值给 y1
  18. derivative_by_block = - (y1 - y0) / (x1 - x0) : 计算当前块的导数。
  19. derivatives.append(np.append(np.array([0]), derivative_by_block)): 将当前块的导数添加到 derivatives 列表中,前面添加一个 0 作为起始导数。
  20. plt.ioff(): 关闭交互模式。
  21. plt.subplots_adjust(hspace=1.0): 调整子图之间的垂直间距。
  22. for idx, means in enumerate(means_by_frame_filtered):: 遍历滤波后的均值序列及其索引。
  23. plt.subplot(4, 2, idx+1): 创建一个 4 行 2 列的子图网格,并选择当前子图。
  24. plt.scatter(x, means, c=COLORS[0], s=1): 绘制散点图,x 轴为帧数,y 轴为均值,颜色为 COLORS[0],点的大小为 1。
  25. 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。
  26. 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。
  27. plt.title('block {}'.format(idx+1)): 设置子图的标题为当前块的编号。
  28. plt.savefig(save_path.replace('.mp4', '_curves.png'), dpi=200): 保存当前子图为图片文件。

接下来,代码开始处理视频帧,将分析结果叠加到视频帧上,并保存为新的视频文件。

  1. cap = cv2.VideoCapture(video_path): 打开视频文件。

  2. fps = int(cap.get(cv2.CAP_PROP_FPS)): 获取视频的帧率。

  3. width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)): 获取视频的宽度。

  4. height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)): 获取视频的高度。

  5. down_sample_ratio = 2: 设置下采样比例。

  6. maxf = max(int(cap.get(cv2.CAP_PROP_FRAME_COUNT)), 1): 获取视频的总帧数。

  7. save_path = '/'.join(video_path.split('/')[:-1]) + '/processed_videos': 构建保存处理后视频的文件夹路径。

  8. if not os.path.exists(save_path):: 如果保存路径不存在。

  9. os.mkdir(save_path): 创建保存路径。

  10. file_type = video_path.split('/')[-1].split('.')[-1]: 获取视频文件的扩展名。

  11. file_name = video_path.split('/')[-1][:-len(file_type)-1]: 获取视频文件名。

  12. save_path = save_path + '/' + file_name + '_process.mp4': 构建保存处理后视频的文件路径。

  13. videoWriter = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*'mp4v'), fps//down_sample_ratio, (width, height), True): 创建视频写入器,指定输出视频的参数。

  14. block_state = ['O'] * self.num_blocks: 创建一个长度为 num_blocks 的列表,用于存储每个块的状态。

  15. block_color = [(100,100,100)] * self.num_blocks: 创建一个长度为 num_blocks 的列表,用于存储每个块的颜色。

  16. value_by_block = [0] * self.num_blocks: 创建一个长度为 num_blocks 的列表,用于存储每个块的导数值。

  17. for frame_idx in tqdm(np.arange(maxf)):: 遍历视频的每一帧。

  18. ret, frame = cap.read(): 读取一帧视频。

  19. if ret:: 如果成功读取到一帧。

  20. output_frame = frame.copy(): 复制当前帧作为输出帧。

  21. for block_idx in range(self.num_blocks):: 遍历每个块的索引。

  22. if frame_idx in x:: 如果当前帧的索引在 x 中。

  23. x_idx = int(np.where(x==frame_idx)[0][0]): 找到当前帧在 x 中的索引。

  24. center = centers_by_frame[block_idx][min(x_idx, len(centers_by_frame[block_idx])-1)]: 根据当前块和 x_idx 的索引,从中心坐标序列中取出对应的中心坐标。

  25. if x_idx in arg_extremas[block_idx]:: 如果 x_idx 在当前块的极值索引中。

  26. value_by_block[block_idx] = derivatives[block_idx][arg_extremas[block_idx]==x_idx]: 将当前块的导数值赋值给 value_by_block 列表中对应的元素。

  27. if np.abs(value_by_block[block_idx]) > self.der_th:: 如果当前块的导数值的绝对值大于阈值。

  28. if value_by_block[block_idx]>0:: 如果当前块的导数值大于 0。

  29. block_state[block_idx] = u'>': 将当前块的状态设置为 '>'。

  30. block_color[block_idx] = (0,0,255): 将当前块的颜色设置为红色。

  31. else:: 否则。

  32. block_state[block_idx] = u'<': 将当前块的状态设置为 '<'。

  33. block_color[block_idx] = (0,255,0): 将当前块的颜色设置为绿色。

  34. else:: 否则。

  35. block_state[block_idx] = u'-': 将当前块的状态设置为 '-'。

  36. block_color[block_idx] = (100,100,100): 将当前块的颜色设置为灰色。

  37. if frame_idx>=x.min():: 如果当前帧的索引大于等于 x 中的最小值。

  38. cv2.putText(output_frame, block_state[block_idx], center[[1,0]], cv2.FONT_HERSHEY_SIMPLEX, 1, block_color[block_idx], 2): 在输出帧上绘制当前块的状态。

  39. if frame_idx % down_sample_ratio != 0:: 如果当前帧不是下采样的帧。

  40. continue: 跳过当前循环,继续下一次循环。

  41. else:: 否则。

  42. videoWriter.write(output_frame.astype('uint8')): 将输出帧写入视频文件。

  43. cap.release(): 释放视频文件。

  44. videoWriter.release(): 释放视频写入器。

这段代码利用巴特沃斯滤波器对视频帧的像素均值进行平滑处理,然后找到局部极值并计算导数,从而判断每个块的运动状态。通过在视频帧上绘制状态标记,可以直观地展示分析结果,这在运动分析、行为识别等领域具有应用价值。

Python 视频分析代码详解:使用巴特沃斯滤波器检测运动

原文地址: https://www.cveoy.top/t/topic/fRbx 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录