#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use Panotools::Makefile;
use Panotools::Script;
my $path_output;
my $path_mk;
my $help = 0;
# TODO run panomatic too
# TODO run vig_optimize conditionally
# TODO vig_optimize celeste_standalone cpclean options
GetOptions ('o|output=s' => \$path_output,
'm|makefile=s' => \$path_mk,
'h|help' => \$help);
pod2usage (-verbose => 2) if $help;
pod2usage (2) unless (defined $path_output and scalar @ARGV);
my $path_prefix = $path_output;
$path_prefix =~ s/\.[[:alnum:]]+$//;
my $path_input = shift @ARGV;
my $mk = new Panotools::Makefile;
$mk->Comment ('Command-line tools');
$mk->Variable ('PTOSPLIT', 'ptosplit');
$mk->Variable ('PTOMERGE', 'ptomerge');
$mk->Variable ('PTOVARIABLE', 'ptovariable');
$mk->Variable ('CPFIND', 'cpfind');
$mk->Variable ('CPCLEAN', 'cpclean');
$mk->Variable ('ALIGNIMAGESTACK', 'align_image_stack');
$mk->Variable ('AUTOOPTIMISER', 'autooptimiser');
$mk->Variable ('VIGOPTIMIZE', 'vig_optimize');
$mk->Variable ('RM', '-', 'rm');
$mk->Comment ('Input project file and prefix for output');
$mk->Variable ('PROJECT_FILE', $path_input);
$mk->Variable ('PREFIX', $path_prefix);
$mk->Variable ('PTO_OUT', $path_output);
my $rule = $mk->Rule ('all');
$rule->Prerequisites ('$(PTO_OUT)');
$mk->Comment ("Files we don't need afterwards");
my $var_tempfiles = $mk->Variable ('TEMP_FILES');
$rule = $mk->Rule ('clean');
$rule->Command ('$(RM_SHELL)', '$(TEMP_FILES_SHELL)');
my $pto = new Panotools::Script;
$pto->Read ($path_input);
my $pix_max = $pto->Option->{cpgenSize} || 1600;
my $points = $pto->Option->{cpgenNumber} || 25;
my $ransac = 1;
$ransac = 0 if (defined $pto->Option->{cpgenRansac} and $pto->Option->{cpgenRansac} eq 'false');
my @refine = ();
@refine = ('--refine', '--keep-unrefinable', 0)
if (defined $pto->Option->{cpgenRefine} and $pto->Option->{cpgenRefine} eq 'true');
my $tmp;
$tmp->{$_} = undef for (map {$_->{j} if defined $_->{j}} @{$pto->Image});
delete $tmp->{''};
if (scalar keys %{$tmp})
{
my $layers;
my $stacks;
for my $index_photo (0 .. scalar @{$pto->Image} -1)
{
next if $pto->Image->[$index_photo]->{r} =~ /^=/;
push @{$stacks->[$pto->Image->[$index_photo]->{j}]}, $index_photo;
}
$mk->Comment ('A bracketed project can be composed of stacks and exposure layers');
my $var_layers = $mk->Variable ('LAYERS');
my $var_stacks = $mk->Variable ('STACKS');
for my $id_el (0 .. scalar @{$pto->ExposureLayers} -1)
{
my @id_images = @{$pto->ExposureLayers->[$id_el]};
next unless scalar @id_images > 1;
$ransac = 0 if $pto->Image->[$id_images[0]]->v ($pto) > 60;
$mk->Comment ('Some intermediate .pto project files for this layer');
my $var_layer_a = $mk->Variable ('LAYER_'. $id_el .'_A');
$var_layer_a->Values ('$(PREFIX)_layer_'. $id_el .'.a.pto');
my $var_layer_b = $mk->Variable ('LAYER_'. $id_el .'_B');
$var_layer_b->Values ('$(PREFIX)_layer_'. $id_el .'.b.pto');
my $var_layer_d = $mk->Variable ('LAYER_'. $id_el .'_D');
$var_layer_d->Values ('$(PREFIX)_layer_'. $id_el .'.d.pto');
$var_tempfiles->Values ($var_layer_a->NameRef, $var_layer_b->NameRef, $var_layer_d->NameRef);
$mk->Comment ('Input photos for this layer');
my $var_layer_input = $mk->Variable ('LAYER_'. $id_el .'_INPUT');
$var_layer_input->Values (map {$pto->Image->[$_]->Path} @id_images);
$mk->Comment ('Rules to generate control points for this layer');
$rule = $mk->Rule ($var_layer_a->NameRef);
$rule->Prerequisites ('$(PROJECT_FILE)');
$rule->Command ('$(PTOSPLIT_SHELL)', @id_images,
'$(PROJECT_FILE_SHELL)', $var_layer_a->NameRefShell);
$rule = $mk->Rule ($var_layer_b->NameRef);
$rule->Prerequisites ($var_layer_a->NameRef, $var_layer_input->NameRef);
$rule->Command ('$(CPFIND_SHELL)', '--celeste', '-o', $var_layer_b->NameRefShell, $var_layer_a->NameRefShell);
$rule = $mk->Rule ($var_layer_d->NameRef);
$rule->Prerequisites ($var_layer_b->NameRef);
$rule->Command ('$(CPCLEAN_SHELL)', '-n', 1.5,
'-o', $var_layer_d->NameRefShell, $var_layer_b->NameRefShell);
$var_layers->Values ($var_layer_d->NameRef);
}
my $stack_id = 0;
for my $stack (@{$stacks})
{
next unless scalar @{$stack} > 1;
my @projection = ();
@projection = ('-e') if ($pto->Image->[$stack->[0]]->f == 2 or $pto->Image->[$stack->[0]]->f == 3);
my $deg_fov = $pto->Image->[$stack->[0]]->v ($pto);
$mk->Comment ('An intermediate .pto project for this stack');
my $var_stack = $mk->Variable ('STACK_'. $stack_id);
$var_stack->Values ('$(PREFIX)_stack_'. $stack_id .'.pto');
$var_tempfiles->Values ($var_stack->NameRef);
$mk->Comment ('Input photos for this stack');
my $var_stack_input = $mk->Variable ('STACK_'. $stack_id .'_INPUT');
$var_stack_input->Values (map {$pto->Image->[$_]->Path} @{$stack});
$mk->Comment ('Rule to generate control points for this stack');
$rule = $mk->Rule ($var_stack->NameRef);
$rule->Prerequisites ($var_stack_input->NameRef);
$rule->Command ('$(ALIGNIMAGESTACK_SHELL)', @projection, '-f', $deg_fov,
'-p', $var_stack->NameRefShell, $var_stack_input->NameRefShell);
$var_stacks->Values ($var_stack->NameRef);
$stack_id++;
}
$mk->Comment ('Some intermediate .pto project files for merging stacks and layers');
$mk->Variable ('MERGED_A', '$(PREFIX)_merged.a.pto');
$mk->Variable ('MERGED_B', '$(PREFIX)_merged.b.pto');
$var_tempfiles->Values ('$(MERGED_A)', '$(MERGED_B)');
$mk->Comment ('Rules to merge stacks and bracketed layers');
$rule = $mk->Rule ('$(MERGED_A)');
$rule->Prerequisites ('$(PROJECT_FILE)', '$(LAYERS)', '$(STACKS)');
$rule->Command ('$(PTOMERGE_SHELL)', '$(PROJECT_FILE_SHELL)',
'$(LAYERS_SHELL)', '$(STACKS_SHELL)', '$(MERGED_A_SHELL)');
# FIXME -n and just positions should be optimised for single stacks
my @autooptimiser_args;
@autooptimiser_args = ('-l') if (scalar @{$stacks} > 1);
# FIXME -a optimises b, d and e for single stacks, bad
$rule = $mk->Rule ('$(MERGED_B)');
$rule->Prerequisites ('$(MERGED_A)');
$rule->Command ('$(AUTOOPTIMISER_SHELL)', @autooptimiser_args, '-a', '-s',
'-o', '$(MERGED_B_SHELL)', '$(MERGED_A_SHELL)');
$rule = $mk->Rule ('$(PTO_OUT)');
$rule->Prerequisites ('$(MERGED_B)');
$rule->Command ('$(PTOVARIABLE_SHELL)', '--positions', '--view', '--barrel',
'--output', '$(PTO_OUT_SHELL)', '$(MERGED_B_SHELL)');
}
else
{
$ransac = 0 if $pto->Image->[0]->v ($pto) > 60;
$mk->Comment ('Some intermediate .pto project files');
$mk->Variable ('SIMPLE_A', '$(PREFIX)_simple.a.pto');
$mk->Variable ('SIMPLE_C', '$(PREFIX)_simple.c.pto');
$mk->Variable ('SIMPLE_D', '$(PREFIX)_simple.d.pto');
$mk->Variable ('SIMPLE_E', '$(PREFIX)_simple.e.pto');
$mk->Variable ('SIMPLE_F', '$(PREFIX)_simple.f.pto');
$var_tempfiles->Values ('$(SIMPLE_A)', '$(SIMPLE_C)', '$(SIMPLE_D)', '$(SIMPLE_E)', '$(SIMPLE_F)');
$mk->Comment ('All input photos');
$mk->Variable ('INPUT_IMAGES', map {$_->Path} @{$pto->Image});
$mk->Comment ('Rules to generate control points for simple projects with no bracketed stacks');
$rule = $mk->Rule ('$(SIMPLE_A)');
$rule->Prerequisites ('$(PROJECT_FILE)', '$(INPUT_IMAGES)');
$rule->Command ('$(CPFIND_SHELL)', '--celeste', '-o', '$(SIMPLE_A_SHELL)', '$(PROJECT_FILE_SHELL)');
$rule = $mk->Rule ('$(SIMPLE_C)');
$rule->Prerequisites ('$(SIMPLE_A)');
$rule->Command ('$(CPCLEAN_SHELL)', '-o', '$(SIMPLE_C_SHELL)', '$(SIMPLE_A_SHELL)');
$rule = $mk->Rule ('$(SIMPLE_D)');
$rule->Prerequisites ('$(SIMPLE_C)', '$(PROJECT_FILE)');
$rule->Command ('$(PTOMERGE_SHELL)', '$(PROJECT_FILE_SHELL)', '$(SIMPLE_C_SHELL)', '$(SIMPLE_D_SHELL)');
$rule = $mk->Rule ('$(SIMPLE_E)');
$rule->Prerequisites ('$(SIMPLE_D)');
$rule->Command ('$(AUTOOPTIMISER_SHELL)', '-a', '-l', '-s', '-o', '$(SIMPLE_E_SHELL)',
'$(SIMPLE_D_SHELL)');
$rule = $mk->Rule ('$(SIMPLE_F)');
$rule->Prerequisites ('$(SIMPLE_E)');
$rule->Command ('$(PTOVARIABLE_SHELL)', '--positions', '--vignetting', '--response', '--exposure', '--view',
'--barrel', '--output', '$(SIMPLE_F_SHELL)', '$(SIMPLE_E_SHELL)');
$rule = $mk->Rule ('$(PTO_OUT)');
$rule->Prerequisites ('$(SIMPLE_F)');
$rule->Command ('$(VIGOPTIMIZE_SHELL)', '-o', '$(PTO_OUT_SHELL)', '$(SIMPLE_F_SHELL)');
}
my $rule_secondary = $mk->Rule ('.SECONDARY');
$rule_secondary->Prerequisites ('$(TEMP_FILES)');
$mk->Write ($path_mk) if defined $path_mk;
$mk->DoIt ('--always-make', 'all', 'clean') unless defined $path_mk;
exit 0;
__END__
=head1 NAME
ptoanchor - add control points to a Hugin project
=head1 SYNOPSIS
ptoanchor [options] --output output.pto input.pto
Options:
-m | --makefile file Output Makefile
-o | --output file Output project
-h | --help Outputs help documentation.
=head1 DESCRIPTION
B<ptoanchor> is a wrapper various tools that generates control points and
optimises a Hugin project using a .pto project as input. Output is in the form
of a .pto project.
If the --makefile option is given, rules for generating the project are written
to a Makefile, if --makefile isn't set then these rules will be executed
immediately.
Stacks are passed to align_image_stack for processing, but need to be indicated
with 'i-line' 'j' parameters in the input project file.
=head1 LICENSE
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
=head1 SEE ALSO
L<http://hugin.sourceforge.net/>
=head1 AUTHOR
Bruno Postle - November 2009.
=cut
=begin perl