性能测试时,日常需要对视频分成多帧标定启动启动时间(ms)
核心后端实现:
pythonimport 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}')
核心前端展示实现:
vuemethods: { 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 许可协议。转载请注明出处!