"""Обновить числовую таблицу VIDEO-001 по сохранённым результатам.""" from __future__ import annotations import argparse from collections import defaultdict import json from pathlib import Path MATRIX = (((320, 240), (5, 10, 15)), ((640, 360), (5, 10, 15)), ((640, 480), (5, 10)), ((854, 480), (5, 10)), ((1280, 720), (5,))) # Это качественные оценки контрольных кадров, а не результат автоматической метрики. QUALITY = { (320, 240, "jpeg"): "Ближние машины и полоса видны; дальние предметы мелки", (320, 240, "160k"): "Ближние машины и направление видны; мелкие детали ограничены", (320, 240, "320k"): "Почти тот же вид, что при 160k", (640, 360, "jpeg"): "Машины и разметка видны; крупный поток", (640, 360, "160k"): "Машины видны; текстура дороги размыта при движении", (640, 360, "320k"): "Машины, полоса и направление различимы", (640, 480, "jpeg"): "Машины видны; чёрные поля 60 пикселей", (640, 480, "160k"): "Машины видны; поля и размытие деталей", (640, 480, "320k"): "Машины видны; полезная область как 640×360", (854, 480, "jpeg"): "Машины и разметка чётче; крупный поток", (854, 480, "160k"): "Ближние машины видны; дальние детали размыты", (854, 480, "320k"): "Машины, полосы и направление хорошо различимы", (1280, 720, "jpeg"): "Машины и разметка подробны; поток велик", (1280, 720, "160k"): "Машины видны; разрешение не даёт ожидаемой детализации", (1280, 720, "320k"): "Машины и разметка видны; дальние надписи не проверены", } def main() -> None: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--comparison-160k", type=Path, required=True) parser.add_argument("--comparison-320k", type=Path, required=True) parser.add_argument("--report", type=Path, required=True) arguments = parser.parse_args() datasets = { code: json.loads(path.read_text(encoding="utf-8")) for code, path in (("160k", arguments.comparison_160k), ("320k", arguments.comparison_320k)) } if datasets["160k"]["source_sha256"] != datasets["320k"]["source_sha256"]: raise ValueError("Наборы сравнения используют разные исходные ролики") if datasets["160k"]["windows"] != datasets["320k"]["windows"]: raise ValueError("Наборы сравнения используют разные участки ролика") cases = defaultdict(list) for code, dataset in datasets.items(): for case in dataset["cases"]: key = (tuple(case["resolution"]), case["fps"], case["codec"], code) cases[key].append(case) rows = [ "| Разрешение | FPS | Кодек | Средний поток | Пик 1 с | Кодирование | " "Декодирование | Качество для оператора | FITS CURRENT RADIO BUDGET |", "|---|---:|---|---:|---:|---:|---:|---|---|", ] for resolution, rates in MATRIX: for fps in rates: for codec, code, label, quality_code in ( ("JPEG_INDEPENDENT", "160k", "JPEG q35", "jpeg"), ("H264_LIBX264", "160k", "H.264 160k", "160k"), ("H264_LIBX264", "320k", "H.264 320k", "320k"), ): pair = cases[(resolution, fps, codec, code)] if len(pair) != 2: raise ValueError(f"Ожидались два участка: {resolution}, {fps}, {label}") average = max(case["average_bitrate_bps"] for case in pair) / 1000 peak = max(case["peak_one_second_bitrate_bps"] for case in pair) / 1000 encode = max(case["encode_time_per_frame_s"]["mean"] for case in pair) * 1000 decode = max(case["decode_time_per_frame_s"]["mean"] for case in pair) * 1000 if any(case["radio_screen"]["fits_current_radio_budget"] != "NO_ON_OBSERVED_BURST_SCREEN" for case in pair): raise ValueError(f"Недостаточно оснований для НЕТ: {resolution}, {fps}, {label}") quality = QUALITY[(*resolution, quality_code)] encode_text = f"{encode:.1f}".replace(".", ",") decode_text = f"{decode:.1f}".replace(".", ",") rows.append(f"| {resolution[0]}×{resolution[1]} | {fps} | {label} | " f"{average:.0f} | {peak:.0f} | " f"{encode_text} | {decode_text} | {quality} | НЕТ |") report = arguments.report.read_text(encoding="utf-8") start = report.index("| Разрешение | FPS | Кодек |") end = report.index("\n\n", start) arguments.report.write_text(report[:start] + "\n".join(rows) + report[end:], encoding="utf-8") print(f"Обновлено строк таблицы: {len(rows) - 2}") if __name__ == "__main__": main()