ffmpeg-python icon indicating copy to clipboard operation
ffmpeg-python copied to clipboard

Loading into memory and processing

Open lravindr opened this issue 5 years ago • 3 comments

I have a use case, where I need to process the entire video. The transformation for each of the frames is different - adding an artifact to the original frame which is pre-computed. I want to read the entire video into a numpy array (these videos are short - less than 30 seconds) and I have 32GB memory host.

In fact, I have this accomplished, by writing the images to a directory and operating on the individual frames and write back. But this is time consuming - takes about a minute.

Here is the sample code:

    ffmpeg.input(inp_video).output('dir/%03d.bmp').run()

    for f in os.listdir(file_dir):
      if "bmp" in f:
           # Add a red block
	   image = cv2.imread(file_dir+'/'+f)
	   image[1:100,1:100,:] = [255,0,0]
	   cv2.imwrite(file_dir+'/'+f, image)
    
   ffmpeg.input('dir/%03d.bmp').output(out).overwrite_output().run()

Are there ways to fasten this process by loading the entire video into memory and operating?

lravindr avatar Jul 27 '20 03:07 lravindr

@lravindr Have you found some faster ways to deal with this case ?

XiaoLaoDi avatar Jun 27 '22 03:06 XiaoLaoDi

Unfortunately, I didn't find a way to fasten this process.

lravindr avatar Jun 27 '22 06:06 lravindr

@lravindr How about grabbing frames with cv2 instead of writing temporary images ?

Here's sample code. In my environment, I got about 3x faster improvement.

# ffmpeg-python issue 401 solution
# https://github.com/kkroening/ffmpeg-python/issues/401

import os
import sys
from functools import wraps
import logging
import time

import ffmpeg
import cv2

# =============================================================================
# Performance logger
# =============================================================================
time_logger = logging.getLogger("perf_measure")
time_logger.setLevel(level=logging.DEBUG)
_handler = logging.FileHandler(filename="perf_measurement.log")
time_logger.addHandler(_handler)


def time_it(func):
    """Time func"""

    @wraps(func)
    def wrapper(*args, **kargs):
        _start = time.perf_counter()
        _result = func(*args, **kargs)
        _elapsed_time = time.perf_counter() - _start
        _elapsed_time = round(_elapsed_time, 3)
        time_logger.info(
            f"Time It: {func.__name__} - processed {_elapsed_time} [s]")
        return _result

    return wrapper


# =============================================================================
# Main function
# =============================================================================


@time_it
def sample_func(temp_dir: str, input_path: str, output_path: str) -> None:
    """sample func from Iravindr
        https://github.com/kkroening/ffmpeg-python/issues/401#issue-665948504
    """
    print("export bmp")
    ffmpeg.input(input_path).output(f'{temp_dir}/%03d.bmp').run()

    print("write images")
    for f in os.listdir(temp_dir):
        if "bmp" in f:
            # Add a red block
            image = cv2.imread(temp_dir + '/' + f)
            image[1:100, 1:100, :] = [255, 0, 0]
            cv2.imwrite(temp_dir + '/' + f, image)

    print("output")
    ffmpeg.input(f'{temp_dir}/%03d.bmp').output(
        output_path).overwrite_output().run()


@time_it
def proposal_func(temp_dir: str, input_path: str, output_path: str) -> None:
    """proposal func with cv2 grabbing"""
    print("start processing")
    cap = cv2.VideoCapture(input_path)
    if not cap.isOpened():
        raise ValueError("Could not open video file")

    frame_width = int(cap.get(3))
    frame_height = int(cap.get(4))
    out = cv2.VideoWriter(output_path,
                          cv2.VideoWriter_fourcc('M', 'P', '4', 'V'), 30,
                          (frame_width, frame_height))

    while cap.isOpened():
        ret, frame = cap.read()
        if ret == True:
            # Add a red block
            frame[1:100, 1:100, :] = [255, 0, 0]
            out.write(frame)
        else:
            print("end of video")
            break

    cap.release()
    out.release()
    cv2.destroyAllWindows()


if __name__ == "__main__":
    temp_dir, input_path, output_path = sys.argv[1:]

    sample_func(temp_dir, input_path, output_path)
    output_path_proposal = output_path.replace(".mp4", "_2.mp4")
    proposal_func(temp_dir, input_path, output_path_proposal)

executing above code, then I got this log file.

# perf_measurement.log
Time It: sample_func - processed 8.013 [s]
Time It: proposal_func - processed 3.096 [s]

I used sample video whose duration is 30secs / fps is 30 / resolusion is 1280x720

sota0121 avatar Jun 28 '22 12:06 sota0121