#!/usr/local/bin/perl

# @(#)$Id: timelapse,v 1.8 1999/02/09 05:31:33 twitham Exp twitham $

# Copyright (C) 1998 - 1999 Tim Witham <twitham@quiknet.com>

# (see the files README and COPYING for more details)

# timelapse: grab and archive non-dark video frames that have changed

$darkness = 0.0864;		# toss images with a sum < this (blue=.0863)
$difference = 0.008;		# toss images with a difference < this
$width = 40;			# width and height for comparison
$height = 30;			# 40x30 = 1/16 of 640x480
$colors = 256;			# use this number of gray levels
$timestamp = "TW %Y/%m/%d %H:%M"; # strftime to put on the image
$comment = "Copyright %Y Timothy D. Witham"; # strftime to hide in the image
$font = '-misc-fixed-medium-r-normal--20-140-100-100-c-100-iso8859-1';
# $font = '-xxl-ledfixed-medium-r-*';
$grabone = '/usr/data/timelapse/etc/grabone'; # how to grab one frame
$DEBUG = 0;			# show frame grabber output and errors

use Image::Magick;
use POSIX qw(strftime);
use File::Copy qw(copy);
use File::Path qw(mkpath);

$| = 1;
$ENV{'PATH'} .= ':/usr/local/bin'; # in case launched from cron
$ENV{'DISPLAY'} = ':0.0' unless $ENV{'DISPLAY'};

($sec, $dir) = @ARGV;
$sec = 60 unless $sec;		# how many seconds between captures?
$dir = '/usr/data/timelapse' unless $dir;
chdir($dir) || die "$0: can't chdir $dir: $!\n";

$white = $width * $height * 255;
open(LOG, ">>grab.log") || die "$0: can't append grab.log: $!\n";
select LOG; $| = 1; select STDOUT;

$images = Image::Magick->new;

$prev = time - $sec;		# force first
while (1) {

    sleep($tmp) if ($tmp = $prev + $sec - time) > 0;
    @time = localtime($prev = time);
#     print "grabbing @ $prev\n";

    $out = `$grabone latest.jpg`;
    print $out if $DEBUG;
    if ($xit = $? >> 8) {
	warn "$0: $grabone returned $xit\n";
	$busy++;
	next;
    }

    copy('latest.jpg', 'last.jpg') # first time?
	|| warn "$0: can't copy latest.jpg last.jpg: $!\n"
	    unless -e 'last.jpg';

    undef @$images;
    $x = $images->Read('last.jpg', 'latest.jpg');
    warn "$x" if "$x";

    undef $smalls;
    $smalls = $images->Clone();	# process small copy of images
    $smalls->Scale(width=>$width, height=>$height);
    $smalls->Quantize(colorspace=>'gray', colors=>$colors);

    if (($sum = &graysum($smalls->[1]) / $white) < $darkness) {
	push(@dark, $sum);
	next;
    }
    if (($diff = &graydiff($smalls->[0], $smalls->[1]) / $white)
	< $difference) {
	push(@same, $diff);
	next;
    }
    if ($busy || @dark || @same) { # log min/mean/max of skipped frames
	my($sum, $diff, @tmp) = (0, 0);
	print LOG $busy if $busy;
	printf LOG "\t%d:%6.4f/%6.4f/%6.4f<%6.4f", &summary(@dark), $darkness
	    if @dark;
	printf LOG "\t%d@%6.4f/%6.4f/%6.4f<%6.4f", &summary(@same), $difference
	    if @same;
	print LOG "\n";
	$busy = 0;
	@dark = @same = ();
    }

    $filename = strftime("%Y/%m/%d/%H%M", @time);
    $filename .= sprintf('%02d', $time[0])
	if $sec < 60 || -e "$filename.jpg";
    printf LOG "%s\t%6.4f\t%6.4f\n", $filename, $sum, $diff;
    $filename .= '.jpg';
    ($path = $filename) =~ s!/[^/]+$!!;
    mkpath($path) || warn "$0: can't mkpath $path: $!\n" unless -d $path;
    copy('latest.jpg', 'last.jpg')
	|| warn "$0: can't copy latest.jpg last.jpg: $!\n";
    undef $images->[0];
    $x = $images->Comment(strftime($comment, @time));
    warn "$x" if "$x";
    $x = $images->Annotate(text=>strftime($timestamp, @time),
			   font=>$font,
			   box=>'black',
			   pen=>'green',
			   gravity=>NorthWest,
			   x=>430,
			   y=>440,
			   );
    warn "$x" if "$x";
    $x = $images->Write($filename); # write this image
    warn "$x" if "$x";
    unlink('final.jpg') if -l 'final.jpg';
    symlink($filename, 'final.jpg')
	|| warn "$0: can't symlink $filename final.jpg: $!\n";
    $images->Scale(width=>320, height=>240);
    $filename =~ s/\.jpg/.yuv/;
    $x = $images->Write($filename); # write this image
    warn "$x" if "$x";
}
close LOG;
exit 0;

sub graydiff {			# pixel difference in 2 gray images
    my($p1, $p2) = @_;
    my($tmp);

    $tmp = $p1->Clone();
    $tmp->Composite(image=>$p2, compose=>'Difference', geometry=>'+0+0');
    return &graysum($tmp);
}

sub graysum {			# sum of all pixels in 1 gray image
    my($image) = @_;
    my($width, $height) = $image->Get('width', 'height');
    my($x, $y, $sum);

    for ($y = 0; $y < $height; $y++) { # add up the pixel values
	for ($x = 0; $x < $width; $x++) {
	    $sum += $image->Get("pixel[$x,$y]'");
	}
    }
    return $sum;
}

sub summary {			# return count, min, mean, max
    my($num, $sum, @a) = (0, 0, @_);

    $num = @a;
    grep($sum += $_, @a = sort {$a <=> $b} @a);
    return $num, $a[$[], $sum / $num, $a[$#a];
}
