media_management_scripts/codec_visualizer/codecVis/__main__.py

192 lines
7.3 KiB
Python
Executable File

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" Author: Hendrik Schutter, localhorst@mosad.xyz
Date of creation: 2022/01/22
Date of last modification: 2022/08/25
"""
import os
import platform
import sys
import time
import subprocess
import shlex
import shutil
import glob
import PIL
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
temp_dir = os.path.join(os.getcwd(), "_tempCodecVisualizer/")
def create_temp_dir():
try:
os.mkdir(temp_dir)
except OSError:
print("Unable to create temp dir: %s" % temp_dir)
sys.exit()
def delete_temp_dir():
try:
shutil.rmtree(temp_dir)
except OSError:
pass
def get_length(filename):
result = subprocess.run(["ffprobe", "-v", "error", "-show_entries",
"format=duration", "-of",
"default=noprint_wrappers=1:nokey=1", filename],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return float(result.stdout)
def get_codec(filename):
result = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "v:0",
"-show_entries", "stream=codec_name",
"-of", "default=noprint_wrappers=1:nokey=1", filename],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
return str(result.stdout.decode("utf-8")).rstrip("\n")
def extract_frame(video_file, time_offset, output_file):
cmd = "ffmpeg -y -ss " + time.strftime('%H:%M:%S', time.gmtime(time_offset)) + ".00 -i " + str(video_file) + " -frames:v 1 " + str(output_file)
devnull = open(os.devnull, 'w')
subprocess.call(shlex.split(cmd),stdout=devnull, stderr=devnull)
def get_font_path():
if platform.system() == "Darwin":
# Assume DejaVu to be installed via Homebrew
path = "~/Library/Fonts/DejaVuSans-Bold.ttf"
return os.path.expanduser(path)
# Default platform: Linux
path = "/usr/share/fonts"
files = glob.glob(path + "/**/DejaVuSans-Bold.ttf", recursive = True)
return files[0]
def zoom_at(img, crop_factor):
image_width, image_height = img.size
img = img.crop((int(image_width/2) - int(image_width/crop_factor) / (crop_factor * 2), int(image_height/2) - int(image_height/crop_factor) / (crop_factor * 2),
int(image_width/2) + int(image_width/crop_factor) / (crop_factor * 2), int(image_height/2) + int(image_height/crop_factor) / (crop_factor * 2)))
return img.resize((int(image_width/crop_factor*2), int(image_height/crop_factor*2)), Image.Resampling.LANCZOS)
def create_collage(images_A, images_B, statistics, output_file):
image_width, image_height = images_A[0].size
x_offset = int(image_width/15)
y_offset = int(image_height/1.5)
output_width = image_width *2 + x_offset
output_height = image_height *4 + y_offset
output_image = Image.new('RGB', (output_width, output_height))
font_file = get_font_path()
draw = ImageDraw.Draw(output_image)
text = "codec visualizer"
draw.text((x_offset, 0), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/10)))
text = "reduced data size: " + str(statistics["compression_rate"]) + "%"
draw.text((x_offset + image_width , int(image_height/18)), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/18)))
text = str(statistics["codec"][0])
draw.text((x_offset , int(image_height/18) * 5), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25)))
text = str(statistics["codec"][1])
draw.text((x_offset + image_width , int(image_height/18) * 5), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25)))
text = str(statistics["filename"][0])
draw.text((x_offset , int(image_height/18) * 7), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25)))
text = str(statistics["filename"][1])
draw.text((x_offset + image_width , int(image_height/18) * 7), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25)))
text = str(statistics["size"][0])
draw.text((x_offset , int(image_height/18) * 9), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25)))
text = str(statistics["size"][1])
draw.text((x_offset + image_width , int(image_height/18) * 9), text,(255,255,255),font=ImageFont.truetype(font_file, int(image_width/25)))
i = 0
y = y_offset
for row in range(4):
output_image.paste(images_A[i], (int(x_offset/2), y))
output_image.paste(images_B[i], (int(x_offset/2) + image_width, y))
i += 1
y += image_height
i = 0
y = y_offset
crop_factor = 6
for row in range(4):
cropped_image = zoom_at(images_A[i], crop_factor)
cropped_image_with, _ = cropped_image.size
output_image.paste(cropped_image, (int(x_offset/2) + image_width - cropped_image_with , y))
cropped_image = zoom_at(images_B[i].transpose(PIL.Image.Transpose.FLIP_LEFT_RIGHT), crop_factor)
output_image.paste(cropped_image, (int(x_offset/2) + image_width, y))
i += 1
y += image_height
output_image.save(output_file)
def calc_compression_rate(size_A, size_B):
if size_A == size_B:
return 0
if size_A > size_B:
return int(100 - 100/size_A*size_B)
if size_B > size_A:
return int(100 - 100/size_B*size_A)
def human_readable_size(size, decimal_places=2):
for unit in ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']:
if size < 1024.0 or unit == 'PiB':
break
size /= 1024.0
return f"{size:.{decimal_places}f} {unit}"
def main():
if(len(sys.argv) != 3):
print("Bad usage!")
print("Usage: python ./codec_analyzer.py file file")
sys.exit()
file_A = sys.argv[1]
file_B = sys.argv[2]
if (int(get_length(file_A)) != int(get_length(file_B))):
print("video files have different lengths")
sys.exit()
video_lenght = int(get_length(file_A))
delete_temp_dir()
create_temp_dir()
stills_timestamps = []
stills_file_A = []
stills_file_B = []
stills_timestamps.append(int(video_lenght*0.15)) #don't get the intro
stills_timestamps.append(int(video_lenght*0.33))
stills_timestamps.append(int(video_lenght*0.50))
stills_timestamps.append(int(video_lenght*0.75)) #don't get the outro
for frame_timestamp in stills_timestamps:
still_file_A = temp_dir + "A_"+ str(frame_timestamp) + ".tif"
extract_frame(file_A, frame_timestamp, still_file_A)
stills_file_A.append(Image.open(still_file_A))
still_file_B = temp_dir + "B_"+ str(frame_timestamp) + ".tif"
extract_frame(file_B, frame_timestamp, still_file_B)
stills_file_B.append(Image.open(still_file_B))
file_statistics = {
"filename": [os.path.basename(file_A),os.path.basename(file_B)],
"size": [human_readable_size(os.path.getsize(file_A)), human_readable_size(os.path.getsize(file_B))],
"codec": [get_codec(file_A), get_codec(file_B)],
"compression_rate": calc_compression_rate(os.path.getsize(file_A), os.path.getsize(file_B))
}
create_collage(stills_file_A, stills_file_B, file_statistics, "output.png")
delete_temp_dir()
if __name__ == "__main__":
main()