Search code examples
pythonopencvvlcavifiji

Combining two .AVI movies with OpenCV unsuccessful


I am trying to use OpenCV to combine two .AVI videos, but having little success. I want a combined final video that plays the two videos sequentially.

The two input videos were created with Fiji's TrackMate algorithm. They are 6.6 and 6.2 MB, each only 10 seconds, but the output file is only 14 KB and does not play at all in VLC Media Player, so I am surely doing something wrong. Any input would be greatly appreciated: How do I combine two .AVI videos into a single one?

My code is below; it is adapted from this answer with what I think is the correct FourCC:

import cv2

path = '/Volumes/GQA HD/GQA Experiments/2023_01_27/'
file1 = 'TrackMate capture of 1.avi'
file2 = 'TrackMate capture of 2.avi'

file_combined = 'TrackMate Combined captures of IMG_0240.avi'

# Paths of videos
videos = [path+file1, path+file2]

# Create new video
fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
frameSize=(720,1280)
fps=30
video = cv2.VideoWriter(path+file_combined, fourcc, fps, frameSize)

# Write all frames sequentially to new video
for v in videos:
    curr_v = cv2.VideoCapture(v)
    while curr_v.isOpened():
        r, frame = curr_v.read()    # Get return value and curr frame of curr video
        if not r:
            break
        video.write(frame)          # Write the frame

video.release()                     # Save the video

Solution

  • frameSize should probably be (1280, 720) instead of (720, 1280).
    OpenCV size conventions is (width, height), when width is the number of columns in the horizontal dimension, and height is the number of row in the vertical dimension.

    We may resize frame to (width, height), before writing it, just in case:

    if frame.shape[0] != height or frame.shape[1] != width:
        frame = cv2.resize(frame, (width, height))
    
    video.write(frame)
    

    Complete code sample:

    import cv2
    
    do_create_sample_video_files = False
    
    if do_create_sample_video_files:
        # Create sample video files for testing (use FFmpeg CLI for creating synthetic video files).
        import subprocess as sp
        import shlex
        sp.run(shlex.split('ffmpeg -y -f lavfi -i testsrc=1280x720:rate=1:duration=300 -vcodec libx264 sample1.avi'))
        sp.run(shlex.split('ffmpeg -y -f lavfi -i testsrc=640x360:rate=1:duration=300 -vcodec libx264 sample2.avi'))  # Use other resolution for testing
    
    path = './'
    file1 = 'sample1.avi'
    file2 = 'sample2.avi'
    
    file_combined = 'CombinedSample1Sample2.avi'
    
    # Paths of videos
    videos = [path+file1, path+file2]
    
    # Create new video
    width, height = 1280, 720
    fourcc = cv2.VideoWriter_fourcc('M','J','P','G')
    frameSize = (width, height) #(720,1280)  <-- Probably supposed to be (1280, 720).
    fps=30
    video = cv2.VideoWriter(path+file_combined, fourcc, fps, frameSize)
    
    # Write all frames sequentially to new video
    for v in videos:
        curr_v = cv2.VideoCapture(v)
    
        while curr_v.isOpened():
            r, frame = curr_v.read()    # Get return value and curr frame of curr video
            if not r:
                break
    
            if frame.shape[0] != height or frame.shape[1] != width:
                # Resize frame to the resolution of the destination video if required.
                frame = cv2.resize(frame, (width, height))
    
            video.write(frame)          # Write the frame
    
    video.release()