#!/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