ffmpeg-python
ffmpeg-python copied to clipboard
Loading into memory and processing
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 Have you found some faster ways to deal with this case ?
Unfortunately, I didn't find a way to fasten this process.
@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