OpenCV合并12个视频输出文件为空的技术求助
问题分析与修复方案
核心错误点
- 子视频帧处理逻辑完全错误:原代码把单个视频的所有帧横向拼接成一张静态长图,仅向输出写入一次,导致输出视频要么只有一帧,要么因尺寸不匹配写入失败,最终生成空文件。
- 未按布局逐帧合成:没有实现3行4列的动态拼接逻辑——即每帧都将对应子视频的画面放到背景的指定位置,再逐帧写入输出。
- 总时长计算逻辑错误:原代码将所有视频时长相加,实际应取最长视频的时长,保证所有视频都能完整播放。
- 文本绘制坐标错误:原代码用全局背景的坐标在拼接长图上写文本,导致位置完全偏移。
修复后的完整代码
import cv2 import numpy as np from tqdm import tqdm # 合成视频尺寸 video_width = 1920 video_height = 1080 # 子视频尺寸 sub_video_width = 480 sub_video_height = 360 # 视频间边距 margin = 20 # 初始化黑色背景 bg_frame = np.zeros((video_height, video_width, 3), dtype=np.uint8) file_name_list = [ 'CAM010000000_compressed.mp4', 'CAM020000000_compressed.mp4', 'CAM030000000_compressed.mp4', 'CAM040000000_compressed.mp4', 'CAM050000000_compressed.mp4', 'CAM060000000_compressed.mp4', 'CAM070000000_compressed.mp4', 'CAM080000000_compressed.mp4', 'CAM090000000_compressed.mp4', 'CAM100000000_compressed.mp4', 'CAM110000000_compressed.mp4', 'CAM120000000_compressed.mp4' ] num_rows = 3 num_cols = 4 # 打开所有视频捕获对象,获取每个视频的FPS和总帧数 caps = [] max_frames = 0 target_fps = 30 # 统一输出FPS for fname in file_name_list: cap = cv2.VideoCapture(fname) if not cap.isOpened(): print(f"无法打开视频: {fname}") exit() caps.append(cap) # 计算每个视频的总帧数(按目标FPS转换) frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) fps = cap.get(cv2.CAP_PROP_FPS) converted_frames = int(frame_count * target_fps / fps) if converted_frames > max_frames: max_frames = converted_frames # 初始化视频写入器 fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 或使用'avc1',确保系统支持 out = cv2.VideoWriter('merged_video.mp4', fourcc, target_fps, (video_width, video_height)) if not out.isOpened(): print("无法初始化视频写入器,请检查编码器或输出路径") exit() # 逐帧合成 for frame_idx in tqdm(range(max_frames), desc='合成视频帧'): # 每次合成前重置背景 current_bg = bg_frame.copy() for i in range(num_rows): for j in range(num_cols): video_idx = i * num_cols + j if video_idx >= len(caps): continue cap = caps[video_idx] # 计算当前视频需要读取的帧位置(适配不同FPS) original_fps = cap.get(cv2.CAP_PROP_FPS) original_frame_idx = int(frame_idx * original_fps / target_fps) # 设置读取位置 cap.set(cv2.CAP_PROP_POS_FRAMES, original_frame_idx) ret, frame = cap.read() if ret: # resize子视频帧 resized_frame = cv2.resize(frame, (sub_video_width, sub_video_height)) # 计算子视频在背景中的位置 x = j * (sub_video_width + margin) + margin y = i * (sub_video_height + margin) + margin # 将子视频帧放入背景 current_bg[y:y+sub_video_height, x:x+sub_video_width] = resized_frame # 在子视频帧上添加文件名(局部坐标) font = cv2.FONT_HERSHEY_SIMPLEX text = file_name_list[video_idx] textsize = cv2.getTextSize(text, font, 0.6, 2)[0] # 文本位置:右下角,留边距 text_x = sub_video_width - textsize[0] - 10 text_y = sub_video_height - textsize[1] - 10 cv2.putText(resized_frame, text, (text_x, text_y), font, 0.6, (255,255,255), 2, cv2.LINE_AA) # 写入合成后的帧 out.write(current_bg) # 释放资源 for cap in caps: cap.release() out.release() cv2.destroyAllWindows()
关键修复说明
- 逐帧动态合成:循环生成每帧输出画面,将对应子视频的当前帧放到背景的3行4列位置,保证输出是完整的动态视频。
- 适配不同FPS与时长:将所有视频的帧转换为统一目标FPS,取最长视频的帧数作为总输出帧数,确保所有视频都能完整播放。
- 修正文本绘制:在子视频帧的局部坐标上绘制文件名,避免位置偏移。
- 增加错误检查:添加视频捕获和写入器的初始化检查,便于快速排查问题。
内容的提问来源于stack exchange,提问作者Marco Bob
相关产品推荐
相关产品推荐

