parent
353bb99097
commit
c5a999d36b
2 changed files with 1240 additions and 0 deletions
@ -0,0 +1,659 @@ |
||||
#!/bin/bash |
||||
# |
||||
# Developed by Fred Weinhaus 12/30/2015 .......... revised 1/4/2020 |
||||
# |
||||
# ------------------------------------------------------------------------------ |
||||
# |
||||
# 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: multicrop2 [-c coords] [-b bcolor] [-f fuzzval] [-d discard] |
||||
# [-u unrotate] [-i innertrim] [-e extend] [-m mask] [-t threshold] |
||||
# [-r resize] [-D density] [-S sortval] [-s showstats] [-v vc] infile outfile |
||||
# USAGE: multicrop [-h or -help] |
||||
# |
||||
# OPTIONS: |
||||
# |
||||
# -c coords pixel coordinate to extract background color; |
||||
# may be expressed as gravity value (NorthWest, etc) |
||||
# or as "x,y" value; default is NorthWest=(0,0) |
||||
# -b bcolor background color to use instead of option -c; |
||||
# any valid IM color; default is to use option -c |
||||
# -f fuzzval fuzz value for separating background color; expressed |
||||
# as (integer) percent 0 to 100; default=0 (uniform color) |
||||
# -d discard discard any region that has an area smaller than |
||||
# this size; integer>0; default is to keep all |
||||
# -u unrotate unrotate method; choices are 1 for -deskew, 2 for |
||||
# unrotate script and 3 for no unrotate; default=1 |
||||
# -i innertrim trims inside the cropped area to an orthogonal rectangle; |
||||
# yes or no; default=no |
||||
# -e extend extend crop on each side in pixels; integer; default=0 |
||||
# -m mask mask presentation method; choices are view, |
||||
# save (to file) or output mask only; default |
||||
# is none of the above, just output the images |
||||
# -t threshold threshold on number of objects; aborts if more than |
||||
# threshold number of objects are detected; integer>0; |
||||
# default is no abort and keep all objects. |
||||
# -r resize resize percent to scale the image down; float>0; |
||||
# default is no resizing. |
||||
# -D density density to use when reading a single page of a PDF; |
||||
# integer>0; default is no assigned density |
||||
# -S sortval sort regions by upper left bounding box x,y coordinates |
||||
# rounded to the specified positive integer increment; |
||||
# default is no sorting |
||||
# -s showstats show connected components stats; yes or no; default=no |
||||
# -v keep virtual canvas; default is not to keep virtual canvas; |
||||
# only valid for -u=3 (no unrotate) and for output format that |
||||
# supports virtual canvas such as PNG or TIFF. |
||||
# |
||||
### |
||||
# |
||||
# NAME: MULTICROP2 |
||||
# |
||||
# PURPOSE: To crop and unrotate multiple images from a scanned image. |
||||
# |
||||
# DESCRIPTION: MULTICROP2 crops and unrotates multiple images from a scanned image. |
||||
# The images must be well separated so that background color shows between them. |
||||
# The process uses a floodfill technique based upon a seed coordinate and a fuzz |
||||
# value to separate the individual images from the background of the scan. |
||||
# The correct choice of fuzz factor is very important. If too small, the images |
||||
# will not be separate. If too large, parts of the outer area of the image |
||||
# containing similar colors will be lost and the image may be separated into |
||||
# multiple parts. There are two unrotate methods. The first uses the IM deskew |
||||
# function, but is limited to 5 degrees of rotate or less. The second uses my |
||||
# unrotate script. It allows much larger rotations, but will be slower. If |
||||
# using the second method, my unrotate script must be downloaded and installed. |
||||
# |
||||
# IMPORTANT: The images in the scanned file must be well separated in x and y |
||||
# so that their bounding boxes do not overlap. This is especially important |
||||
# if the images have a significant rotation. |
||||
# |
||||
# The output images will be named from the specified outfile and -000, -001, |
||||
# -002 etc, will be appended before the .suffix. |
||||
# |
||||
# Arguments: |
||||
# |
||||
# -c coords ... COORDS is any location within the background (non-image) area |
||||
# for the algorithm to find the background color. It may be specified in terms |
||||
# of gravity parameters (NorthWest, North, NorthEast, East, SouthEast, South, |
||||
# SouthWest or West) or as a pixel coordinate "x,y". The default is the |
||||
# upper left corner = NorthWest = "0,0". |
||||
# |
||||
# -b bcolor ... BCOLOR is the background color to use for flood fill instead |
||||
# of extracting this color from the image. This is useful when an image has |
||||
# no borders so that the sub-images are hard against the edges. The bcolor |
||||
# will be used to put a one pixel border around the image and coords will be |
||||
# set to 0,0. Any valid IM color is allowed. The default is to use option -c. |
||||
# |
||||
# -f fuzzval ... FUZZVAL is the fuzz amount specified as an integer percent |
||||
# value between 0 to 100 (without the % sign). The correct choice of fuzz |
||||
# factor is very important. If too small, the images will not be separate. |
||||
# If too larger, parts of the outer area of the image containing similar |
||||
# colors will be lost and the image may be separated into multiple parts. |
||||
# Typical values are probably between 5 and 20 percent. The default=10 |
||||
# |
||||
# -d discard ... DISCARD any region that has an area smaller than the |
||||
# specified discard size. Values are integer>0. The default is to keep all |
||||
# regions. |
||||
# |
||||
# -u unrotate ... UNROTATE is the unrotation method. Choices are: 1, 2 or 3. |
||||
# The default is unrotate=1, which is fast and uses the IM -deskew function, |
||||
# but is limited to images that are rotated no more than 5 degrees in the scan |
||||
# and generally a light background color. Option unrotate=2 uses my unrotate |
||||
# script. It can handle larger rotations, but is slower. If using the latter |
||||
# method, my unrotate script must be downloaded and also installed so that it |
||||
# is available for this script to use. Option unrotate=3 makes no attempt to |
||||
# unrotate the images. |
||||
# |
||||
# -i innertrim ... INNERTRIM trims inside the cropped area to an orthogonal |
||||
# rectangle. Requires my script, autotrim. The choices are: yes or no. |
||||
# The default=no. |
||||
# |
||||
# -e extend ... EXTEND crop on each side for the output images in pixels. |
||||
# The extended region will come from the background of the image. Values are integers. |
||||
# Positive makes the results larger. Negative makes the results smaller. Positive |
||||
# values are only allowed for unrotate=3 (no unrotation) and innertrim=no. The default=0. |
||||
# |
||||
# -m mask ... MASK provides several options for reviewing the initial mask that |
||||
# is generated by the fuzz value. The choices are: view (display to X11 window), |
||||
# save (to disk) along with the images, or output (without processing the images). |
||||
# The default is to simply process the images without showing or saving the mask. |
||||
# If using the view mode, then processing will stop until the image is closed. |
||||
# But this allows you to then kill the script if the mask is not appropriate. |
||||
# A good approach is to use the output mode repeatedly with various fuzzvals |
||||
# until a reasonable mask is created. Note that the mask must separate the |
||||
# images, but the background can "eat" a little into the images so long as no |
||||
# full edge is lost or the images is split into multiple parts. |
||||
# |
||||
# -t threshold ... THRESHOLD on the number of objects. The script aborts, if |
||||
# more than the threshold number of objects are detected. Value must be |
||||
# integers greater than 0. The default is no abort and keep all objects. |
||||
# To avoid an abort, use the -d discard option. |
||||
# |
||||
# -r resize ... RESIZE amount in percent to scale the image down. Values are floats>0. |
||||
# The default is no resizing. This is useful to improve speed and floodfilling |
||||
# when you have a large image, especially if the background is grainy. Note: do not |
||||
# include the % symbol. |
||||
# |
||||
# -D density ... DENSITY to use when reading a single page of a PDF. Multipage pdf |
||||
# files are not permitted. Values are integers>0. The default is no assigned density. |
||||
# |
||||
# -S sortval ... SORT regions by their upper left bounding box x,y coordinates |
||||
# rounded to the specified positive integer increment. The default is |
||||
# no sorting. |
||||
# |
||||
# -s showstats ... SHOWSTATS shows the connected components statistics. |
||||
# Choices are: yes (y) or no (n). The default=no. |
||||
# |
||||
# -v ... keep VIRTUAL CANVAS. The default is not to keep virtual canvas. This option |
||||
# is only valid for -u=3 (no unrotate) and for output format that supports virtual |
||||
# canvas such as PNG or TIFF. |
||||
# |
||||
# REQUIREMENTS: IM 6.8.9.10 due to the use of -connected-components. |
||||
# If using unrotate method 2, then my script, unrotate, is required. |
||||
# If using innertrim, then my script, autotrim is required. |
||||
# |
||||
# 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="" # initial coord for finding background color |
||||
bcolor="" # initial background color |
||||
fuzzval=10 # fuzz amount in percent for making background transparent |
||||
discard="" # discard small regions |
||||
extend=0 # extend the output crop on all sides |
||||
mask="" # view, save, output |
||||
threshold="" # threshold on number of objects; if larger, then abort |
||||
unrotate=1 # 1=deskew 2=unrotate |
||||
innertrim="no" # trim inside cropped area to orthogonal rectangle |
||||
resize="" # resize percent |
||||
density="" # density to use for input PDF |
||||
sortval="" |
||||
showstats="no" # show CCL stats |
||||
vc="no" # keep virtual canvas |
||||
debug="false" |
||||
|
||||
# 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" |
||||
} |
||||
|
||||
# test for correct number of arguments and get values |
||||
if [ $# -eq 0 ] |
||||
then |
||||
# help information |
||||
echo "" |
||||
usage2 |
||||
exit 0 |
||||
elif [ $# -gt 29 ] |
||||
then |
||||
errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---" |
||||
else |
||||
while [ $# -gt 0 ] |
||||
do |
||||
# get parameters |
||||
case "$1" in |
||||
-h|-help) # help information |
||||
echo "" |
||||
usage2 |
||||
;; |
||||
-f) # fuzzval |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID FUZZVAL SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
fuzzval=`expr "$1" : '\([0-9]*\)'` |
||||
[ "$fuzzval" = "" ] && errMsg "--- FUZZVAL=$fuzzval MUST BE A NON-NEGATIVE INTEGER VALUE (with no sign) ---" |
||||
fuzzvaltestA=`echo "$fuzzval < 0" | bc` |
||||
fuzzvaltestB=`echo "$fuzzval > 100" | bc` |
||||
[ $fuzzvaltestA -eq 1 -a $fuzzvaltestB -eq 1 ] && errMsg "--- FUZZVAL=$fuzzval MUST BE A NON-NEGATIVE INTEGER VALUE BETWEEN 0 AND 100 ---" |
||||
;; |
||||
-c) # coords |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID COORDS SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
coords=$1 |
||||
# further testing done later |
||||
;; |
||||
-b) # bcolor |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID BCOLOR SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
bcolor=$1 |
||||
;; |
||||
-d) # discard |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID DISCARD SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
discard=`expr "$1" : '\([0-9]*\)'` |
||||
[ "$discard" = "" ] && errMsg "--- DISCARD=$discard MUST BE A NON-NEGATIVE INTEGER VALUE (with no sign) ---" |
||||
testA=`echo "$discard < 1" | bc` |
||||
[ $testA -eq 1 ] && errMsg "--- DISCARD=$discard MUST BE A NON-NEGATIVE INTEGER VALUE GREATER THAN 0 ---" |
||||
;; |
||||
-e) # extend |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID EXTEND SPECIFICATION ---" |
||||
#checkMinus "$1" |
||||
extend=`expr "$1" : '\([-0-9]*\)'` |
||||
[ "$extend" = "" ] && errMsg "--- EXTEND=$extend MUST BE AN INTEGER VALUE (with no sign) ---" |
||||
;; |
||||
-u) # unrotate |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID UNROTATE SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
unrotate=`expr "$1" : '\([0-9]\)'` |
||||
[ $unrotate -lt 1 -a $unrotate -gt 3 ] && errMsg "--- UNROTATE=$unrotate MUST BE EITHER 1, 2, 3 OR 4 ---" |
||||
;; |
||||
-i) # get innertrim |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID INNERTRIM SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
innertrim="$1" |
||||
innertrim=`echo "$innertrim" | tr "[:upper:]" "[:lower:]"` |
||||
case "$innertrim" in |
||||
yes|y) innertrim="yes" ;; |
||||
no|n) innertrim="no" ;; |
||||
*) errMsg "--- INNERTRIM=$innertrim IS AN INVALID VALUE ---" |
||||
esac |
||||
;; |
||||
-m) # mask |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID MASK SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
mask=`echo "$1" | tr "[:upper:]" "[:lower:]"` |
||||
[ "$mask" != "view" -a "$mask" != "save" -a "$mask" != "output" ] && errMsg "--- MASK=$mask MUST BE EITHER VIEW, SAVE OR OUTPUT ---" |
||||
;; |
||||
-t) # 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 VALUE (with no sign) ---" |
||||
testA=`echo "$threshold < 1" | bc` |
||||
[ $testA -eq 1 ] && errMsg "--- THRESHOLD=$threshold MUST BE A NON-NEGATIVE INTEGER VALUE GREATER THAN 0 ---" |
||||
;; |
||||
-r) # resize |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID RESIZE SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
resize=`expr "$1" : '\([.0-9]*\)'` |
||||
[ "$resize" = "" ] && errMsg "--- RESIZE=$resize MUST BE A NON-NEGATIVE FLOAT VALUE (with no sign) ---" |
||||
testA=`echo "$resize == 0" | bc` |
||||
[ $testA -eq 1 ] && errMsg "--- RESIZE=$resize MUST BE A NON-NEGATIVE FLOAT VALUE GREATER THAN 0 ---" |
||||
;; |
||||
-D) # density |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID DENSITY SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
density=`expr "$1" : '\([0-9]*\)'` |
||||
[ "$density" = "" ] && errMsg "--- DENSITY=$density MUST BE A NON-NEGATIVE INTEGER VALUE (with no sign) ---" |
||||
testA=`echo "$density == 0" | bc` |
||||
[ $testA -eq 1 ] && errMsg "--- DENSITY=$density MUST BE A NON-NEGATIVE INTEGER VALUE GREATER THAN 0 ---" |
||||
;; |
||||
-S) # sortval |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID SORTVAL SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
sortval=`expr "$1" : '\([0-9]*\)'` |
||||
testA=`echo "$sortval == 0" | bc` |
||||
[ $testA -eq 1 ] && errMsg "--- SORTVAL=$sortval MUST BE AN INTEGER VALUE GREATER THAN 0 ---" |
||||
;; |
||||
-s) # showstats |
||||
shift # to get the next parameter |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID SHOWSTATS SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
showstats=`echo "$1" | tr "[:upper:]" "[:lower:]"` |
||||
[ "$showstats" != "yes" -a "$showstats" != "no" ] && errMsg "--- SHOWSTATS=$showstats MUST BE EITHER YES OR NO ---" |
||||
;; |
||||
-v) # vc |
||||
vc="yes" |
||||
;; |
||||
-) # 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 if both bcolor and coords specified at the same time |
||||
if [ "$bcolor" != "" -a "$coords" != "" ]; then |
||||
errMsg "--- BACKGROUND COLOR AND COODINATES CAN NOT BE USED TOGETHER ---" |
||||
elif [ "$bcolor" = "" -a "$coords" = "" ]; then |
||||
coords="0,0" |
||||
fi |
||||
|
||||
# test that infile provided |
||||
[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED" |
||||
|
||||
# test that outfile provided |
||||
[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED" |
||||
|
||||
# test if input is single layer/page/frame |
||||
count=`convert -ping "$infile" -format "%m\n" info: | wc -l` |
||||
[ $count -gt 1 ] && errMsg "--- MULTIPAGE/MULTIFRAME/MULTILAYER IMAGES ARE NOT ALLOWED ---" |
||||
|
||||
if [ "$density" != "" ]; then |
||||
dproc="-density $density" |
||||
else |
||||
dproc="" |
||||
fi |
||||
|
||||
# set up temp file |
||||
tmpA1="$dir/multicrop2_1_$$.mpc" |
||||
tmpB1="$dir/multicrop2_1_$$.cache" |
||||
tmpA2="$dir/multicrop2_2_$$.mpc" |
||||
tmpB2="$dir/multicrop2_2_$$.cache" |
||||
tmpA3="$dir/multicrop2_3_$$.miff" |
||||
tmpA4="$dir/multicrop2_4_$$.mpc" |
||||
tmpB4="$dir/multicrop2_4_$$.cache" |
||||
|
||||
trap "rm -f $tmpA1 $tmpB1 $tmpA2 $tmpB2 $tmpA3 $tmpB4 $tmpA4;" 0 |
||||
trap "rm -f $tmpA1 $tmpB1 $tmpA2 $tmpB2 $tmpA3 $tmpB4 $tmpA4; exit 1" 1 2 3 15 |
||||
#trap "rm -f $tmpA1 $tmpB1 $tmpA2 $tmpB2 $tmpA3; exit 1" ERR |
||||
|
||||
# read the input image into the temp files and test validity. |
||||
if [ "$resize" != "" ]; then |
||||
convert -quiet $dproc "$infile" +repage "$tmpA4" || |
||||
errMsg "--- FILE $infile1 DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---" |
||||
convert "$tmpA4" -scale $resize% "$tmpA1" |
||||
else |
||||
convert -quiet $dproc "$infile" +repage "$tmpA1" || |
||||
errMsg "--- FILE $infile1 DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---" |
||||
fi |
||||
|
||||
|
||||
# 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` |
||||
|
||||
# get output filename and suffix |
||||
outnameArr=(`echo "$outfile" | sed -n 's/^\(.*\)[.]\([^.]*\)$/\1 \2/p'`) |
||||
outname="${outnameArr[0]}" |
||||
suffix="${outnameArr[1]}" |
||||
#echo "outname=$outname" |
||||
#echo "suffix=$suffix" |
||||
|
||||
if [ "$im_version" -ge "07000000" ]; then |
||||
identifying="magick identify" |
||||
else |
||||
identifying="identify" |
||||
fi |
||||
|
||||
# get image width and height |
||||
width=`$identifying -ping -format "%w" $tmpA1` |
||||
height=`$identifying -ping -format "%h" $tmpA1` |
||||
|
||||
# test for extend |
||||
test=`convert xc: -format "%[fx:$extend>0?1:0]" info:` |
||||
[ $test -eq 1 -a $unrotate -ne 3 ] && errMsg "--- SIGN FOR EXTEND=$extend MUST BE NEGATIVE ---" |
||||
[ $test -eq 1 -a $unrotate -eq 3 -a "$innertrim" = "yes" ] && errMsg "--- SIGN FOR EXTEND=$extend MUST BE NEGATIVE ---" |
||||
|
||||
# test if coords provided as x,y |
||||
# coords="" if test fails and have other characters such as northwest |
||||
coords1=`expr "$coords" : '\([0-9]*,[0-9]*\)'` |
||||
|
||||
# get color at user specified location |
||||
if [ "$bcolor" != "" ]; then |
||||
coords="0,0" |
||||
elif [ "$coords1" != "" -a "$resize" != "" ]; then |
||||
x=`echo "$coords1" | cut -d, -f1` |
||||
y=`echo "$coords1" | cut -d, -f2` |
||||
x=`convert xc: -format "%[fx:round($x*$resize/100)]" info:` |
||||
y=`convert xc: -format "%[fx:round($y*$resize/100)]" info:` |
||||
coords="$x,$y" |
||||
bcolor=`convert $tmpA1 -format "%[pixel:u.p{$coords}]" info:` |
||||
elif [ "$coords1" != "" -a "$resize" = "" ]; then |
||||
x=`echo "$coords1" | cut -d, -f1` |
||||
y=`echo "$coords1" | cut -d, -f2` |
||||
# account for pad of 1 |
||||
x=$((x+1)) |
||||
y=$((y+1)) |
||||
coords="$x,$y" |
||||
bcolor=`convert $tmpA1 -format "%[pixel:u.p{$coords}]" info:` |
||||
elif [ "$coords1" = "" ]; then |
||||
widthm1=`convert xc: -format "%[fx:$width-1]" info:` |
||||
heightm1=`convert xc: -format "%[fx:$height-1]" info:` |
||||
midwidth=`convert xc: -format "%[fx:round(($width-1))/2]" info:` |
||||
midheight=`convert xc: -format "%[fx:round(($height-1))/2]" info:` |
||||
coords=`echo "$coords" | tr "[:upper:]" "[:lower:]"` |
||||
case "$coords" in |
||||
''|nw|northwest) coords="0,0" ;; |
||||
n|north) coords="$midwidth,0" ;; |
||||
ne|northeast) coords="$widthm1,0" ;; |
||||
e|east) coords="$widthm1,$midheight" ;; |
||||
se|southeast) coords="$widthm1,$heightm1" ;; |
||||
s|south) coords="$midwidth,$heightm1" ;; |
||||
sw|southwest) coords="0,$heightm1" ;; |
||||
w|west) coords="0,$midheight" ;; |
||||
*) errMsg "--- INVALID COORDS ---" ;; |
||||
esac |
||||
bcolor=`convert $tmpA1 -format "%[pixel:u.p{$coords}]" info:` |
||||
fi |
||||
#echo "bcolor=$bcolor" |
||||
|
||||
# set up floodfill |
||||
if [ "$im_version" -ge "07000000" ]; then |
||||
matte_alpha="alpha" |
||||
else |
||||
matte_alpha="matte" |
||||
fi |
||||
|
||||
# add a border, and flood fill from all edges inward |
||||
convert $tmpA1 -fuzz ${fuzzval}% -fill none \ |
||||
-bordercolor $bcolor -border 1x1 \ |
||||
-draw "$matte_alpha $coords floodfill" \ |
||||
-shave 1x1 -fill white +opaque none \ |
||||
-background black -alpha background -alpha off -type bilevel \ |
||||
$tmpA2 |
||||
|
||||
if [ "$mask" = "view" ]; then |
||||
display $tmpA2 |
||||
elif [ "$mask" = "save" ]; then |
||||
convert $tmpA2 ${outname}_mask.gif |
||||
elif [ "$mask" = "output" ]; then |
||||
convert $tmpA2 ${outname}_mask.gif |
||||
exit 0 |
||||
fi |
||||
|
||||
# set up for unrotate 1 or 3 |
||||
if [ $unrotate -eq 1 ]; then |
||||
derotate="-background $bcolor -deskew 40%" |
||||
elif [ $unrotate -eq 3 ]; then |
||||
derotate="" |
||||
fi |
||||
|
||||
# set up discard |
||||
if [ "$discard" != "" ]; then |
||||
discarding="-define connected-components:area-threshold=$discard" |
||||
else |
||||
discarding="" |
||||
fi |
||||
|
||||
# process image using connected components labeling |
||||
echo "" |
||||
data=`convert $tmpA2 \ |
||||
-define connected-components:verbose=true \ |
||||
$discarding -connected-components 4 null:` |
||||
[ "$showstats" = "yes" ] && echo "$data" |
||||
|
||||
wwArr=(`echo "$data" | tail -n +2 | tr -cs "0-9.,\(\)\n" " " | awk '{print $2}'`) |
||||
hhArr=(`echo "$data" | tail -n +2 | tr -cs "0-9.,\(\)\n" " " | awk '{print $3}'`) |
||||
xoArr=(`echo "$data" | tail -n +2 | tr -cs "0-9.,\(\)\n" " " | awk '{print $4}'`) |
||||
yoArr=(`echo "$data" | tail -n +2 | tr -cs "0-9.,\(\)\n" " " | awk '{print $5}'`) |
||||
colorArr=(`echo "$data" | tail -n +2 | tr -cs "0-9.,gray\(\)\n" " " | awk '{print $8}'`) |
||||
num=${#wwArr[*]} |
||||
#echo $num |
||||
|
||||
# do sorting if specified |
||||
if [ "$sortval" != "" ]; then |
||||
# build dataArr and sort it |
||||
# note new line before last quote so have list for sorting |
||||
# add rounded points for sorting |
||||
nearest=$sortval |
||||
for((i=0; i<num; i++)); do |
||||
xx=${xoArr[$i]} |
||||
yy=${yoArr[$i]} |
||||
xr=`convert xc: -format "%[fx:$nearest*round($xx/$nearest)]" info:` |
||||
yr=`convert xc: -format "%[fx:$nearest*round($yy/$nearest)]" info:` |
||||
dataArr[$i]="${wwArr[$i]} ${hhArr[$i]} ${xoArr[$i]} ${yoArr[$i]} ${colorArr[$i]} $xr $yr |
||||
" |
||||
#echo ${dataArr[$i]} |
||||
done |
||||
OLDIFS=$IFS |
||||
IFS=$'\n' |
||||
sortArr=(`echo "${dataArr[*]}" | sort -n -k7,7 -k6,6`) |
||||
IFS=$OLDIFS |
||||
for((i=0; i<num; i++)); do |
||||
#echo "${sortArr[$i]}" |
||||
wwArr[$i]=`echo ${sortArr[$i]} | cut -d\ -f1` |
||||
hhArr[$i]=`echo ${sortArr[$i]} | cut -d\ -f2` |
||||
xoArr[$i]=`echo ${sortArr[$i]} | cut -d\ -f3` |
||||
yoArr[$i]=`echo ${sortArr[$i]} | cut -d\ -f4` |
||||
colorArr[$i]=`echo ${sortArr[$i]} | cut -d\ -f5` |
||||
done |
||||
fi |
||||
|
||||
# abort if too many objects |
||||
# subtract 1 for background |
||||
numm1=$((num-1)) |
||||
if [ "$threshold" != "" ]; then |
||||
[ $numm1 -gt $threshold ] && errMsg "--- TOO MANY OBJECTS DETECTED ---" |
||||
fi |
||||
|
||||
if [ "$vc" = "yes" ]; then |
||||
repaging="" |
||||
else |
||||
repaging="+repage" |
||||
fi |
||||
#echo "repaging=$repaging;" |
||||
|
||||
echo "" |
||||
k=0 |
||||
for ((i=0; i<num; i++)); do |
||||
color="${colorArr[$i]}" |
||||
if [ "$color" != "gray(0)" ]; then |
||||
echo "Processing Image $k" |
||||
kk=`printf "%03d" "$k"` |
||||
offx=${xoArr[$i]} |
||||
offy=${yoArr[$i]} |
||||
wd=${wwArr[$i]} |
||||
ht=${hhArr[$i]} |
||||
if [ "$resize" != "" ]; then |
||||
wd=`convert xc: -format "%[fx:round($wd*100/$resize)+2*$extend]" info:` |
||||
ht=`convert xc: -format "%[fx:round($ht*100/$resize)+2*$extend]" info:` |
||||
offx=`convert xc: -format "%[fx:round($offx*100/$resize)-$extend]" info:` |
||||
offy=`convert xc: -format "%[fx:round($offy*100/$resize)-$extend]" info:` |
||||
img=$tmpA4 |
||||
else |
||||
wd=$((wd+2*extend)) |
||||
ht=$((ht+2*extend)) |
||||
offx=$((offx-extend)) |
||||
offy=$((offy-extend)) |
||||
img=$tmpA1 |
||||
fi |
||||
echo "Initial Crop Box: ${wd}x${ht}+${offx}+${offy}" |
||||
echo "" |
||||
if [ $unrotate -eq 3 ]; then |
||||
convert $img -crop ${wd}x${ht}+${offx}+${offy} $repaging $tmpA3 |
||||
elif [ $unrotate -eq 1 ]; then |
||||
convert $img -crop ${wd}x${ht}+${offx}+${offy} +repage $derotate \ |
||||
-bordercolor "$bcolor" -border 2 -fuzz ${fuzzval}% -trim +repage $tmpA3 |
||||
elif [ $unrotate -eq 2 ]; then |
||||
convert $img -crop ${wd}x${ht}+${offx}+${offy} +repage \ |
||||
-fuzz ${fuzzval}% -trim miff:- | \ |
||||
unrotate -C "$bcolor" -f ${fuzzval}% - $tmpA3 |
||||
fi |
||||
if [ "$innertrim" = "yes" ]; then |
||||
mextend=-$extend |
||||
autotrim -m inner -f $fuzzval -C "$bcolor" -l $mextend -t $mextend \ |
||||
-r $extend -b $extend $tmpA3 ${outname}-${kk}.${suffix} |
||||
echo "" |
||||
else |
||||
convert $tmpA3 ${outname}-${kk}.${suffix} |
||||
fi |
||||
k=$((k+1)) |
||||
fi |
||||
done |
||||
echo "" |
||||
|
||||
exit 0 |
||||
|
||||
|
||||
|
@ -0,0 +1,581 @@ |
||||
#!/bin/bash |
||||
# |
||||
# Developed by Fred Weinhaus 12/12/2007 .......... revised 10/16/2017 |
||||
# |
||||
# ------------------------------------------------------------------------------ |
||||
# |
||||
# 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: unrotate [-f fuzzval] [-c coords] [-C color] [-a angle] [-l left] [-r right ] |
||||
# [-t top ] [-b bottom ] infile [outfile] |
||||
# USAGE: unrotate [-h or -help] |
||||
# |
||||
# OPTIONS: |
||||
# |
||||
# -f fuzzval fuzz value for determining border color; |
||||
# expressed as (float) percent 0 to 100; |
||||
# default=0 (uniform color) |
||||
# -c coords pixel coordinate to extract color; may be |
||||
# expressed as gravity value (NorthWest, etc) |
||||
# or as "x,y" value; default is NorthWest=(0,0) |
||||
# -C color border color; any valid IM color is allowed, |
||||
# default is to use coords to get color |
||||
# -a angle angle of rotation of image; default indicates |
||||
# to autocalculate; -45<=angle<=45 degrees (float) |
||||
# -l left pixel shift of left edge; +/- is right/left |
||||
# default=0 (no change) |
||||
# -r right pixel shift of right edge; +/- is right/left |
||||
# default=0 (no change) |
||||
# -t top pixel shift of top edge; +/- is down/up |
||||
# default=0 (no change) |
||||
# -b bottom pixel shift of bottom edge; +/- is down/up |
||||
# default=0 (no change) |
||||
# -h or -help get help |
||||
# [outfile] if outfile is left off, the script will simply |
||||
# report the rotation angle needed to unrotate |
||||
# the image. |
||||
# |
||||
### |
||||
# |
||||
# NAME: UNROTATE |
||||
# |
||||
# PURPOSE: To unrotate a rotated image and trim the surrounding border. |
||||
# |
||||
# DESCRIPTION: UNROTATE computes the amount an image has been rotated and |
||||
# attempts to automatically unrotate the image. It assumes that the image |
||||
# contains a border area around the rotated data and that one must identify |
||||
# a coordinate within the border area for the algorithm to extract the base |
||||
# border color. A fuzz value should be specified when the border color is not |
||||
# uniform, but also because the edge of rotated image is a blend of image and |
||||
# border color. Thus, the fuzz value must be a compromise. If too large, the |
||||
# rotation angle will not be accurate. If too small, the blended edge around |
||||
# unrotated image will not be trimmed enough. The rotation angle displayed |
||||
# by the script for an appropriate fuzz value will typically be within a few |
||||
# tenths of a degree of the correct value. However, if the results are not |
||||
# accurate enough or there is still some border showing, you may rerun the |
||||
# script on the original image again and either specify an adjusted rotation |
||||
# angle or use the left/right/top/bottom arguments to specify extra trim. |
||||
# |
||||
# |
||||
# Arguments: |
||||
# |
||||
# -h or -help --- displays help information. |
||||
# |
||||
# -f fuzzval --- FUZZVAL is the fuzz amount specified as a percent 0 to 100 |
||||
# (without the % sign). The default is zero which indicates that border is a |
||||
# uniform color. Larger values are needed when the border is not a uniform |
||||
# color and to trim the border of the rotated area where the image data is |
||||
# a blend with the border color. |
||||
# |
||||
# -c coords --- COORDS is any location within the border area for the |
||||
# algorithm to find the base border color. It may be specified in terms of |
||||
# gravity parameters (NorthWest, North, NorthEast, East, SouthEast, South, |
||||
# SouthWest or West) or as a pixel coordinate "x,y". The default is the |
||||
# upper left corner = NorthWest = "0,0". |
||||
# |
||||
# -C color ... COLOR of border or background. Any valid IM color is allowed. |
||||
# The default is to use coords to get color. |
||||
# |
||||
# -a angle --- ANGLE is the rotation angle needed to unrotate the picture data |
||||
# within the image. The default (no argument) tells the algorithm to automatically |
||||
# estimate the rotation angle. One may override the automatic determination and |
||||
# specify your own value. Values are positive floats between -45 and 45 degrees. |
||||
# Note that the algorithm cannot correct beyond 45 degrees and cannot |
||||
# distinguish between exactly +45 degrees and exactly -45. Therefore you |
||||
# may need to do a 90, 180, or 270 degree rotation after using this script. |
||||
# You may need to do a 90, 180, or 270 degree rotation after using this script. |
||||
# If the outfile is left off, then the script will simply report the rotation |
||||
# angle needed to unrotate the image. |
||||
# |
||||
# -l left --- LEFT is the number of extra pixels to shift the trim of the left |
||||
# edge of the image. The trim is shifted right/left for +/- integer values. |
||||
# The default=0. |
||||
# |
||||
# -r right --- RIGHT is the number of extra pixels to shift the trim of the right |
||||
# edge of the image. The trim is shifted right/left for +/- integer values. |
||||
# The default=0. |
||||
# |
||||
# -t top --- TOP is the number of extra pixels to shift the trim of the top |
||||
# edge of the image. The trim is shifted down/up for +/- integer values. |
||||
# The default=0. |
||||
# |
||||
# -b bottom --- BOTTOM is the number of extra pixels to shift the trim of the |
||||
# bottom edge of the image. The trim is shifted down/up for +/- integer values. |
||||
# The default=0. |
||||
# |
||||
# 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; |
||||
fuzzval=0 # fuzz threshold |
||||
coords="NorthWest" # coordinates to get color |
||||
color="" |
||||
pad=1 # border pad size |
||||
rotang="" # rotation angle -45 to 45 or "" for calc automatic |
||||
lt=0 # left edge shift of trim (+/- is right/left) |
||||
rt=0 # right edge shift of trim (+/- is right/left) |
||||
tp=0 # top edge shift of trim (+/- is down/up) |
||||
bm=0 # top bottom shift of trim (+/- is down/up) |
||||
|
||||
# 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" |
||||
} |
||||
|
||||
# test for correct number of arguments and get values |
||||
if [ $# -eq 0 ] |
||||
then |
||||
# help information |
||||
echo "" |
||||
usage2 |
||||
exit 0 |
||||
elif [ $# -gt 18 ] |
||||
then |
||||
errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---" |
||||
else |
||||
while [ $# -gt 0 ] |
||||
do |
||||
# get parameters |
||||
case "$1" in |
||||
-h|-help) # help information |
||||
echo "" |
||||
usage2 |
||||
;; |
||||
-f) # fuzzval |
||||
shift # to get the next parameter - fuzzval |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID FUZZVAL SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
fuzzval=`expr "$1" : '\([.0-9]*\)'` |
||||
[ "$fuzzval" = "" ] && errMsg "--- FUZZVAL=$fuzzval MUST BE A NON-NEGATIVE FLOATING POINT VALUE (with no sign) ---" |
||||
fuzzvaltest=`echo "$fuzzval < 0" | bc` |
||||
[ $fuzzvaltest -eq 1 ] && errMsg "--- FUZZVAL=$fuzzval MUST BE A NON-NEGATIVE FLOATING POINT VALUE ---" |
||||
;; |
||||
-c) # coords |
||||
shift # to get the next parameter - coords |
||||
# test if parameter starts with minus sign |
||||
errorMsg="--- INVALID COORDS SPECIFICATION ---" |
||||
checkMinus "$1" |
||||
coords=$1 |
||||
# further testing done later |
||||
;; |
||||
-C) # color |
||||
shift # to get the next parameter - coords |
||||
color="$1" |
||||
;; |
||||
-a) # angle |
||||
shift # to get the next parameter - angle |
||||
# test if parameter starts with minus sign |
||||
#errorMsg="--- INVALID ANGLE SPECIFICATION ---" |
||||
#checkMinus "$1" |
||||
rotang=`expr "$1" : '\([.0-9\-]*\)'` |
||||
[ "$rotang" = "" ] && errMsg "--- ANGLE=$rotang MUST BE A NON-NEGATIVE FLOATING POINT VALUE (with no sign) ---" |
||||
rotangtestA=`echo "$rotang < -45" | bc` |
||||
rotangtestB=`echo "$rotang > 45" | bc` |
||||
[ $rotangtestA -eq 1 -a $rotangtestB -eq 1 ] && errMsg "--- ANGLE=$rotang MUST BE A NON-NEGATIVE FLOATING POINT VALUE LESS THAN OR EQUAL TO 45 ---" |
||||
;; |
||||
-l) # left |
||||
shift # to get the next parameter - left |
||||
lt=`expr "$1" : '\([0-9\-]*\)'` |
||||
[ "$lt" = "" ] && errMsg "--- LEFT=$lt MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" |
||||
;; |
||||
-r) # right |
||||
shift # to get the next parameter - right |
||||
rt=`expr "$1" : '\([0-9\-]*\)'` |
||||
[ "$rt" = "" ] && errMsg "--- RIGHT=$rt MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" |
||||
;; |
||||
-t) # top |
||||
shift # to get the next parameter - top |
||||
tp=`expr "$1" : '\([0-9\-]*\)'` |
||||
[ "$tp" = "" ] && errMsg "--- TOP=$tp MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" |
||||
;; |
||||
-b) # bottom |
||||
shift # to get the next parameter - bottom |
||||
bm=`expr "$1" : '\([0-9\-]*\)'` |
||||
[ "$bm" = "" ] && errMsg "--- BOTTOM=$bm MUST BE AN INTEGER VALUE (with no sign or minus sign) ---" |
||||
;; |
||||
-) # 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" |
||||
|
||||
# setup temporary images and auto delete upon exit |
||||
# use mpc/cache to hold input image temporarily in memory |
||||
tmpA="$dir/unrotate_$$.mpc" |
||||
tmpB="$dir/unrotate_$$.cache" |
||||
tmp0="$dir/unrotate_0_$$.mpc" |
||||
tmp0a="$dir/unrotate_0_$$.cache" |
||||
tmp1="$dir/unrotate_1_$$.mpc" |
||||
tmp1a="$dir/unrotate_1_$$.cache" |
||||
tmp2="$dir/unrotate_2_$$.mpc" |
||||
tmp2a="$dir/unrotate_2_$$.cache" |
||||
tmp3="$dir/unrotate_3_$$.mpc" |
||||
tmp3a="$dir/unrotate_3_$$.cache" |
||||
tmp4="$dir/unrotate_4_$$.miff" |
||||
trap "rm -f $tmpA $tmpB $tmp0 $tmp0a $tmp1 $tmp1a $tmp2 $tmp2a $tmp3 $tmp3a $tmp4;" 0 |
||||
trap "rm -f $tmpA $tmpB $tmp0 $tmp0a $tmp1 $tmp1a $tmp2 $tmp2a $tmp3 $tmp3a $tmp4; exit 1" 1 2 3 15 |
||||
trap "rm -f $tmpA $tmpB $tmp0 $tmp0a $tmp1 $tmp1a $tmp2 $tmp2a $tmp3 $tmp3a; exit 1" ERR |
||||
|
||||
# test input image |
||||
convert -quiet "$infile" +repage $tmpA || |
||||
errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---" |
||||
|
||||
|
||||
# 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 autolevel. |
||||
# with IM 6.7.4.10, 6.7.6.10, 6.7.8.6 |
||||
if [ "$im_version" -lt "06070607" -o "$im_version" -gt "06070707" ]; then |
||||
setcspace1="-set colorspace RGB" |
||||
setcspace2="-set colorspace sRGB" |
||||
fi |
||||
# no need for setcspace for grayscale or channels after 6.8.5.4 |
||||
if [ "$im_version" -gt "06080504" ]; then |
||||
setcspace1="" |
||||
setcspace2="" |
||||
fi |
||||
|
||||
# set up floodfill |
||||
if [ "$im_version" -ge "07000000" ]; then |
||||
matte_alpha="alpha" |
||||
identifying="magick identify" |
||||
else |
||||
matte_alpha="matte" |
||||
identifying="identify" |
||||
fi |
||||
|
||||
# function to get dimensions |
||||
dimensions() |
||||
{ |
||||
width=`$identifying -format "%w" $1` |
||||
height=`$identifying -format "%h" $1` |
||||
widthm1=`expr $width - 1` |
||||
heightm1=`expr $height - 1` |
||||
midwidth=`echo "scale=0; $width / 2" | bc` |
||||
midheight=`echo "scale=0; $height / 2" | bc` |
||||
widthmp=`expr $width - 2 \* $pad` |
||||
heightmp=`expr $height - 2 \* $pad` |
||||
} |
||||
|
||||
# function to get color at user specified location |
||||
getColor() |
||||
{ |
||||
dimensions $tmpA |
||||
case "$coords" in |
||||
NorthWest|Northwest|northwest) coords="0,0" |
||||
;; |
||||
North|north) coords="$midwidth,0" |
||||
;; |
||||
NorthEast|Northeast|northeast) coords="$widthm1,0" |
||||
;; |
||||
East|east) coords="$widthm1,$midheight" |
||||
;; |
||||
SouthEast|Southeast|southeast) coords="$widthm1,$heightm1" |
||||
;; |
||||
South|south) coords="$midwidth,$heightm1" |
||||
;; |
||||
SouthWest|Southwest|southwest) coords="0,$heightm1" |
||||
;; |
||||
West|west) coords="0,$midheight" |
||||
;; |
||||
[0-9]*,[0-9]*) coords=$coords |
||||
;; |
||||
*) errMsg "--- INVALID COORDS ---" |
||||
;; |
||||
esac |
||||
tmpcolor=`convert $tmpA -format "%[pixel:u.p{$coords}]" info:` |
||||
if [ "$color" = "" ]; then |
||||
color=$tmpcolor |
||||
fi |
||||
} |
||||
|
||||
# function to pad and extract binary image |
||||
paddedBinary() |
||||
{ |
||||
fuzzthresh=$1 |
||||
# reset coords to 0,0 |
||||
coords="0,0" |
||||
|
||||
# pad image with border of color found at original coords |
||||
convert $tmpA -bordercolor "$color" -border ${pad}x${pad} $tmpA |
||||
|
||||
# get dimensions of padded image |
||||
dimensions $tmpA |
||||
|
||||
# make exterior transparent and inside white |
||||
convert $tmpA -fuzz $fuzzthresh% -fill none \ |
||||
-draw "$matte_alpha $coords floodfill" -fill white +opaque none $tmp0 |
||||
|
||||
# make exterior black and inside white |
||||
convert \( -size ${width}x${height} xc:black \) $tmp0 -compose over -composite $tmp1 |
||||
# convert $tmp0 -background black -compose over -flatten $tmp1 |
||||
} |
||||
|
||||
# function to get black to white transition location along row or column |
||||
# specify arguments 1D image of data, dimension=width,height,widthm1 or heightm1, and direction=inc or dec |
||||
getTransition() |
||||
{ |
||||
img1D=$1 |
||||
dim=$2 |
||||
direct=$3 |
||||
rowcol=`convert $img1D -compress None -depth 8 txt:-` |
||||
vals=`echo "$rowcol" | sed -n 's/^[0-9]*,[0-9]*: [(].*[)] #...... \(.*\)$/\1/p'` |
||||
vals_Array=($vals) |
||||
#echo "$vals" |
||||
if [ "$direct" = "inc" ] |
||||
then |
||||
i=0 |
||||
while [ $i -lt $dim ] |
||||
do |
||||
[ "${vals_Array[$i]}" = "white" ] && break |
||||
i=`expr $i + 1` |
||||
done |
||||
location=$i |
||||
elif [ "$direct" = "dec" ] |
||||
then |
||||
i=$dim |
||||
while [ $i -ge 0 ] |
||||
do |
||||
[ "${vals_Array[$i]}" = "white" ] && break |
||||
i=`expr $i - 1` |
||||
done |
||||
location=$i |
||||
fi |
||||
} |
||||
|
||||
# function to process binary to get cropped image |
||||
cropImage() |
||||
{ |
||||
trim=$1 |
||||
angle=$2 |
||||
if [ "$angle" = "" ] |
||||
then |
||||
thresh=1 |
||||
normalize="" |
||||
elif [ `echo "($angle >= -45) -a ($angle <= 45)" | bc` -eq 1 ] |
||||
then |
||||
# threshold relation to angle determined empirically and seems to be reasonably good, but not perfect |
||||
if [ `echo "$angle <= 5" | bc` -eq 1 ] |
||||
then |
||||
thresh=`echo "scale=1; (99 - (1.0 * $angle)) / 1" | bc` |
||||
else |
||||
thresh=`echo "scale=1; (99 - (1.07 * $angle)) / 1" | bc` |
||||
fi |
||||
thresh=$thresh% |
||||
# normalize="-normalize" |
||||
normalize="-contrast-stretch 0" |
||||
|
||||
else |
||||
echo "--- INVALID ANGLE VALUE ---" |
||||
exit 1 |
||||
fi |
||||
|
||||
# average to one row and one column |
||||
# convert $tmp1 -filter box -resize 1x${height}! $normalize -threshold $thresh $tmp2 |
||||
# convert $tmp1 -filter box -resize ${width}x1! $normalize -threshold $thresh $tmp3 |
||||
|
||||
# need to add -set colorspace RGB for versions after 6.7.7.7 to convert to nonlinear gray so thresholds correctly |
||||
# some versions after 6.6.9.10 and before 6.7.2.10 (tested at 6.7.2.0 and 6.7.2.1) produce srgb(...) values so must trap out -set colorspace sRGB before the output so they report black and white |
||||
# with the above scheme a few odd versions will show some white in the corners (e.g. tests show that in 6.7.7.8, 6.7.7.10, 6.7.8.2, 6.7.8.3, 6.8.4.0, 6.8.4.2, but not before 6.7.7.8 or 6.8.1.9, 6.8.3.9, 6.8.4.3 and after) |
||||
convert $tmp1 -scale 1x${height}! $normalize $setcspace1 -threshold $thresh $setcspace2 $tmp2 |
||||
convert $tmp1 -scale ${width}x1! $normalize $setcspace1 -threshold $thresh $setcspace2 $tmp3 |
||||
|
||||
# get top and bottom by locating first occurence of value=white from top and bottom of column |
||||
getTransition $tmp2 $height "inc" |
||||
top=$location |
||||
|
||||
getTransition $tmp2 $heightm1 "dec" |
||||
bottom=$location |
||||
|
||||
# get left and right by locating first occurence of value=white from left and right of row |
||||
getTransition $tmp3 $width "inc" |
||||
left=$location |
||||
|
||||
getTransition $tmp3 $widthm1 "dec" |
||||
right=$location |
||||
|
||||
#compute start x and y and width and height |
||||
if [ "$trim" = "" ] |
||||
then |
||||
new_x=$left |
||||
new_y=$top |
||||
new_width=`expr $right - $left + 1` |
||||
new_height=`expr $bottom - $top + 1` |
||||
else |
||||
new_x=`expr $left + $lt` |
||||
new_y=`expr $top + $tp` |
||||
new_width=`expr $right - $left - $lt + $rt + 1` |
||||
new_height=`expr $bottom - $top - $tp + $bm + 1` |
||||
fi |
||||
|
||||
#echo "top=$top; bottom=$bottom; left=$left; right=$right;" |
||||
#echo "new_x=$new_x; new_y=$new_y; new_width=$new_width; new_height=$new_height;" |
||||
|
||||
#crop image |
||||
convert $tmpA[${new_width}x${new_height}+${new_x}+${new_y}] +repage $tmpA |
||||
} |
||||
|
||||
# function to compute rotation angle |
||||
computeRotation() |
||||
{ |
||||
# start with image already cropped to outside bounds of rotated image |
||||
|
||||
# get new dimension |
||||
dimensions $tmpA |
||||
|
||||
# get padded bindary image |
||||
paddedBinary $fuzzval |
||||
|
||||
# trim off pad (repage to clear page offsets) |
||||
convert $tmp1[${widthmp}x${heightmp}+1+1] +repage $tmp1 |
||||
|
||||
# get rotation angle |
||||
# get coord of 1st white pixel in left column |
||||
getTransition $tmp1[1x${height}+0+0] $height "inc" |
||||
p1x=1 |
||||
p1y=$location |
||||
|
||||
# get coord of 1st white pixel in top row |
||||
getTransition $tmp1[${width}x1+0+0] $width "inc" |
||||
p2x=$location |
||||
p2y=1 |
||||
|
||||
# compute slope and angle (reverse sign of dely as y increases downward) |
||||
delx=`expr $p2x - $p1x` |
||||
dely=`expr $p1y - $p2y` |
||||
if [ $delx -eq 0 ] |
||||
then |
||||
rotang=0 |
||||
else |
||||
pi=`echo "scale=10; 4*a(1)" | bc -l` |
||||
angle=`echo "scale=5; (180/$pi) * a($dely / $delx)" | bc -l` |
||||
if [ `echo "$angle > 45" | bc` -eq 1 ] |
||||
then |
||||
rotang=`echo "scale=2; ($angle - 90.005) / 1" | bc` |
||||
else |
||||
rotang=`echo "scale=2; ($angle + 0.005) / 1" | bc` |
||||
fi |
||||
fi |
||||
} |
||||
|
||||
|
||||
# start processing |
||||
|
||||
# get color at user specified location |
||||
getColor |
||||
|
||||
# get rotation angle if appropriate |
||||
if [ "$rotang" = "" ] |
||||
then |
||||
# crop out any border to get bounding box |
||||
paddedBinary $fuzzval |
||||
cropImage "" "" |
||||
computeRotation |
||||
fi |
||||
if [ "$outfile" = "" ] |
||||
then |
||||
echo "" |
||||
echo "Image Needs To Be Rotated $rotang degrees" |
||||
echo "" |
||||
else |
||||
echo "" |
||||
echo "Image Is Being Rotated $rotang degrees" |
||||
echo "" |
||||
# unrotate |
||||
# fixed 8/20/2012 +repage issue (after rotate) that started between IM 6.7.2.10 and IM 6.7.3.10 and continued |
||||
# convert $tmpA +repage -background $color -rotate $rotang $tmpA |
||||
convert $tmpA +repage -background "$color" -rotate $rotang +repage $tmpA |
||||
# process to trim (no rotation) |
||||
paddedBinary $fuzzval |
||||
cropImage "trim" "" |
||||
# due to bug in IM 6.9.9.12 - 6.9.9.19 in freeing memory for MPC, write to miff, then save to output |
||||
# no problems with IM 7 versions |
||||
if [ "$im_version" -ge "06090909" -o "$im_version" -le "06090919" ]; then |
||||
convert $tmpA $tmp4 |
||||
convert $tmp4 +repage "$outfile" |
||||
else |
||||
convert $tmpA +repage "$outfile" |
||||
fi |
||||
fi |
||||
exit 0 |
Loading…
Reference in new issue