OpenCV 输出文件为空

问题描述 投票:0回答:1

我想在一个视频中以 4:3 的格式显示 12 个视频。下面的代码运行没有错误,但输出文件始终为空。我尝试了以下编码器格式:

fourcc = cv2.VideoWriter_fourcc(*'mp4v')
fourcc = cv2.VideoWriter_fourcc(*'avc1')

注意:视频的持续时间并不完全相同。

我在 Windows10 上使用 Pycharm、Python 3.9、opencv-python=4.6.066,ffmpeg 安装了 PATH 变量集,264 库似乎加载正常。

import cv2
import numpy as np
import math
from tqdm import tqdm

# Set the dimensions of the merged video
video_width = 1920
video_height = 1080

# Set the dimensions of each video
sub_video_width = 480
sub_video_height = 360

# Set the margin between each video
margin = 20

# Create a black background frame
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']

# Calculate the number of rows and columns
num_videos = len(file_name_list)
num_rows = 3
num_cols = 4
num_total = num_rows * num_cols

# Determine the size of each sub-clip
sub_video_x = sub_video_width + margin
sub_video_y = sub_video_height + margin

# Calculate the total video duration
total_duration = 0
for filename in file_name_list:
    cap = cv2.VideoCapture(filename)
    total_duration += cap.get(cv2.CAP_PROP_FRAME_COUNT) / cap.get(cv2.CAP_PROP_FPS)
    cap.release()

# Initialize the final video
#fourcc = cv2.VideoWriter_fourcc(*'mp4v')
#out = cv2.VideoWriter('merged_video.mp4', fourcc, 30, (video_width, video_height))

fourcc = cv2.VideoWriter_fourcc(*'avc1')
out = cv2.VideoWriter('merged_video.mp4', fourcc, 30, (video_width, video_height))

# Iterate over each row and column
for i in tqdm(range(num_rows), desc='Processing rows'):
    for j in tqdm(range(num_cols), desc='Processing columns'):
        # Calculate the index of the video to be inserted
        video_index = i * num_cols + j

        # If there is no video at this index, skip to the next one
        if video_index >= num_videos:
            continue

        # Load the sub-clip and resize it
        cap = cv2.VideoCapture(file_name_list[video_index])
        frames = []
        while True:
            ret, frame = cap.read()
            if not ret:
                break
            frames.append(frame)
        cap.release()
        clip = cv2.resize(frames[0], (sub_video_width, sub_video_height))
        for frame in frames[1:]:
            frame = cv2.resize(frame, (sub_video_width, sub_video_height))
            clip = np.concatenate((clip, frame), axis=1)

        # Calculate the position of the sub-clip
        x = sub_video_x * j + margin
        y = sub_video_y * i + margin

        # Add the filename to the sub-clip
        font = cv2.FONT_HERSHEY_SIMPLEX
        text = file_name_list[video_index]
        textsize = cv2.getTextSize(text, font, 1, 2)[0]
        text_x = x + sub_video_width - textsize[0] - margin
        text_y = y + sub_video_height - textsize[1] - margin
        cv2.putText(clip, text, (text_x, text_y), font, 1, (255, 255, 255), 2, cv2.LINE_AA)

        # Add the sub-clip to the final video
        out.write(clip) # write the sub-clip to the output video

# Release the final video
out.release()
python opencv ffmpeg windows-10 h.264
1个回答
0
投票

输出的视频文件是空的,因为

clip.shape
out
的分辨率不匹配。

out
的分辨率在
out = cv2.VideoWriter('merged_video.mp4', fourcc, 30, (video_width, video_height))
中定义为
1920x1080
.
执行
clip
out.write(clip)
的形状必须是
(1080, 1920, 3)
.

clip = np.concatenate((clip, frame), axis=1)
沿水平轴连接子帧,因此结果帧比
1920
像素宽很多。


我们最好打开 12 个

cv2.VideoCapture
对象,而不是尝试将所有帧放在一个列表中,然后连接所有帧。

  • 从每个视频文件中读取一帧——一帧来自
    CAM010000000_compressed.mp4
    ,一帧来自
    CAM020000000_compressed.mp4
    ...还有一帧来自
    CAM120000000_compressed.mp4
  • 并排排列子框架以形成 4x3
    clip
    马赛克框架。
  • 将马赛克
    clip
    帧写入输出视频文件。

假设这 12 个文件可能有不同的持续时间,解决方案可能会有点复杂 - 我们必须使用前一帧以防一个文件结束而其他文件没有结束。

注意主循环不能迭代行和列——我们必须迭代输出视频文件的帧(我们不能返回并更新已经写入的视频帧)。


更新的代码示例:

import cv2
import numpy as np
#import math
#from tqdm import tqdm

# Set the dimensions of the merged video
video_width = 1920
video_height = 1080

# Set the dimensions of each video
sub_video_width = 480
sub_video_height = 360

# Set the margin between each video
margin = 20

# Create a black background frame
clip = 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']

# Calculate the number of rows and columns
num_videos = len(file_name_list)
num_rows = 3
num_cols = 4
num_total = num_rows * num_cols

# Determine the size of each sub-clip
sub_video_x = sub_video_width + margin
sub_video_y = sub_video_height + margin

# Build a list of 12 cv2.VideoCapture objects 
cap_list = []
for filename in file_name_list:
    cap = cv2.VideoCapture(filename)
    cap_list.append(cap)

# Initialize the final video
#fourcc = cv2.VideoWriter_fourcc(*'mp4v')
#out = cv2.VideoWriter('merged_video.mp4', fourcc, 1, (video_width, video_height))

fourcc = cv2.VideoWriter_fourcc(*'avc1')  # In Windows, requires openh264-1.8.0-win64.dll
out = cv2.VideoWriter('merged_video.mp4', fourcc, 1, (video_width, video_height))  # Use 1 fps for testing


# Iterate over each row and column
sub_frames = [None]*num_total  # Initialize with Nones
while True:
    any_ret = False
    for i in range(num_rows):
        for j in range(num_cols):
            # Calculate the index of the video to be inserted
            video_index = i * num_cols + j

            ret, frame = cap_list[video_index].read()  # Read frame from "cap object" that matches video_index

            if ret:
                frame = cv2.resize(frame, (sub_video_width, sub_video_height))  # Resize the "sub-frame" to the desired resolution.
                sub_frames[video_index] = frame  # Store frame in the list.

            any_ret |= ret  # Going to be False only when ret of from all videos is false. 

    if not any_ret:
        break  # Break the main loop when all video files are ended.

    for i in range(num_rows):
        for j in range(num_cols):
            # Calculate the index of the video to be inserted
            video_index = i * num_cols + j
        
            # Place the sub_frame in the correct location in "clip 4x3 mosaic frame"
            clip[i*sub_video_height:i*sub_video_height+sub_video_height, j*sub_video_width:j*sub_video_width+sub_video_width, :] = sub_frames[video_index]

            # Calculate the position of the sub-clip
            x = sub_video_width * j + margin//4  # sub_video_x * j + margin
            y = sub_video_height * i + margin  # sub_video_y * i + margin

            # Add the filename to the sub-clip
            font = cv2.FONT_HERSHEY_SIMPLEX
            text = file_name_list[video_index]
            textsize = cv2.getTextSize(text, font, 1, 2)[0]
            text_x = x #+ sub_video_width - textsize[0] - margin ???
            text_y = y + textsize[1] #+ sub_video_height - textsize[1] - margin ???
            cv2.putText(clip, text, (text_x, text_y), font, 1, (255, 255, 255), 2, cv2.LINE_AA)

    # Add the sub-clip to the final video
    out.write(clip)  # write the clip 4x3 mosaic frame to the output video

# Release the final video
out.release()

for cap in cap_list:
    cap.release()

为了测试,我们可以使用 FFmpeg CLI 构建合成视频文件。
如果代码示例不起作用,请使用合成视频文件 - 它们使解决方案可重现(不依赖于只有您可以访问的视频文件)。

创建 12 个示例视频文件:

ffmpeg -y -f lavfi -i testsrc=size=480x360:rate=1 -t 12 -vcodec libx264 CAM010000000_compressed.mp4

ffmpeg -y -f lavfi -i mandelbrot=size=480x360:rate=1 -t 10 -vcodec libx264 CAM020000000_compressed.mp4

ffmpeg -y -f lavfi -i color=blue:size=480x360:rate=1 -t 10 -vcodec libx264 CAM030000000_compressed.mp4

ffmpeg -y -f lavfi -i color=black:size=480x360:rate=1 -t 11 -vcodec libx264 CAM040000000_compressed.mp4

ffmpeg -y -f lavfi -i mandelbrot=size=480x360:rate=1 -t 9 -vcodec libx264 CAM050000000_compressed.mp4

ffmpeg -y -f lavfi -i mandelbrot=size=480x360:rate=1 -t 8 -vcodec libx264 CAM060000000_compressed.mp4

ffmpeg -y -f lavfi -i mandelbrot=size=480x360:rate=1 -t 10 -vcodec libx264 CAM070000000_compressed.mp4

ffmpeg -y -f lavfi -i mandelbrot=size=480x360:rate=1 -t 12 -vcodec libx264 CAM080000000_compressed.mp4

ffmpeg -y -f lavfi -i testsrc=size=480x360:rate=1 -t 10 -vcodec libx264 CAM090000000_compressed.mp4

ffmpeg -y -f lavfi -i testsrc=size=480x360:rate=1 -t 10 -vcodec libx264 CAM100000000_compressed.mp4

ffmpeg -y -f lavfi -i testsrc=size=480x360:rate=1 -t 10 -vcodec libx264 CAM110000000_compressed.mp4

ffmpeg -y -f lavfi -i testsrc=size=480x360:rate=1 -t 10 -vcodec libx264 CAM120000000_compressed.mp4


样本输出框架:

© www.soinside.com 2019 - 2024. All rights reserved.