#!/usr/bin/perl
use strict;
use warnings;
use Panotools::Script;
use File::Spec::Functions qw/splitpath abs2rel/;
use 5.010;

$ENV{PATH} = "$ENV{PATH}:/cygdrive/c/Program Files/Hugin/bin";

my $prefix = undef;
if ($ARGV[0] eq '-o') {shift @ARGV; $prefix = shift @ARGV}

my $path_template_pto = shift || '';
die "Usage: $0 [-o output] template.pto path/to/*/*/*.jpg" unless $path_template_pto =~ /\.pto$/i;

=pod

Deshake a sequence of images
Supply remapping parameters and the location of a reference photo using a Hugin PTO project
Bruno Postle <bruno@postle.net>, June 2018

Extract JPEG frames from an existing video like so:
ffmpeg -i webcam.mp4 webcam%08d.jpg

Create a video file from a sequence of JPEG files like so:
rm -f frames.txt; for FILE in *.jpg; do echo "file '$FILE'" >> frames.txt; done
ffmpeg -f concat -i frames.txt output.mp4

=cut

# Can't use File::Temp when mixing win32 exe files with cygwin
my $tmp_align_pto = '_tmp_align.pto';
my $tmp_extract_pto = '_tmp_extract.pto';

# Zero the existing image rotation in the template project, we will reapply after deshaking
my $pto_template = new Panotools::Script;
$pto_template->Read($path_template_pto);
my (@rpy_template) = ($pto_template->Image->[0]->r, $pto_template->Image->[0]->p, $pto_template->Image->[0]->y);
($pto_template->Image->[0]->{r}, $pto_template->Image->[0]->{p}, $pto_template->Image->[0]->{y}) = (0.0, 0.0, 0.0);

# Win32 align_image_stack can't cope with a full cygwin path for the reference image, but is ok with a relative path
my $path_anchor_image = $pto_template->Image->[0]->Path($path_template_pto);
$path_anchor_image = abs2rel($path_anchor_image);

my $inc = 1;
for my $path_image (@ARGV)
{
    # Give output images either the same names as the input images, or sequential defined by the -o option
    my @split = splitpath($path_image);
    my $basename = pop(@split);
    $basename = sprintf("$prefix%08d.jpg", $inc) if (defined $prefix);
    $inc++;

    # Don't worry, we won't overwrite existing files
    next if -e $basename;

    # Calculate shake for the current frame
    system('align_image_stack', '-c', 16, '-g', 10, '--use-given-order', '-f', $pto_template->Image->[0]->v, '-p', $tmp_align_pto, $path_anchor_image, $path_image);
    my $pto_align = new Panotools::Script;
    $pto_align->Read($tmp_align_pto);
    my (@rpy_shake) = ($pto_align->Image->[1]->r, $pto_align->Image->[1]->p, $pto_align->Image->[1]->y);

    # Apply calculated shake and reapply saved rotation to the template project
    my $pto_extract = $pto_template->Clone;
    $pto_extract->Transform(@rpy_shake);
    $pto_extract->Transform(@rpy_template);
    $pto_extract->Write($tmp_extract_pto);

    system('nona', '-m', 'JPEG', '-z', 75, '-o', $basename, $tmp_extract_pto, $path_image);
    system('touch', '-r', $path_image, $basename);
    say STDERR $basename;
}

unlink($tmp_align_pto, $tmp_extract_pto);