#!/usr/bin/perl # Filename: burstAnim # Author: David Ljung Madison # See License: http://MarginalHacks.com/License/ # Description: Take a multi-burst image from a digital camera # (which is a grid of photos) and convert to an animated gif. use strict; ################################################## # Setup the variables ################################################## my $PROGNAME = $0; $PROGNAME =~ s|.*/||; my ($BASENAME,$PROGNAME) = ($0 =~ m|(.*)/(.+)|) ? ($1?$1:'/',$2) : ('.',$0); my $DEFAULTS = { rows => 4, columns => 4, delay => 2, # The default on my camera is 1/30th of a second skip => [], # Executables/paths convert => 'convert', jhead => 'jhead', dev_null => '/dev/null', # Tmp image tmp => "/tmp/burst.$$.%d.jpg", }; ################################################## # Interrupts ################################################## my @TMPFILES; sub cleanup() { unlink(@TMPFILES); exit; } $SIG{'INT'}=\&cleanup; $SIG{'TERM'}=\&cleanup; $SIG{'HUP'}=\&cleanup; $SIG{'SUSP'}=\&cleanup; $SIG{'QUIT'}=\&cleanup; $SIG{'DONE'}=\&cleanup; ################################################## # Usage ################################################## sub fatal { foreach my $msg (@_) { print STDERR "\n[$PROGNAME] ERROR: $msg\n"; } exit(-1); } sub usage { foreach my $msg (@_) { print STDERR "\nERROR: $msg\n"; } print STDERR < Convert a multi-burst image from a digital camera to an animated gif -d Set debug mode -rows Set number of rows [$DEFAULTS->{rows}] -columns Set number of columns [$DEFAULTS->{columns}] -delay Set delay between frames (1/100ths of sec) [$DEFAULTS->{delay}] --skip List of frames to skip. Either '-skip 1-3' or '--skip 1 5-9 --' -convert Path to 'convert' executable [$DEFAULTS->{convert}] -dev_null Path for null output [$DEFAULTS->{dev_null}] -tmp Temp image name (must include '%d') [$DEFAULTS->{tmp}] USAGE exit -1; } sub expand { my ($str) = @_; return $str unless $str =~ /^(\d+)-(\d+)$/; return ($1..$2); } sub parse_args { my $opt = $DEFAULTS; my @files; while (my $arg=shift(@ARGV)) { if ($arg =~ /^-h$/) { usage(); } if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; } if ($arg =~ /^--(.*)$/ && $DEFAULTS->{$1}) { $arg = $1; my $val; push(@{$opt->{$1}}, expand($val)) while (($val=shift(@ARGV)) && $val ne "--"); next; } if ($arg =~ /^-(.*)$/ && $DEFAULTS->{$1}) { $arg = $1; if (ref($DEFAULTS->{$arg}) eq "ARRAY") { push(@{$opt->{$arg}}, expand(shift @ARGV)); } else { $opt->{$arg} = shift @ARGV; } next; } if ($arg =~ /^-/ && !-f $arg) { usage("Unknown option: $arg"); } push(@files,$arg); } usage("No images defined") unless @files; ($opt,@files); } sub debug { return unless $MAIN::DEBUG; foreach my $msg (@_) { print STDERR "[$PROGNAME] $msg\n"; } } sub mySystem { my ($opt, @cmd) = @_; debug("Run: @cmd\n"); system(@cmd); fatal("Error running: [@cmd]:\n $?: $!") if $?; } ################################################## # Image processing ################################################## sub file_quote { my ($opt, $file) = @_; "\Q$file\E"; } sub get_size { my ($opt,$img) = @_; return (0,0) unless (-f $img); my $qimg = file_quote($opt,$img); # Try jhead if we have it. if ($opt->{jhead} && $img=~/.jpe?g$/) { return ($1,$2) if (qx/$opt->{jhead} -c $qimg 2>$opt->{dev_null}/=~/\s(\d+)x(\d+)(\s)/); undef $opt->{jhead}; # jhead didn't work, don't keep trying... } open(SIZE,"$opt->{convert} -verbose $qimg $opt->{dev_null} 2>&1 |") || fatal("Couldn't run convert! [$opt->{convert}]\n"); while() { print STDERR "get_xy(): $_" if $opt->{d}; if(/\s(\d+)x(\d+)(\s|\+|\-)/) { close SIZE; return ($1,$2); } } close SIZE; fatal("Can't get [$img] size from 'convert -verbose' output") } ################################################## # Image processing ################################################## sub splitImage { my ($opt, $img) = @_; my @ret; my ($X,$Y) = get_size($opt,$img); my ($x,$y) = ($X/$opt->{columns},$Y/$opt->{rows}); my $num = $opt->{columns}*$opt->{rows}; print STDERR "Splitting [$img] into $num frames: /$num"; my $back = length($num)+4; my $cnt = 0; for (my $sy=0; $sy<$Y; $sy+=$y) { for (my $sx=0; $sx<$X; $sx+=$x) { $cnt++; next if (grep($_ eq $cnt, @{$opt->{skip}})); my $tmp = $opt->{tmp}; fatal("Need '%d' somewhere in -tmp setting") unless $tmp =~ s/%d/$cnt/g; push(@TMPFILES,$tmp); push(@ret,$tmp); print STDERR ""x$back; printf STDERR "%3d/$num", $cnt; #print "convert -crop ${x}x${y}+$sx+$sy $img $tmp\n"; mySystem($opt,"convert","-crop","${x}x${y}+$sx+$sy",$img,$tmp); } } print STDERR "\n"; usage("No frames left after skipping [@{$opt->{skip}}]") unless @ret; @ret; } sub animate { my ($opt, $file, @split) = @_; my $anim = $file; $anim =~ s/(\.[^\.]*)?$/.anim.gif/; print STDERR "Animating.."; mySystem($opt,"convert","-delay",$opt->{delay},@split,$anim); print "ed: $anim\n"; $anim; } ################################################## # Main code ################################################## sub main { my ($opt,@files) = parse_args(); foreach my $file ( @files ) { my @split = splitImage($opt, $file); my $animate = animate($opt, $file, @split); } cleanup; } main(); #; vim: ts=4