diff options
author | Evgeny Zinoviev <me@ch1p.io> | 2022-06-07 01:03:37 +0300 |
---|---|---|
committer | Evgeny Zinoviev <me@ch1p.io> | 2022-06-07 01:03:37 +0300 |
commit | 2fc3d44a03d022e9ce0afc9c2d4f0255c0011dbf (patch) | |
tree | 625803118087ba8ccdb22d9db11b0b134efd4c25 | |
parent | ea0ac514650972fafef13726da2e6f74fdfe11cc (diff) |
tools: add some tools to automate video analyzing
-rwxr-xr-x | tools/process-motion-timecodes.py | 77 | ||||
-rwxr-xr-x | tools/roi-visualize.php | 114 |
2 files changed, 191 insertions, 0 deletions
diff --git a/tools/process-motion-timecodes.py b/tools/process-motion-timecodes.py new file mode 100755 index 0000000..8c9d5fc --- /dev/null +++ b/tools/process-motion-timecodes.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 +import os.path + +from argparse import ArgumentParser +from datetime import datetime, timedelta + +DATETIME_FORMAT = '%Y-%m-%d-%H.%M.%S' + + +def chunks(lst, n): + for i in range(0, len(lst), n): + yield lst[i:i + n] + + +def time2seconds(time: str) -> int: + time, frac = time.split('.') + frac = int(frac) + + h, m, s = [int(i) for i in time.split(':')] + + return round(s + m*60 + h*3600 + frac/1000) + + +def filename_to_datetime(filename: str) -> datetime: + filename = os.path.basename(filename).replace('record_', '').replace('.mp4', '') + return datetime.strptime(filename, DATETIME_FORMAT) + + +if __name__ == '__main__': + parser = ArgumentParser() + parser.add_argument('--source-filename', type=str, required=True, + help='recording filename') + parser.add_argument('--timecodes', type=str, required=True, + help='timecodes') + parser.add_argument('--padding', type=int, default=2, + help='amount of seconds to add before and after each fragment') + arg = parser.parse_args() + + if arg.padding < 0: + raise ValueError('invalid padding') + + timecodes = arg.timecodes.split(',') + if len(timecodes) % 2 != 0: + raise ValueError('invalid number of timecodes') + + timecodes = list(map(time2seconds, timecodes)) + timecodes = list(chunks(timecodes, 2)) + + # sort out invalid fragments (dvr-scan returns them sometimes, idk why...) + timecodes = list(filter(lambda f: f[0] < f[1], timecodes)) + + file_dt = filename_to_datetime(arg.source_filename) + + # https://stackoverflow.com/a/43600953 + timecodes.sort(key=lambda interval: interval[0]) + merged = [timecodes[0]] + for current in timecodes: + previous = merged[-1] + if current[0] <= previous[1]: + previous[1] = max(previous[1], current[1]) + else: + merged.append(current) + + for fragment in merged: + start, end = fragment + + start -= arg.padding + end += arg.padding + + if start < 0: + start = 0 + + dt1 = (file_dt + timedelta(seconds=start)).strftime(DATETIME_FORMAT) + dt2 = (file_dt + timedelta(seconds=end)).strftime(DATETIME_FORMAT) + filename = f'{dt1}__{dt2}.mp4' + + print(f'{start} {end} {filename}') diff --git a/tools/roi-visualize.php b/tools/roi-visualize.php new file mode 100755 index 0000000..9677303 --- /dev/null +++ b/tools/roi-visualize.php @@ -0,0 +1,114 @@ +#!/usr/bin/env php +<?php + +function fatal(string $message) { + fprintf(STDERR, $message); + exit(1); +} + +function parse_roi_input(string $file): array { + if (!file_exists($file)) + throw new Error("file $file does not exists"); + + $lines = file($file); + $lines = array_map('trim', $lines); + $lines = array_filter($lines, fn($line) => $line != '' && $line[0] != '#'); + $lines = array_map(fn($line) => array_map('intval', explode(' ', $line)), $lines); + foreach ($lines as $points) { + if (count($points) != 4) + throw new Exception("invalid line: ".implode(' ', $points)); + } + + return $lines; +} + +function hex2rgb(int $color): array { + $r = ($color >> 16) & 0xff; + $g = ($color >> 8) & 0xff; + $b = $color & 0xff; + return [$r, $g, $b]; +} + +function imageopen(string $filename) { + $size = getimagesize($filename); + $types = [ + 2 => 'jpeg', + 3 => 'png' + ]; + if (!$size || !isset($types[$size[2]])) + return false; + + $f = 'imagecreatefrom'.$types[$size[2]]; + return call_user_func($f, $filename); +} + +error_reporting(E_ALL); +ini_set('display_errors', 1); + +$colors = [ + 0xff0000, + 0x00ff00, + 0x0000ff, + 0xffff00, + 0xff00ff, + 0x00ffff, +]; + +if ($argc < 2) + fatal("usage: {$argv[0]} --roi FILE --input PATH --output PATH\n"); + +array_shift($argv); +while (count($argv) > 0) { + switch ($argv[0]) { + case '--roi': + array_shift($argv); + $roi_file = array_shift($argv); + break; + + case '--input': + array_shift($argv); + $input = array_shift($argv); + break; + + case '--output': + array_shift($argv); + $output = array_shift($argv); + break; + + default: + fatal('unsupported argument: '.$argv[0]); + } +} + +if (!$roi_file) + throw new Exception("--roi is not specified"); + +if (!$input) + throw new Exception('--input is not specified'); + +if (!$output) + throw new Exception('--output is not specified'); + +$regions = parse_roi_input($roi_file); +$img = imageopen($input); +if (!$img) + throw new Exception("failed to open image"); + +$imgw = imagesx($img); +$imgh = imagesy($img); + +foreach ($regions as $i => $region) { + list($r, $g, $b) = hex2rgb($colors[$i]); + + if ($region[0]+$region[2] > $imgw || $region[1]+$region[3] > $imgh) + throw new Exception('error: invalid region (line '.($i+1).')'); + + $col = imagecolorallocatealpha($img, $r, $g, $b, 50); + imagerectangle($img, $region[0], $region[1], $region[0]+$region[2], $region[1]+$region[3], $col); + + $col = imagecolorallocatealpha($img, $r, $g, $b, 90); + imagefilledrectangle($img, $region[0]+1, $region[1]+1, $region[0]+$region[2]-2, $region[1]+$region[3]-2, $col); +} + +imagejpeg($img, $output, 97); +echo "saved to $output\n"; |