编辑
2025-04-23
Python
00

目录

背景
实现

背景

性能测试时,日常需要对视频分成多帧标定启动启动时间(ms)

实现

核心后端实现:

python
import cv2 import os import logging class VideoProcessor: def __init__(self, video_path: str): """ 初始化视频处理器 Args: video_path (str): 视频文件路径 """ self.video_path = video_path self.file_name = os.path.basename(video_path).split('.')[0] self.dir_path = os.path.join(os.path.dirname(video_path), self.file_name) self.dir_path_th = None self.v = None def initialize_directories(self, mk_black_white=False): """ 初始化保存帧的目录 Args: mk_black_white (bool): 是否创建黑白帧保存目录 """ os.makedirs(self.dir_path, exist_ok=True) if mk_black_white: self.dir_path_th = os.path.join(os.path.dirname(self.video_path), self.file_name + '_th') os.makedirs(self.dir_path_th, exist_ok=True) def open_video(self): """ 打开视频文件 Raises: Exception: 如果视频文件无法打开 """ self.v = cv2.VideoCapture(self.video_path) if not self.v.isOpened(): raise Exception(f'Video[{self.video_path}] open failed!') def get_video_info(self) -> (float, int): """ 获取视频的帧率和总帧数 Returns: fps (float): 视频帧率 total_frame (int): 视频总帧数 """ total_frame = int(self.v.get(cv2.CAP_PROP_FRAME_COUNT)) # 获取总帧数 fps = self.v.get(cv2.CAP_PROP_FPS) # 获取帧率 return fps, total_frame def cv2black_white(self, frame, gray_threshold=None): """ 将图像转换为黑白图像 Args: frame: 图像帧 gray_threshold: 灰度阈值 Returns: th_frame: 黑白图像帧 """ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) if gray_threshold is not None: _, th_frame = cv2.threshold(gray, gray_threshold, 255, cv2.THRESH_BINARY) else: _, th_frame = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) return th_frame def split_video_frame(self, mk_black_white=False, gray_threshold=None) -> (float, int, str): """ 将视频分解为帧并保存 Args: mk_black_white (bool): 是否保存黑白帧 gray_threshold: 黑白图像的灰度阈值 Returns: fps (float): 视频帧率 total_frame (int): 视频总帧数 dir_path (str): 保存帧的目录路径 """ logging.info(f'即将对视频【{self.video_path}】进行分帧处理...') self.initialize_directories(mk_black_white=mk_black_white) self.open_video() fps, total_frame = self.get_video_info() idx = 0 while True: ok, frame = self.v.read() if ok: idx += 1 # 保存原始帧 cv2.imwrite(os.path.join(self.dir_path, f'{idx}.jpg'), frame) cv2.waitKey(1) # 保存黑白帧 if mk_black_white: th_frame = self.cv2black_white(frame, gray_threshold=gray_threshold) cv2.imwrite(os.path.join(self.dir_path_th, f'{idx}.jpg'), th_frame) cv2.waitKey(1) else: break self.v.release() return fps, total_frame, self.dir_path # 使用示例 if __name__ == "__main__": logging.basicConfig(level=logging.INFO) video_path = "/home/aab/android-10.1.10.671-open-plugin_163745_1.mp4" processor = VideoProcessor(video_path) fps, total_frame, dir_path = processor.split_video_frame(mk_black_white=False, gray_threshold=128) logging.info(f'视频帧率: {fps}, 总帧数: {total_frame}, 帧保存目录: {dir_path}')

核心前端展示实现:

vue
methods: { async showImgList (item, field) { console.log('item: ', item) const currSelected = [item.start_idx, item.end_idx].filter(idx => idx !== null) // 检查 img_list 是否存在并且是数组 if (!item.img_list || !Array.isArray(item.img_list)) { console.error('img_list is undefined or not an array:', item.img_list) alert('图片列表未加载或为空,请检查数据!') return; } const vm = this const dl = vm.openDialog({ title: '分帧图片', width: '80%', render: (h) => { return h(SelectableImg, { props: { imgList: item.img_list, // 确保 imgList 是一个数组 currSelected: currSelected, onItemSelected: async (e, idx) => { if (e.target.checked) { currSelected.push(idx + 1) } else { currSelected.splice(currSelected.indexOf(idx + 1), 1) } await vm.change_select_idx(item.id, currSelected).then(async data => { item.result = data.result item.start_idx = data.start_idx item.end_idx = data.end_idx dl.close(); await field.$view.reload() }).catch(_ => {}) } } }) } }) }, async change_select_idx (itemID, selected = []) { const vm = this; return new Promise(async (resolve, reject) => { if (selected.length < 2) { return; } if (selected.length !== 2) { alert(`请选择2个图片,当前选择[${selected.length}]个`) return; } let resp = await vm.serviceApi('app-perf', 'test_term_simple_video_result_item').post({ action: 'update_idx' }, {id: itemID, 'idx_list': selected.join(',')}) if (resp.data.ok) { await vm.$confirm(`耗时计算成功: [${resp.data.data.result}]!需要关闭并刷新数据吗?`).then( _ => resolve(resp.data.data) ).catch(reject); } }) }, async bindVideoToPerfMetrics(metrics, item) { const vm = this try { await vm.api('perf_metrics').patch({ id: metrics.id }, { perf_id: item.id }) vm.$Message.success('绑定成功') } catch (error) { vm.$Message.success('绑定失败') console.error('绑定过程中发生错误:', error) } } }

本文作者:lixf6

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!