Path: blob/master/video-editor/benchmark_video_blur.py
641 views
import argparse1import datetime2import math3import os4import subprocess5import time6from collections import OrderedDict7from concurrent.futures import ThreadPoolExecutor8from functools import partial9from timeit import default_timer1011import cv212import requests131415def parse_arguments():16parser = argparse.ArgumentParser(description="Benchmark Video Blur")17parser.add_argument(18"--video-editor-url",19default="http://localhost:8081/process-video",20help="URL endpoint to process video files with Blur",21required=False,22)23parser.add_argument(24"--sample",25help="One or more samples separated by a comma, for example: 60,50,30",26required=False,27)28parser.add_argument(29"--video",30help="File path of video file to be processed with Blur, for example: assets/cars.mp4",31required=False,32)33parser.add_argument(34"--blur-output",35default="output/cars_blur.mp4",36help="Video file path where blur saves the already processed file",37required=False,38)39parser.add_argument(40"--benchmark-results",41default="benchmark_results.txt",42help="File to store benchmarking results",43required=False,44)45return parser.parse_args()464748def get_duration(args):49video = cv2.VideoCapture(args.video)50fps = video.get(cv2.CAP_PROP_FPS)51frame_count = video.get(cv2.CAP_PROP_FRAME_COUNT)52seconds = frame_count // fps53video_time = datetime.timedelta(seconds=seconds)54return str(video_time)555657def write_results(args, results):58file_path = args.benchmark_results5960# Check if the file exists61file_exists = os.path.exists(file_path)6263# Open the file in append mode if it exists, or create it if it doesn't64mode = "a" if file_exists else "w"6566# Get video duration utilizing OpenCV67length = get_duration(args)6869with open(file_path, mode) as file:70if not file_exists:71file.write(72"| {:<18} | {:<6} | {:<11} | {:>19} |\n".format(73"Video Duration (m)", "Sample", "Output Size", "Processing Time (s)"74)75)76file.write(77"| ------------------ | ------ | ----------- | ------------------- |\n"78)7980# Append or write the results81for result in results:82file.write(83"| {:>18} | {:>6} | {:>11} | {:>19.1f} |\n".format(84length, result["rate"], result["size"], result["duration"]85)86)878889def print_table(file_path):90with open(file_path) as file:91for line in file:92print(line, end="")939495def video_blur_api(fp, video_editor_url, exit_on_error=True):96fp.seek(0)97response = requests.post(98video_editor_url, data=dict(action="blur"), files=dict(upload=fp)99)100if response is None:101return {}102if response.status_code < 200 or response.status_code > 300:103print(response.text)104if exit_on_error:105exit(1)106return response.json(object_pairs_hook=OrderedDict)107108109def call_duration(path, video_editor_url):110now = default_timer()111with open(path, "rb") as fp:112video_blur_api(fp, video_editor_url=video_editor_url)113return default_timer() - now114115116def convert_size(size_bytes):117if size_bytes == 0:118return "0B"119sign = ""120if size_bytes < 0:121size_bytes *= -1122sign = "-"123size_name = ("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")124i = int(math.floor(math.log(size_bytes, 1024)))125p = math.pow(1024, i)126s = round(size_bytes / p, 2)127return f"{sign}{s} {size_name[i]}"128129130def benchmark(args, executor, sample_rate):131stats = list(132executor.map(133partial(call_duration, video_editor_url=args.video_editor_url), [args.video]134)135)136file_size = os.path.getsize(args.blur_output)137yield dict(rate=sample_rate, size=convert_size(file_size), duration=max(stats))138139140def check_api_access(api_url, max_wait_time=60, poll_interval=2):141"""142Waits for the API endpoint to become accessible within a specified time.143144Args:145api_url (str): The URL of the API endpoint to check.146max_wait_time (int): Maximum time to wait in seconds.147poll_interval (int): Interval for checking the API availability.148149Returns:150bool: True if the API is accessible within the specified time, False otherwise.151"""152start_time = time.time()153while time.time() - start_time < max_wait_time:154try:155# Try to make an HTTP request to the API endpoint156response = requests.options(api_url)157if response.status_code == 200:158# The API is accessible, return True159return True160except requests.exceptions.RequestException:161pass # Handle any exceptions if the request fails162163# Wait for the next poll_interval seconds before checking again164time.sleep(poll_interval)165166# If we reach here, it means the API didn't become accessible within max_wait_time167return False168169170def main(args):171rate_string = args.sample172number_strings = rate_string.split(",")173sample_rates = [int(x) for x in number_strings]174175with ThreadPoolExecutor(max_workers=1) as executor:176for sample_rate in sample_rates:177# Read the existing env.txt file and remove any existing SAMPLE entry178lines = []179with open("env.txt") as env_file:180for line in env_file:181if not line.startswith("SAMPLE="):182lines.append(line)183184with open("env.txt", "w") as env_file:185for line in lines:186env_file.write(line)187188# Add the new SAMPLE_RATE entry189env_file.write(f"SAMPLE={sample_rate}\n")190191# Process the video with the modified environment192cmd = "docker compose up --build"193subprocess.Popen(cmd, shell=True)194195if check_api_access(args.video_editor_url):196print("API is accessible, you can proceed with the benchmark.")197else:198print(199"API not accessible within the specified time. Take appropriate action."200)201202# Start Benchmark203results = list(benchmark(args, executor, sample_rate))204205cmd = "docker compose down"206subprocess.call(cmd, shell=True)207208# Write benchmark results to file to save progress.209write_results(args, results)210211print_table(args.benchmark_results)212213214if __name__ == "__main__":215args = parse_arguments()216217if not args.sample:218raise Exception("sample is required")219elif not args.video:220raise Exception("video is required")221else:222main(args)223224225