#!/usr/bin/perl -w

# Copyright 2003, 2004 David Hilvert <dhilvert@auricle.dyndns.org>,
#                                    <dhilvert@ugcs.caltech.edu>

#  This file is part of the Anti-Lamenessing Engine.
#
#  The Anti-Lamenessing Engine 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 3 of the License, or
#  (at your option) any later version.
#
#  The Anti-Lamenessing Engine is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with Anti-Lamenessing Engine; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

# This script attempts to calibrate a filter under ALE for characteristics of a
# specific device.  It uses the stdin IPC device to configure the filter.
#
# The initial filter tested is the box filter, and individual elements
# of the filter are increased or decreased to determine the effects of
# such changes.  When the change increases the match with the desired
# output, it is kept.  Otherwise, the change is backed out.
#
# The magnitude of change tested is determined by an increment variable,
# which starts at a user-defined value and is halved until it is smaller
# than 0.1 (an arbitrary value which could easily be changed).
#
# For every increment value, all elements are tested with positive and negative
# changes of this magnitude.  If an element fails to offer any error
# improvement on a given pass, it is skipped on all future passes for the same
# increment value.
#
# Passes at a given increment value continue until all elements are skipped, at
# which point the magnitude is halved.
#
# It may be a good idea to use this script with relatively small images, as
# large images may require a long time to process.

#
# Remove these two lines to run the script.
#
# Note: This script has been manually updated for ALE 0.7.0 from a non-generic
# working copy, and hence may contain serious bugs.  Bugs may be reported to
# ale@ventricle.dyndns.org.
#

print "Edit this script to set relevant parameters for calibration.\n";
exit 1;

#
# The diameter of the filter, in pixels.  Set nlheight to zero to disable 
# non-linear filtering.
#

$lheight = 3;
$lwidth = 3;

$nlheight = 0;
$nlwidth = 0;

#
# Filter rows and columns
#

$lrows = 3;
$lcols = 3;

$nlrows = 0;
$nlcols = 0;

#
# These are the input images used for calibration
#
# inputs: input images.
#
# tfile: transformation file indicating the proper transformations for the
# input images.
#

$inputs = "*.ppm";
$tfile = "b.t";

#
# Factor (>= 1) by which to scale each of the input images.
#

$scale = 5;

#
# Comparison images used for calibration.
# 
# Either:
#
# (a) set $comparison to the known scene data, or
#
# (b) set $comparison_inputs for calibration frames taken with the same imaging
# device (at a larger scale).  $comparison_tfile indicates the alignment of
# $comparison_inputs.
#

$comparison = "c-align.png";
# $comparison_inputs = "comparison/*.ppm";
# $comparison_tfile = "comparison/b.t";

#
# Random (but non-uniform) temporary files
#

$output1 = "/tmp/ale1-$$.png";
$temp_file = "/tmp/ale-$$-input";
$result_file = "/tmp/ale-$$-results";
$comparison = "/tmp/ale-$$-comparison.ppm" if defined $comparison_inputs;

#
# Initialize a box filter. 
#

@lfilter = (((1.0) x $lrows) x $lcols) x 3;
@nlfilter = (((1.0) x $nlrows) x $nlcols) x 3;

#
# Alternatively, something like this can be used:
#
# @lfilter = (
# 	1, 1, 1,
#	1, 1, 1,
# 	3, 4, 5.3,
# 	.
#	.
#	.
#)
#
# Or:
#
# $lfilter = "1 0.5 ... 3"
#
# @lfilter = split / /, $filter;
#

#
# Affine colorspace parameters
#

@affine_mul = (1, 1, 1);
@affine_add = (0, 0, 0);

#
# When modifying the filter, we start by modifying each element by $increment.
# We halve the amount until we reach a value smaller than $increment_lower.
# $a*_multiplier scales the increment for @affine_add and @affine_mul.
#

$increment = 0.8;
$increment_lower = 0.01;
$am_multiplier = 0.01;
$aa_multiplier = 0.01;

#
# This array indicates the last direction tried for each index.  Since we
# haven't tried anything yet, initialize this to +1 for all indices.
#
#    +1 ==> positive change
#    -1 ==> negative change
#

@llast_dir = (((1.0) x $lcols) x $lrows) x 3;
@nllast_dir = (((1.0) x $nlcols) x $nlrows) x 3;

#
# Program name to invoke ale
#

$invocation = "ale";

#
# Subroutine to obtain the match value associated with the current filter.
#

sub foo {
	`echo $lheight > $temp_file`;
	`echo $lwidth >> $temp_file`;
	`echo $lrows >> $temp_file`;
	`echo $lcols >> $temp_file`;
	foreach $elem (@lfilter) {
		`echo $elem >> $temp_file`;
	}

	if ($nlheight > 0) {
		`echo $nlheight >> $temp_file`;
		`echo $nlwidth >> $temp_file`;
		`echo $nlrows >> $temp_file`;
		`echo $nlcols >> $temp_file`;
		foreach $elem (@nlfilter) {
			`echo $elem >> $temp_file`;
		}
	}

	if ($nlheight > 0)  {
		`$invocation --dchain triangle:2 --no-inc --mc 1 --ips 4 --lpsf stdin --nlpsf stdin --projective --perturb-upper=0 --trans-load=$comparison_tfile $comparison_inputs $comparison < $temp_file 2> /dev/null` if ($init_comparison && $comparison_inputs);
		`$invocation --dchain triangle:2 --no-inc --mc 1 --scale=$scale --lpsf stdin --nlpsf stdin --psf-match @affine_mul @affine_add --projective --perturb-upper=0 --trans-load=$tfile $inputs $comparison $output1 < $temp_file 2> $result_file`;
	} else {
		`$invocation --dchain triangle:2 --no-inc --mc 1 --ips 4 --lpsf stdin --projective --perturb-upper=0 --trans-load=$comparison_tfile $comparison_inputs $comparison < $temp_file 2> /dev/null` if ($init_comparison && $comparison_inputs);
		`$invocation --dchain triangle:2 --no-inc --mc 1 --scale=$scale --lpsf stdin --psf-match @affine_mul @affine_add --projective --perturb-upper=0 --trans-load=$tfile $inputs $comparison $output1 < $temp_file 2> $result_file`;
	}

	`rm -f $output1`;
	`rm -f $temp_file`;

	$data = `cat $result_file`;

	`rm -f $result_file`;

	$data =~ /::\s*(\S+)/ or die "Couldn't get match value.\n";

	$data = $1;

	print "Measured Error: " . $data . "\n";

	$_ = $1;
}

$curval = foo();

while ($increment >= $increment_lower) {
	$changed = 0;

	for $i (0 .. $lrows - 1) {
	for $j (0 .. $lcols - 1) {
	for $k (0 .. 2) {
		$index = $k + 3 * ($j + $lcols * $i);

		if ($lskip[$index]) {
			print "Skipping linear element: " . $index . "\n";
			next;
		} else {
			print "Testing linear element: " . $index . "\n";
		}

		$dir_incr = $increment * $llast_dir[$index];

		$lfilter[$index] += $dir_incr;
		$newval = foo();
		if ($newval < $curval) {
			$changed = 1;
			$curval = $newval;
			if ($nlheight > 0) {
				print "Filter: " . "$lheight $lwidth $lrows $lcols @lfilter $nlheight $nlwidth $nlrows $nlcols @nlfilter\n";
			} else {
				print "Filter: " . "$lheight $lwidth $lrows $lcols @lfilter\n";
			}
		} else {
			$lfilter[$index] -= 2 * $dir_incr;
			$newval = foo();
			if ($newval < $curval) {
				$changed = 1;
				$curval = $newval;
				if ($nlheight > 0) {
					print "Filter: " . "$lheight $lwidth $lrows $lcols @lfilter $nlheight $nlwidth $nlrows $nlcols @nlfilter\n";
				} else {
					print "Filter: " . "$lheight $lwidth $lrows $lcols @lfilter\n";
				}
				$llast_dir[$index] *= -1;
			} else {
				$lfilter[$index] += $dir_incr;
				$lskip[$index] = 1;
			}
		}
	}}}

	if ($nlheight > 0) {
	for $i (0 .. $nlrows - 1) {
	for $j (0 .. $nlcols - 1) {
	for $k (0 .. 2) {
		$index = $k + 3 * ($j + $nlcols * $i);

		if ($nlskip[$index]) {
			print "Skipping non-linear element: " . $index . "\n";
			next;
		} else {
			print "Testing non-linear element: " . $index . "\n";
		}

		$dir_incr = $increment * $nllast_dir[$index];

		$nlfilter[$index] += $dir_incr;
		$newval = foo();
		if ($newval < $curval) {
			$changed = 1;
			$curval = $newval;
			print "Filter: " . "$lheight $lwidth $lrows $lcols @lfilter $nlheight $nlwidth $nlrows $nlcols @nlfilter\n";
		} else {
			$nlfilter[$index] -= 2 * $dir_incr;
			$newval = foo();
			if ($newval < $curval) {
				$changed = 1;
				$curval = $newval;
				print "Filter: " . "$lheight $lwidth $lrows $lcols @lfilter $nlheight $nlwidth $nlrows $nlcols @nlfilter\n";
				$nllast_dir[$index] *= -1;
			} else {
				$nlfilter[$index] += $dir_incr;
				$nlskip[$index] = 1;
			}
		}
	}}}}

	if ($changed == 0) {
		@lskip = ();
		@nlskip = ();
		$increment /= 2;
		print "Increment: $increment\n";
	}
}

