You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:19:58