The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/perl

use strict;
use warnings;
use Getopt::Long;
use Pod::Usage;
use Panotools::Script;
use Panotools::Photos;

my $path_oto;
my $pix_max = 1600;
my $points = 25;
my $noransac = 0;
my $refine = 0;
my $dostacks = 0;
my $linkstacks = 0;
my $projection = 0;
my $deg_fov;
my $crop_s = undef;
my $align = 0;
my $clean = 0;
my $help = 0;

GetOptions ('o|output=s' => \$path_oto,
            's|size=i' => \$pix_max,
            'p|points=i' => \$points,
            'n|noransac' => \$noransac,
            'r|refine' => \$refine,
            'b|stacks' => \$dostacks,
            'l|linkstacks' => \$linkstacks,
            'f|projection=i' => \$projection,
            'v|fov=s' => \$deg_fov,
            'k|selection=s' => \$crop_s,
            'a|align' => \$align,
            'c|clean' => \$clean,
            'h|help' => \$help);

# code assumes images are sorted
@ARGV = sort @ARGV;

pod2usage (-verbose => 2) if $help;
pod2usage (2) unless (scalar @ARGV > 1);

my $photos_all = new Panotools::Photos (@ARGV);

$deg_fov = 50 unless defined $deg_fov;
# decimal separator workaround
$deg_fov =~ s/,.*//;

my $oto = new Panotools::Script;

my @AverageRGB = $photos_all->AverageRGB;

my $sum_Eev;
for my $id (0 .. scalar @ARGV -1)
{
    my $image = new Panotools::Script::Line::Image;
    my $photo = $photos_all->[$id];
    %{$image} = (
        n => '"'. $photo->{path} .'"',
        w => $photo->{exif}->{ImageWidth},
        h => $photo->{exif}->{ImageHeight},
        r => $photos_all->Rotation ($id), p => 0.0, y => 0.0,
        a => 0.0, b => 0.0, c => 0.0, d => 0.0, e => 0.0,
        Ra => 0, Rb => 0, Rc => 0, Rd => 0, Re => 0,
        Va => 1, Vb => 0, Vc => 0, Vd => 0, Vx => 0, Vy => 0,
        f => $projection,
        Eev => $photos_all->Eev ($id),
        v => ($photos_all->FOV ($id) || $deg_fov)
    );
    $image->{n} = '"'. $image->Path .'"';
    $image->{S} = $crop_s if defined $crop_s;
    $sum_Eev += $photos_all->Eev ($id);
    $image->{Er} = $photo->{exif}->{RedBalance} / $AverageRGB[0] if $photo->{exif}->{RedBalance};
    $image->{Eb} = $photo->{exif}->{BlueBalance} / $AverageRGB[2] if $photo->{exif}->{BlueBalance};

    if ($photos_all->Bracketed)
    {
        $image->{j} = int ($id / scalar @{$photos_all->Speeds});
    }
    push @{$oto->Image}, $image;

    my $imagemetadata = new Panotools::Script::Line::ImageMetadata;
    $imagemetadata->{cropFactor} = $photo->{exif}->{ScaleFactor35efl} || 1;
    $imagemetadata->{autoCenterCrop} = 0 if defined $crop_s;
    push @{$oto->ImageMetadata}, $imagemetadata;

    unless ($id == 0)
    {
        $oto->Variable->{$id}->{r} = 1;
        $oto->Variable->{$id}->{p} = 1;
        $oto->Variable->{$id}->{y} = 1;
    }
}

#TODO calculate fisheye field of view from EXIF FoV

$oto->Panorama->{E} = sprintf ('%.3f', $sum_Eev / scalar @{$photos_all});

$oto->Panorama->{w} = $oto->OptimalWidth (0.7);
$oto->Panorama->{h} = $oto->Panorama->{w} / 2;

$oto->UnifyLenses;

$oto->LinkStacks if $linkstacks;

$oto->Option->{cpgenSize} = $pix_max;
$oto->Option->{cpgenNumber} = $points;
$oto->Option->{cpgenRansac} = 'true';
$oto->Option->{cpgenRansac} = 'false' if $noransac;
$oto->Option->{cpgenRefine} = 'false';
$oto->Option->{cpgenRefine} = 'true' if $refine;

if ($photos_all->Bracketed)
{
    $oto->Option->{outputLDRBlended} = 'false';
    $oto->Option->{outputLDRExposureBlended} = 'true';
    $oto->Option->{outputLDRExposureLayersFused} = 'false';
}
elsif ($photos_all->Layered)
{
    $oto->Option->{outputLDRBlended} = 'false';
    $oto->Option->{outputLDRExposureBlended} = 'false';
    $oto->Option->{outputLDRExposureLayersFused} = 'true';
}
else
{
    $oto->Option->{outputLDRBlended} = 'true';
    $oto->Option->{outputLDRExposureBlended} = 'false';
    $oto->Option->{outputLDRExposureLayersFused} = 'false';
}

$path_oto = $photos_all->Stub .'.pto' unless defined $path_oto;
if ($align)
{
    my $path_nopoints = $path_oto;
    $path_nopoints =~ s/\.[[:alnum:]]+$//;
    $path_nopoints .= '.pointless.pto';
    $oto->Write ($path_nopoints);
    system ('ptoanchor', '--output', $path_oto, $path_nopoints);
}
else
{
    $oto->Write ($path_oto);
}

exit 0;

__END__

=head1 NAME

match-n-shift - generate a Hugin .pto project from a list of photos

=head1 SYNOPSIS

match-n-shift [options] --output project.pto image1 image2 [...]

 Options:
  -o | --output name    Filename of created panorama project
  -s | --size number    Downsize images until width and height is
                          smaller than number, default 1600
  -p | --points number  Number of generated control points between,
                          each pair, default: 25
  -n | --noransac       No ransac detection, useful for fisheye images
  -r | --refine         Refine the found control points using the
                          original images, delete unrefinable.
  -f | --projection     Panotools style input projection number. Use
                          0 for rectilinear (default), 2 for circular fisheye and
                          3 for full-frame fisheye images.  Note, this has to be
                          specified for fisheye lenses as this can't be determined
                          from EXIF metadata
  -v | --fov            Horizontal field of view in degrees, this value is
                          only used if FoV can't be determined from EXIF
                          metadata, defaults to 50
  -k | --selection      Crop selection boundary, eg -459,2459,-57,2861
  -l | --linkstacks     Hard link positions of stacked photos.
  -a | --align          Generate control points (default no).
  -h | --help           Outputs help documentation.

=head1 DESCRIPTION

B<match-n-shift> takes a list of image files and creates a hugin compatible
project file optionally containing control points linking the images together.

=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 - February 2008.

=cut

=begin perl