|
Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
#!/bin/bash
# This is a(n outdated) COPY of Fred Winehaus's "whiteboard" script.
# For the upstream version, see:
# http://www.fmwconcepts.com/imagemagick/whiteboard/index.php
#
# This copy is hosted at:
# http://angg.twu.net/bin/whiteboard.html
# http://angg.twu.net/bin/whiteboard
# (find-angg "bin/whiteboard")
#
# Some of my makefiles ("me" = Eduardo Ochs, a.k.a. Edrx) call this
# script. For example:
#
# http://angg.twu.net/2019.2-C2/Makefile.html
# (find-angg "2019.2-C2/Makefile")
# Developed by Fred Weinhaus 5/29/2009 .......... revised 5/7/2015
#
# ------------------------------------------------------------------------------
#
# Licensing:
#
# Copyright © Fred Weinhaus
#
# My scripts are available free of charge for non-commercial use, ONLY.
#
# For use of my scripts in commercial (for-profit) environments or
# non-free applications, please contact me (Fred Weinhaus) for
# licensing arrangements. My email address is fmw at alink dot net.
#
# If you: 1) redistribute, 2) incorporate any of these scripts into other
# free applications or 3) reprogram them in another scripting language,
# then you must contact me for permission, especially if the result might
# be used in a commercial or for-profit environment.
#
# My scripts are also subject, in a subordinate manner, to the ImageMagick
# license, which can be found at: http://www.imagemagick.org/script/license.php
#
# ------------------------------------------------------------------------------
#
####
#
# USAGE: whiteboard [-c coords] [-a aspect ] [-m magnify] [-d dimensions]
# [-e enhance ] [-f filtersize] [-o offset] [-t threshold] [-s sharpen]
# [-S saturation] [-w whitecolor] [-p percent] infile outfile
# USAGE: whiteboard [-help]
#
# OPTIONS:
#
# -c coords a list of the four x,y coordinates of the corner of
# the whiteboard in the picture ordered clockwise
# from the upper left corner. The default is the
# four corners of the input image.
# -a aspect width-to-height aspect ratio of actual whiteboard;
# typical aspect ratios are: 2 (2:1), 1.5 (3:2) and
# 1.33 (4:3); floats>0; The default is computed
# automatically
# -m magnify ouptut image magnification (or minification) factor
# applied to automatically computed dimensions; float>0;
# values>1 are magnify; values<1 are minify;
# default=1 (no change)
# -d dimensions desired dimension(s) of the output; choices are:
# WIDTH, xHEIGHT or WIDTHxHEIGHT; if either of the
# first two, then the other will be computed from the
# aspect ratio and magnify will be ignored; if the latter,
# then both aspect and magnify will be ignored; default is
# to ignore dimensions and use aspect and magnify
# -e enhance enhance image brightness before cleaning background;
# choices are: none, stretch, whitebalance or both;
# default=both
# -f filtersize size of processing filter to clean up background;
# integer>0; default=15
# -o offset offset of filter in percent to reduce noise;
# integer>=0; default=5
# -t threshold text smoothing threshold; 0<=threshold<=100;
# nominal value is about 50; default is no smoothing
# -s sharpamt sharpening amount in pixels; float>=0;
# nominal about 1; default=0
# -S saturation color saturation expressed as percent; integer>=0;
# default=200 (double saturation)
# -w whitecolor desired color for whiteboard background;
# default=white
# -p percent percent near white to use for white balancing;
# float>=0; default=0.01
#
###
#
# NAME: WHITEBOARD
#
# PURPOSE: To process a picture of a whiteboard to clean up the background
# and correct the perspective.
#
# DESCRIPTION: WHITEBOARD processses a picture of a whiteboard with writing
# on it to clean up the background and correct the perspective. The four
# corners of the actual interior of the whiteboard in the picture must be
# supplied in order to correct the perspective.
#
# OPTIONS:
#
# -c coords ... COORDS is a list of the four x,y coordinates of the corner of
# the whiteboard in the picture ordered clockwise startin with the upper left
# corner, e.g. "x1,y1 x2,y2 x3,y3 x4,y4". The default will be the four corners
# of the input image and thus will not trim any existing border or area outside
# the whiteboard, nor will it correct any perspective distortion.
#
# -a aspect ... ASPECT is the width-to-height aspect ratio of actual whiteboard.
# Typical values are: 2 (2:1), 1.5 (3:2) and 1.33 (4:3). Values are floats>0.
# The default is computed automatically.
#
# -m magnify ... MAGNIFY is the output image magnification (or minification) factor
# Values are floats>0. Values larger than 1 will magnify. Values less than 1 will
# minify. The default=1 and will produce an output whose height is the length of the
# left edge as defined by the supplied coordinates and whose width=height*aspect. A
# value of 2 will be twice that size and a value of 0.5 will be half that size. If
# no coordinates are supplied, then the width and height will be those of the
# input image multiplied by the magnify factor.
#
# -d dimensions ... DIMENSIONS are the desired dimension(s) of the output image.
# Choices are: WIDTH, xHEIGHT or WIDTHxHEIGHT; if either of the first two options
# are selected, then the other dimension will be computed from the aspect ratio
# and magnify will be ignored. If the latter option is selected, then both aspect
# and magnify will be ignored. If no coordinates are supplied, then the input image
# aspect ratio will be use. The default is to ignore dimensions and use the aspect
# and magnify.
#
# -e enhance ... Enhance image brightness before cleaning background. The choices
# are: none, stretch, white balance or both. The default=none.
#
# -f filtersize ... FILTERSIZE is the size of the filter used to clean up the
# background. Values are integers>0. The filtersize needs to be larger than
# the thickness of the writing, but the smaller the better beyond this. Making it
# larger will increase the processing time and may lose text. The default is 15.
#
# -o offset ... OFFSET is the offset threshold in percent used by the filter
# to eliminate noise. Values are integers>=0. Values too small will leave much
# noise and artifacts in the result. Values too large will remove too much
# text leaving gaps. The default is 5.
#
# -t threshold ... THRESHOLD is the text smoothing threshold. Values are integers
# between 0 and 100. Smaller values smooth/thicken the text more. Larger values
# thin, but can result in gaps in the text. Nominal value is in the middle at
# about 50. The default is to disable smoothing.
#
# -s sharpamt ... SHARPAMT is the amount of sharpening to be applied to the
# resulting image in pixels. Values are floats>=0. If used, it should be small
# (suggested about 1). The default=0 (no sharpening).
#
# -S saturation ... SATURATION is the desired color saturation of the text
# expressed as a percentage. Values are integers>=0. A value of 100 means
# no change. The default=200. Larger values will make the text colors more
# saturated.
#
# -w whitecolor ... WHITECOLOR is the desired background color of the whiteboard
# after it has been cleaned up. Any valid IM color may be use. The default is white.
#
# -p percent ... PERCENT near white to use for white balancing; float>=0;
# default=0.01
#
# NOTE: For coordinate selection, one can use the IM display function and
# on the Mac option-rightmousebutton hold and drag to display coordinates.
# I am not sure what the equivalent is on other systems. Possibly middle
# mouse button.
#
# NOTE: Requires IM 6.3.6-0 or higher only if control points are supplied or
# magnification is not equal to 1, due to the control point ordering for the
# perspective and also due to the use of -set option:distort:viewport.
#
# Thanks to Jens Mueller for suggesting this function and supplying references to examples.
#
# REFERENCES:
# http://www.sagenb.org/home/pub/704/
# http://research.microsoft.com/users/zhang/Papers/WhiteboardRectification.pdf
# http://research.microsoft.com/en-us/um/people/zhang/papers/tr03-39.pdf
#
# CAVEAT: No guarantee that this script will work on all platforms,
# nor that trapping of inconsistent parameters is complete and
# foolproof. Use At Your Own Risk.
#
######
#
# set default values
coords="" # 4 pairs of x,y coordinates
aspect="" # typical width-to-height ratios of 1, 2, 1.5 or 1.33
magnify=1 # magnification of image size from computed dimensions
dimensions="" # output image dimension(s)
enhance="none" # none, stretch, normalize
filtersize=15 # local area filter size
offset=5 # local area offset to remove "noise"; too small-get noise, too large-lose text
threshold="" # smoothing threshold
sharpamt=0 # sharpen sigma
saturation=200 # color saturation percent; 100 is no change
wcolor="white" # color for output whiteboard background
bluramt=0.2 # blur sigma for use with smoothing threshold
percent=0.01 # percent close to white for white balancing
# set directory for temporary files
dir="." # suggestions are dir="." or dir="/tmp"
# set up functions to report Usage and Usage with Description
PROGNAME=`type $0 | awk '{print $3}'` # search for executable on path
PROGDIR=`dirname $PROGNAME` # extract directory of program
PROGNAME=`basename $PROGNAME` # base name of program
usage1()
{
echo >&2 ""
echo >&2 "$PROGNAME:" "$@"
sed >&2 -e '1,/^####/d; /^###/g; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
}
usage2()
{
echo >&2 ""
echo >&2 "$PROGNAME:" "$@"
sed >&2 -e '1,/^####/d; /^######/g; /^#/!q; s/^#*//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
}
# function to report error messages
errMsg()
{
echo ""
echo $1
echo ""
usage1
exit 1
}
# function to test for minus at start of value of second part of option 1 or 2
checkMinus()
{
test=`echo "$1" | grep -c '^-.*$'` # returns 1 if match; 0 otherwise
[ $test -eq 1 ] && errMsg "$errorMsg"
}
# function to test if valid float point pair
testIntegerPair()
{
v1=`echo $1 | cut -d, -f1`
v2=`echo $1 | cut -d, -f2`
test1=`expr "$v1" : '^[0-9][0-9]*$'`
test2=`expr "$v2" : '^[0-9][0-9]*$'`
[ $test1 -eq 0 -o $test2 -eq 0 ] && errMsg "$1 IS NOT A VALID POINT PAIR"
}
# function to get channel mean
getChannelMean()
{
img="$1"
# get im version
if [ "$im_version" -ge "06030901" ]
then
mean=`convert $img -format "%[mean]" info:`
mean=`convert xc: -format "%[fx:100*$mean/quantumrange]" info:`
else
data=`convert $img -verbose info:`
mean=`echo "$data" | sed -n 's/^.*[Mm]ean:.*[(]\([0-9.]*\).*$/\1/p ' | head -1`
mean=`convert xc: -format "%[fx:100*$mean]" info:`
fi
}
# function to get average of near-white pixels
getAverage()
{
getChannelMean "$1"
# get ave in range 0-100
# note both mean and mask_mean are in range 0-100
# note average of just near_white values mean of masked image divided by
# the fraction of white pixels (from mask)
# which is the mean in range 0 to 1 divided by 100
ave=`convert xc: -format "%[fx:100*$mean/$maskmean]" info:`
[ "$ave" = "0" -o "$ave" = "0.0" ] && ave=100
ratio=`convert xc: -format "%[fx:100/$ave]" info:`
diff=`convert xc: -format "%[fx:(100-$ave)]" info:`
}
# function to perform whitebalance (taken from autowhite script)
whiteBalance()
{
img="$1"
# get image size
ww=`convert $img -format "%w" info:`
hh=`convert $img -format "%h" info:`
# separate channels
convert $tmpA1 $setcspace -channel R -separate $tmpR1
convert $tmpA1 $setcspace -channel G -separate $tmpG1
convert $tmpA1 $setcspace -channel B -separate $tmpB1
# get mask of top percent closest to white
# approximation using negated saturation and brightness channels multiplied
convert $tmpA1 $setcspace -colorspace HSB -channel G -negate -channel GB -separate \
-compose multiply -composite +channel \
$setcspace -contrast-stretch 0,${percent}% -fill black +opaque white \
$tmpM1
# get mean of mask
getChannelMean "$tmpM1"
maskmean=$mean
# use mask image to isolate user supplied percent of pixels closest to white
# then get ave graylevel for each channel of mask selected pixels
convert $tmpR1 $tmpM1 -compose multiply -composite $tmpT1
getAverage "$tmpT1"
redratio=$ratio
convert $tmpG1 $tmpM1 -compose multiply -composite $tmpT1
getAverage "$tmpT1"
greenratio=$ratio
convert $tmpB1 $tmpM1 -compose multiply -composite $tmpT1
getAverage "$tmpT1"
blueratio=$ratio
# process image
if [ "$im_version" -lt "06060100" ]; then
convert $tmpA1 -recolor "$redratio 0 0 0 $greenratio 0 0 0 $blueratio" $tmpA1
else
convert $tmpA1 -color-matrix "$redratio 0 0 0 $greenratio 0 0 0 $blueratio" $tmpA1
fi
}
# test for correct number of arguments and get values
if [ $# -eq 0 ]
then
# help information
echo ""
usage2
exit 0
elif [ $# -gt 26 ]
then
errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---"
else
while [ $# -gt 0 ]
do
# get parameter values
case "$1" in
-h|-help) # help information
echo ""
usage2
exit 0
;;
-c) # get coords
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID COORDS SPECIFICATION ---"
checkMinus "$1"
coords="$1"
;;
-w) # get wcolor
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID WHITE COLOR SPECIFICATION ---"
checkMinus "$1"
wcolor="$1"
;;
-a) # get aspect ratio
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID ASPECT SPECIFICATION ---"
checkMinus "$1"
aspect=`expr "$1" : '\([.0-9]*\)'`
[ "$aspect" = "" ] && errMsg "--- ASPECT=$aspect MUST BE A NON-NEGATIVE FLOAT ---"
aspecttest=`echo "$aspect <= 0" | bc`
[ $aspecttest -eq 1 ] && errMsg "--- ASPECT=$aspect MUST BE A FLOAT GREATER THAN 0 ---"
;;
-m) # get magnify
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID MAGNIFY SPECIFICATION ---"
checkMinus "$1"
magnify=`expr "$1" : '\([.0-9]*\)'`
[ "$magnify" = "" ] && errMsg "--- MAGNIFY=$magnify MUST BE A NON-NEGATIVE FLOAT ---"
magnifytest=`echo "$magnify <= 0" | bc`
[ $magnifytest -eq 1 ] && errMsg "--- MAGNIFY=$magnify MUST BE A FLOAT GREATER THAN 0 ---"
;;
-d) # get dimensions
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID DIMENSIONS SPECIFICATION ---"
checkMinus "$1"
dimensions="$1"
dimensions="${dimensions}x"
dimensions=`expr "$dimensions" : '\([x0-9]*\)'`
numdim=`echo "$dimensions" | tr "x" " " | wc -w`
[ "$dimensions" = "" ] && errMsg "--- ONE OR TWO DIMENSIONS MUST BE PROVIDED ---"
[ $numdim -ne 1 -a $numdim -ne 2 ] && errMsg "--- ONE OR TWO DIMENSIONS MUST BE PROVIDED ---"
ww=`echo "$dimensions" | cut -dx -f1`
hh=`echo "$dimensions" | cut -dx -f2`
;;
-e) # get enhance
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID ENHANCE SPECIFICATION ---"
checkMinus "$1"
enhance="$1"
case "$1" in
none) ;;
stretch) ;;
whitebalance) ;;
both) ;;
*) errMsg "--- ENHANCE=$enhance IS NOT A VALID CHOICE ---" ;;
esac
;;
-f) # get filtersize
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID FILTERSIZE SPECIFICATION ---"
checkMinus "$1"
filtersize=`expr "$1" : '\([0-9]*\)'`
[ "$filtersize" = "" ] && errMsg "--- FILTERSIZE=$filtersize MUST BE A NON-NEGATIVE INTEGER ---"
filtersizetest=`echo "$filtersize < 1" | bc`
[ $filtersizetest -eq 1 ] && errMsg "--- FILTERSIZE=$filtersize MUST BE AN INTEGER GREATER THAN 0 ---"
;;
-o) # get offset
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID OFFSET SPECIFICATION ---"
checkMinus "$1"
offset=`expr "$1" : '\([0-9]*\)'`
[ "$offset" = "" ] && errMsg "--- OFFSET=$offset MUST BE A NON-NEGATIVE INTEGER ---"
;;
-t) # get threshold
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID THRESHOLD SPECIFICATION ---"
checkMinus "$1"
threshold=`expr "$1" : '\([0-9]*\)'`
[ "$threshold" = "" ] && errMsg "--- THRESHOLD=$threshold MUST BE A NON-NEGATIVE INTEGER ---"
thresholdtestA=`echo "$threshold < 0" | bc`
thresholdtestB=`echo "$threshold > 100" | bc`
[ $thresholdtestA -eq 1 -o $thresholdtestB -eq 1 ] && errMsg "--- THRESHOLD=$threshold MUST BE AN INTEGER GREATER BETWEEN 0 AND 100 ---"
;;
-s) # get sharpamt
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID SHARPAMT SPECIFICATION ---"
checkMinus "$1"
sharpamt=`expr "$1" : '\([.0-9]*\)'`
[ "$sharpamt" = "" ] && errMsg "--- SHARPAMT=$sharpamt MUST BE A NON-NEGATIVE FLOAT ---"
;;
-S) # get saturation
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID SATURATION SPECIFICATION ---"
checkMinus "$1"
saturation=`expr "$1" : '\([0-9]*\)'`
[ "$saturation" = "" ] && errMsg "--- SATURATION=$saturation MUST BE A NON-NEGATIVE INTEGER ---"
;;
-p) # get percent
shift # to get the next parameter
# test if parameter starts with minus sign
errorMsg="--- INVALID PERCENT SPECIFICATION ---"
checkMinus "$1"
percent=`expr "$1" : '\([.0-9]*\)'`
[ "$percent" = "" ] && errMsg "--- PERCENT=$percent MUST BE A NON-NEGATIVE FLOAT ---"
;;
-) # STDIN and end of arguments
break
;;
-*) # any other - argument
errMsg "--- UNKNOWN OPTION ---"
;;
*) # end of arguments
break
;;
esac
shift # next option
done
#
# get infile and outfile
infile="$1"
outfile="$2"
fi
# test that infile provided
[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED"
# test that outfile provided
[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED"
tmpA1="$dir/autowhite_1_$$.mpc"
tmpA2="$dir/autowhite_1_$$.cache"
tmpM1="$dir/autowhite_M_$$.mpc"
tmpM2="$dir/autowhite_M_$$.cache"
tmpT1="$dir/autowhite_T_$$.mpc"
tmpT2="$dir/autowhite_T_$$.cache"
tmpR1="$dir/autowhite_R_$$.mpc"
tmpR2="$dir/autowhite_R_$$.cache"
tmpG1="$dir/autowhite_G_$$.mpc"
tmpG2="$dir/autowhite_G_$$.cache"
tmpB1="$dir/autowhite_B_$$.mpc"
tmpB2="$dir/autowhite_B_$$.cache"
trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpT1 $tmpT2 $tmpR1 $tmpR2 $tmpG1 $tmpG2 $tmpB1 $tmpB2;" 0
trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpT1 $tmpT2 $tmpR1 $tmpR2 $tmpG1 $tmpG2 $tmpB1 $tmpB2; exit 1" 1 2 3 15
# if use following it fails to produce the output????
#trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpT1 $tmpT2 $tmpR1 $tmpR2 $tmpG1 $tmpG2 $tmpB1 $tmpB2; exit 1" ERR
# get im_version
im_version=`convert -list configure | \
sed '/^LIB_VERSION_NUMBER /!d; s//,/; s/,/,0/g; s/,0*\([0-9][0-9]\)/\1/g' | head -n 1`
# colorspace RGB and sRGB swapped between 6.7.5.5 and 6.7.6.7
# though probably not resolved until the latter
# then -colorspace gray changed to linear between 6.7.6.7 and 6.7.8.2
# then -separate converted to linear gray channels between 6.7.6.7 and 6.7.8.2,
# though probably not resolved until the latter
# so -colorspace HSL/HSB -separate and -colorspace gray became linear
# but we need to use -set colorspace RGB before using them at appropriate times
# so that results stay as in original script
# The following was determined from various version tests using whiteboard
# with IM 6.7.4.10, 6.7.6.10, 6.7.9.1
if [ "$im_version" -lt "06070607" -o "$im_version" -gt "06070707" ]; then
setcspace="-set colorspace RGB"
else
setcspace=""
fi
# no need for setcspace for grayscale or channels after 6.8.5.4
if [ "$im_version" -gt "06080504" ]; then
setcspace=""
fi
# read the input image into the TMP cached image.
convert -quiet "$infile" +repage "$tmpA1" ||
errMsg "--- FILE $infile NOT READABLE OR HAS ZERO SIZE ---"
# get input image size and center
w=`convert -ping $tmpA1 -format "%w" info:`
h=`convert -ping $tmpA1 -format "%h" info:`
cx=`convert xc: -format "%[fx:$w/2]" info:`
cy=`convert xc: -format "%[fx:$h/2]" info:`
if [ "$coords" != "" ]; then
# separate input coords
# first pattern below replaces all occurrences of commas and spaces with a space => 1 2 3 4 5 6
# second pattern below replaces the first occurrence of a space with a comma => 1,2[ 3 4][ 5 6] - ignore [], they are for emphasis only
# third pattern below looks for all space number space number pairs and replaces them with a space followed by number1,number2 => 1,2 3,4 5,6
set - `echo "$coords" | sed 's/[, ][, ]*/ /g; s/ /,/; s/ \([^ ]*\) \([^ ]*\)/ \1,\2/g'`
# test for valid integers for x and y
index=0
plist=""
while [ $# -gt 0 ]
do
testIntegerPair $1
plist="$plist $1"
shift
index=`expr $index + 1`
done
#remove leading space from plist
plist=`echo "$plist" | sed -n 's/ [ ]*\(.*\)/\1/p'`
# test validity
[ "$plist" = "" ] && errMsg "--- NO POINTS WERE PROVIDED ---"
[ $index -ne 4 ] && errMsg "--- FOUR AND ONLY FOUR POINTS MUST BE PROVIDED ---"
# put list into an array
pArray=($plist)
# separate x,y coordinates
x1=`echo "${pArray[0]}," | cut -d, -f1`
y1=`echo "${pArray[0]}," | cut -d, -f2`
x2=`echo "${pArray[1]}," | cut -d, -f1`
y2=`echo "${pArray[1]}," | cut -d, -f2`
x3=`echo "${pArray[2]}," | cut -d, -f1`
y3=`echo "${pArray[2]}," | cut -d, -f2`
x4=`echo "${pArray[3]}," | cut -d, -f1`
y4=`echo "${pArray[3]}," | cut -d, -f2`
# compute the aspect ratio if not provided
if [ "$aspect" = "" ]; then
m1x=$x4
m1y=$y4
m2x=$x3
m2y=$y3
m3x=$x1
m3y=$y1
m4x=$x2
m4y=$y2
# get centroid of quadrilateral
ccx=`convert xc: -format "%[fx:($m1x+$m2x+$m3x+$m4x)/4]" info:`
ccy=`convert xc: -format "%[fx:($m1y+$m2y+$m3y+$m4y)/4]" info:`
# convert to proper x,y coordinates relative to center
m1x=`convert xc: -format "%[fx:$m1x-$ccx]" info:`
m1y=`convert xc: -format "%[fx:$ccy-$m1y]" info:`
m2x=`convert xc: -format "%[fx:$m2x-$ccx]" info:`
m2y=`convert xc: -format "%[fx:$ccy-$m2y]" info:`
m3x=`convert xc: -format "%[fx:$m3x-$ccx]" info:`
m3y=`convert xc: -format "%[fx:$ccy-$m3y]" info:`
m4x=`convert xc: -format "%[fx:$m4x-$ccx]" info:`
m4y=`convert xc: -format "%[fx:$ccy-$m4y]" info:`
#simplified equations, assuming u0=0, v0=0, s=1
k2=`echo "scale=5; (($m1y - $m4y)*$m3x - ($m1x - $m4x)*$m3y + $m1x*$m4y - $m1y*$m4x)/(($m2y- $m4y)*$m3x - ($m2x - $m4x)*$m3y + $m2x*$m4y - $m2y*$m4x)" | bc`
k3=`echo "scale=5; (($m1y - $m4y)*$m2x - ($m1x - $m4x)*$m2y + $m1x*$m4y - $m1y*$m4x)/(($m3y- $m4y)*$m2x - ($m3x - $m4x)*$m2y + $m3x*$m4y - $m3y*$m4x)" | bc`
ff=`echo "scale=5; (($k3*$m3y - $m1y)*($k2*$m2y - $m1y) + ($k3*$m3x - $m1x)*($k2*$m2x- $m1x))/(($k3 - 1)*($k2 - 1))" | bc`
if [ "$ff" = "" ]; then
errMsg "--- ASPECT RATIO CANNOT BE DETERMINED ---"
else
# sqrt( $ff*$ff) = abs($ff)
f=`echo "scale=5; sqrt( sqrt( $ff*$ff) )" | bc`
aspect=`echo "scale=5; sqrt((($k2 - 1)^2 + ($k2*$m2y - $m1y)^2/$f^2 + ($k2*$m2x - $m1x)^2/$f^2)/(($k3 - 1)^2 + ($k3*$m3y - $m1y)^2/$f^2 + ($k3*$m3x - $m1x)^2/$f^2))" | bc`
fi
fi
if [ "$dimensions" != "" -a "$ww" = "" -a "$hh" != "" ]; then
# compute output size from aspect ratio and hh
height=$hh
width=`convert xc: -format "%[fx:$height*$aspect]" info:`
elif [ "$dimensions" != "" -a "$ww" != "" -a "$hh" = "" ]; then
# compute output size from aspect ratio and ww
width=$ww
height=`convert xc: -format "%[fx:$width/$aspect]" info:`
elif [ "$dimensions" != "" -a "$ww" != "" -a "$hh" != "" ]; then
# output size is ww x hh
width=$ww
height=$hh
else
# compute output size from aspect ratio, magnify and left edge
height=`convert xc: -format "%[fx:$magnify*hypot(($x1-$x4),($y1-$y4))]" info:`
width=`convert xc: -format "%[fx:$aspect*$height]" info:`
fi
# compute corresponding corners of output image
xx1=0
yy1=0
xx2=$width
yy2=0
xx3=$width
yy3=$height
xx4=0
yy4=$height
# setup perspective conjugate control points src,dst pairs
coords="$x1,$y1 $xx1,$yy1 $x2,$y2 $xx2,$yy2 $x3,$y3 $xx3,$yy3 $x4,$y4 $xx4,$yy4"
#echo "coords=$coords"
else
if [ "$dimensions" != "" -a "$ww" = "" -a "$hh" != "" ]; then
# compute output size from aspect ratio and hh
aspect=`convert xc: -format "%[fx:$w/$h]" info:`
height=$hh
width=`convert xc: -format "%[fx:$height*$aspect]" info:`
magx=`convert xc: -format "%[fx:$width/$w]" info:`
magy=`convert xc: -format "%[fx:$height/$h]" info:`
elif [ "$dimensions" != "" -a "$ww" != "" -a "$hh" = "" ]; then
# compute output size from aspect ratio and ww
aspect=`convert xc: -format "%[fx:$w/$h]" info:`
width=$ww
height=`convert xc: -format "%[fx:$width/$aspect]" info:`
magx=`convert xc: -format "%[fx:$width/$w]" info:`
magy=`convert xc: -format "%[fx:$height/$h]" info:`
elif [ "$dimensions" != "" -a "$ww" != "" -a "$hh" != "" ]; then
# output size is ww x hh
width=$ww
height=$hh
magx=`convert xc: -format "%[fx:$width/$w]" info:`
magy=`convert xc: -format "%[fx:$height/$h]" info:`
else
# use image width and height multiplied by magnify
width=`convert xc: -format "%[fx:$magnify*$w]" info:`
height=`convert xc: -format "%[fx:$magnify*$h]" info:`
fi
# compute offsets for virtual canvas
delx=`convert xc: -format "%[fx:($w-$width)/2]" info:`
dely=`convert xc: -format "%[fx:($h-$height)/2]" info:`
signx=`convert xc: -format "%[fx:sign($delx)]" info:`
signy=`convert xc: -format "%[fx:sign($dely)]" info:`
[ "$signx" = "-1" ] && delx="$delx" || delx="+$delx"
[ "$signy" = "-1" ] && dely="$dely" || dely="+$dely"
fi
# setup sharpening
if [ "$sharpamt" = "0" -o "$sharpamt" = "0.0" ]; then
sharpening=""
else
sharpening="-sharpen 0x${sharpamt}"
fi
# setup blurring
if [ "$threshold" = "" ]; then
blurring=""
else
blurring="-blur 1x65535 -level ${threshold}x100%"
fi
# setup modulation
if [ $saturation -eq 100 ]; then
modulation=""
else
modulation="-modulate 100,$saturation"
fi
# do enhance
if [ "$enhance" = "stretch" ]; then
convert $tmpA1 $setcspace -contrast-stretch 0 $tmpA1
elif [ "$enhance" = "whitebalance" ]; then
whiteBalance "$tmpA1"
elif [ "$enhance" = "both" ]; then
convert $tmpA1 $setcspace -contrast-stretch 0 $tmpA1
whiteBalance "$tmpA1"
fi
# process image
if [ "$coords" = "" -a "$magnify" = "1" -a "$dimensions" = "" ]; then
convert \( $tmpA1 \) \
\( -clone 0 -colorspace gray -negate -lat ${filtersize}x${filtersize}+${offset}% $setcspace -contrast-stretch 0 $blurring \) \
-compose copy_opacity -composite -fill "$wcolor" -opaque none -alpha off \
$sharpening $modulation \
"$outfile"
elif [ "$coords" = "" -a "$magnify" != "1" ]; then
convert \( $tmpA1 -virtual-pixel white -set option:distort:viewport ${width}x${height}${delx}${dely} -distort SRT "$magnify 0" \) \
\( -clone 0 -colorspace gray -negate -lat ${filtersize}x${filtersize}+${offset}% $setcspace -contrast-stretch 0 $blurring \) \
-compose copy_opacity -composite -fill "$wcolor" -opaque none -alpha off \
$sharpening $modulation \
"$outfile"
elif [ "$coords" = "" -a "$dimensions" != "" ]; then
convert \( $tmpA1 -virtual-pixel white -set option:distort:viewport ${width}x${height}${delx}${dely} -distort SRT "$cx,$cy $magx,$magy 0" \) \
\( -clone 0 -colorspace gray -negate -lat ${filtersize}x${filtersize}+${offset}% $setcspace -contrast-stretch 0 $blurring \) \
-compose copy_opacity -composite -fill "$wcolor" -opaque none -alpha off \
$sharpening $modulation \
"$outfile"
elif [ "$coords" != "" ]; then
convert \( $tmpA1 -virtual-pixel white -set option:distort:viewport ${width}x${height}+0+0 -distort perspective "$coords" \) \
\( -clone 0 -colorspace gray -negate -lat ${filtersize}x${filtersize}+${offset}% $setcspace -contrast-stretch 0 $blurring \) \
-compose copy_opacity -composite -fill "$wcolor" -opaque none -alpha off \
$sharpening $modulation \
"$outfile"
else
errMsg = "--- INVALID SITUATION ---"
fi
exit 0