summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bin/2colorthresh168
-rw-r--r--bin/fuzzythresh407
-rw-r--r--bin/isodatathresh346
-rw-r--r--bin/kapurthresh329
-rw-r--r--bin/kmeansthresh428
-rw-r--r--bin/localthresh347
-rw-r--r--bin/otsuthresh368
-rw-r--r--bin/ptilethresh213
-rw-r--r--bin/sahoothresh385
-rw-r--r--bin/trianglethresh419
-rwxr-xr-xphotoblaster/modules/pbbreaker/__init__.py2
-rwxr-xr-xshare/frontend/imbreak/index.html2
12 files changed, 3412 insertions, 2 deletions
diff --git a/bin/2colorthresh b/bin/2colorthresh
new file mode 100644
index 0000000..b05005c
--- /dev/null
+++ b/bin/2colorthresh
@@ -0,0 +1,168 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/29/2008 .......... revised 11/3/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: 2colorthresh infile outfile
+# USAGE: 2colorthresh [-help]
+#
+# OPTIONS:
+#
+# There are no options
+#
+###
+#
+# NAME: 2COLORTHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using an adaptive spatial subdivision color reduction technique.
+#
+# DESCRIPTION: 2COLORTHRESH automatically thresholds an image to binary
+# (b/w) format using an adaptive spatial subdivision color reduction
+# technique. This is the -colors IM operator as implemented with slight
+# modification from Anthony's Examples at
+# http://www.imagemagick.org/Usage/quantize/#two_color
+# For algorithm details, see http://www.imagemagick.org/script/quantize.php
+#
+# OPTIONS:
+#
+# There are no options.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# 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 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 2 ]
+ 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
+ ;;
+ -) # 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/2colorsthresh_1_$$.mpc"
+tmpA2="$dir/2colorsthresh_1_$$.cache"
+trap "rm -f $tmpA1 $tmpA2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2; exit 1" 1 2 3 15
+
+if convert -quiet "$infile" +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# process image
+convert $tmpA1 +dither -colors 2 -colorspace gray -contrast-stretch 0 "$outfile"
+
+exit 0
+
+
+
diff --git a/bin/fuzzythresh b/bin/fuzzythresh
new file mode 100644
index 0000000..51c269d
--- /dev/null
+++ b/bin/fuzzythresh
@@ -0,0 +1,407 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/30/2008 .......... revised 11/3/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: fuzzythresh [-r radius] [-g graph] infile outfile
+# USAGE: fuzzythresh [-help]
+#
+# OPTIONS:
+#
+# -r radius radius for spatial correlation; float;
+# radius>=0; the default=0 signifies to
+# ignore spatial correlation
+# -g graph graph specifies whether to generate a
+# histogram graph image displaying the
+# location and value of the threshold;
+# choices are: view or save;
+# default is no graph
+#
+###
+#
+# NAME: FUZZYTHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using the fuzzy c-means technique.
+#
+# DESCRIPTION: FUZZYTHRESH automatically thresholds an image to binary
+# (b/w) format. It assume the histogram is bimodal, i.e. is the composite
+# of two bell-shaped distributions representing the foreground and
+# background classes. The fuzzy c-means appoach iteratively thresholds the
+# image, computes a weighted mean for the foreground (above threshold data)
+# and background (at and below threshold value), computes a new threshold
+# equal to the average of these two means and repeats until there is no
+# change in threshold between successive iterations. The weighting factors
+# are the normalized inverse square difference between each pixel in the
+# foreground or background data sets and the corresponding mean. However,
+# to allow for spatial correlation of the graylevels among neighboring
+# pixels, a Gaussian filtered version of the weighting factors may be used.
+# This script is similar to the isodatathresh script, except uses a weighted
+# mean calculation.
+#
+# OPTIONS:
+#
+# -r radius ... RADIUS is the radius of a Gaussian (blur) filter to apply
+# to the weighting factors for the mean calculation. This permits spatial
+# correlation of the graylevels among neighboring pixels and is useful
+# to remove noise from the result. The radius value should be set to
+# the size of the features in the image. Values are floats greater than
+# zero. The default=0 signifies to ignore spatial correlation.
+#
+# -g graph ... GRAPH specifies whether to generate a graph (image) of
+# the histogram, displaying the location and value of the threshold.
+# The choices are: view, save and none. If graph=view is selected, the
+# graph will be created and displayed automatically, but not saved.
+# If graph=save is selected, then the graph will be created and saved
+# to a file using the infile name, with "_histog_isodata.gif" appended,
+# but the graph will not be displayed automatically. If -g option is
+# not specified, then no graph will be created.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES: see the following:
+# http://www.iiitmk.ac.in/wiki/images/d/d2/Fuzzy_CMeans.pdf
+# http://www.quantlet.com/mdstat/scripts/xag/html/xaghtmlframe149.html
+# http://home.dei.polimi.it/matteucc/Clustering/tutorial_html/cmeans.html
+#
+# 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
+radius=0
+graph="" #save or view
+
+# 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 6 ]
+ 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
+ ;;
+ -r) # get radius
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID RADIUS SPECIFICATION ---"
+ checkMinus "$1"
+ radius=`expr "$1" : '\([.0-9]*\)'`
+ [ "$radius" = "" ] && errMsg "--- RADIUS=$radius MUST BE A NON-NEGATIVE FLOAT ---"
+ ;;
+ -g) # get graph
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID GRAPH SPECIFICATION ---"
+ checkMinus "$1"
+ graph="$1"
+ [ "$graph" != "view" -a "$graph" != "save" ] && errMsg "--- GRAPH=$graph MUST BE EITHER VIEW OR SAVE ---"
+ ;;
+ -) # 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"
+
+# get outname from infile to use for graph
+inname=`convert $infile -format "%t" info:`
+histfile=${inname}_histog_fuzzy.gif
+
+tmpA1="$dir/fuzzythresh_1_$$.mpc"
+tmpA2="$dir/fuzzythresh_1_$$.cache"
+tmpM1="$dir/fuzzythresh_M_$$.mpc"
+tmpM2="$dir/fuzzythresh_M_$$.cache"
+tmpL1="$dir/fuzzythresh_L_$$.mpc"
+tmpL2="$dir/fuzzythresh_L_$$.cache"
+tmpH1="$dir/fuzzythresh_H_$$.mpc"
+tmpH2="$dir/fuzzythresh_H_$$.cache"
+tmpD1="$dir/fuzzythresh_D_$$.mpc"
+tmpD2="$dir/fuzzythresh_d_$$.cache"
+trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpL1 $tmpL2 $tmpH1 $tmpH2 $tmpD1 $tmpD2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpL1 $tmpL2 $tmpH1 $tmpH2 $tmpD1 $tmpD2 $histfile; exit 1" 1 2 3 15
+
+# 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 fuzzythresh.
+# with IM 6.7.4.10, 6.7.6.10, 6.7.8.10
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace Gray +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# get image width and heigh
+ww=`convert $tmpA1 -format "%w" info:`
+hh=`convert $tmpA1 -format "%h" info:`
+
+getMinMax()
+ {
+ img="$1"
+ if [ "$im_version" -ge "06030901" ]
+ then
+ min=`convert $img -format "%[min]" info:`
+ max=`convert $img -format "%[max]" info:`
+ min=`convert xc: -format "%[fx:100*$min/quantumrange]" info:`
+ max=`convert xc: -format "%[fx:100*$max/quantumrange]" info:`
+ else
+ data=`convert $img -verbose info:`
+ min=`echo "$data" | sed -n 's/^.*[Mm]in:.*[(]\([0-9.]*\).*$/\1/p ' | head -1`
+ max=`echo "$data" | sed -n 's/^.*[Mm]ax:.*[(]\([0-9.]*\).*$/\1/p ' | head -1`
+ min=`convert xc: -format "%[fx:100*$min)]" info:`
+ max=`convert xc: -format "%[fx:100*$max)]" info:`
+ fi
+ }
+
+
+getMean()
+ {
+ img="$1"
+ 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
+ }
+
+
+# set gaussian filter
+if [ "$radius" = "0" -o "$radius" = "0.0" ]; then
+ filter=""
+else
+ filter="-blur 0x${radius}"
+fi
+
+
+# process image using isodata approach
+
+echo ""
+echo "Compute Threshold"
+# begin threshold at ave of min and max
+getMinMax "$tmpA1"
+min=$min
+max=$max
+thresh=`convert xc: -format "%[fx:($min+$max)/2]" info:`
+oldthresh=0
+diff=`convert xc: -format "%[fx:abs($thresh-$oldthresh)]" info:`
+test=`echo "$diff == 0" | bc`
+k=1
+# iterate the following
+# threshold image at newmean
+# get weighted means above and below threshold
+# where weights are the normalized inverse squared differences of each pixel from the class mean
+# get average of hmean and lmean as new threshold
+# repeat until difference between oldthresh and current thresh is zero
+#
+# Note: low and high (below threshold and above threshold) means
+# are extracted by masking and not by histogram counting.
+# Thus they need to be corrected for mixture of black pixels.
+# So, mean from masks (Mmeasured) and actual low/high means, Mtrue, are related
+# Mmeasured=(Sum(Cw*Gw)+Sum(Cb*0))/Ct = Sum(Cw*Gw)/Ct, summed over whole image
+# where Cw=count of white mask pixels, Gw graylevel at those pixels, Ct=total image pixels
+# Mtrue=Sum(Cw*Gw)/Ctw, where Ctw is total count of only white (non-black) mask pixels
+# Ctw=Mean of the mask/100
+# All graylevels are in range 0-100
+#
+while [ "$test" != "1" ]; do
+ echo "Iteration $k"
+ oldthresh=$thresh
+
+ # get high mask and its mean
+ convert $tmpA1 -threshold ${thresh}% $tmpM1
+ getMean "$tmpM1"
+ mmean=$mean
+
+ # compute high membership as normalized inverse squared difference of each pixel from high mean
+ # in this case we just use -compose difference -evalute pow 2 and negate it (so inverse squared distance scaled to Q)
+ convert $tmpA1 \( -size ${ww}x${hh} xc:"gray($mmean%)" \) \
+ -compose difference -evaluate pow 2 $filter -negate $tmpD1
+
+ # apply multiply membership image by image as weighting and mask to get the resulting mean
+ convert $tmpA1 $tmpD1 -compose multiply -composite \
+ $tmpM1 -compose multiply -composite \
+ $tmpH1
+ getMean "$tmpH1"
+ hmean=$mean
+# echo "mmean=$mmean; hmean=$hmean"
+ # correct hmean using mmean
+ hmean=`convert xc: -format "%[fx:(100*$hmean/$mmean)]" info:`
+
+ # get low mean
+ # low mean = 100% - high mean
+ nmean=`convert xc: -format "%[fx:(100-$mmean)]" info:`
+
+ # compute low membership as normalized inverse squared difference of each pixel from high mean
+ # in this case we just use -compose difference -evalute pow 2 and negate it (so inverse squared distance scaled to Q)
+ convert $tmpA1 \( -size ${ww}x${hh} xc:"gray($nmean%)" \) \
+ -compose difference -evaluate pow 2 $filter -negate $tmpD1
+
+ # apply multiply membership image by image as weighting and mask to get the resulting mean
+ # note: low mask is just the complement of the high mask, i.e. $tmpM1 -negate
+ convert $tmpA1 $tmpD1 -compose multiply -composite \
+ \( $tmpM1 -negate \) -compose multiply -composite \
+ $tmpL1
+ getMean "$tmpL1"
+ lmean=$mean
+# echo "nmean=$nmean; lmean=$lmean"
+ # correct lmean using nmean
+ lmean=`convert xc: -format "%[fx:(100*$lmean/$nmean)]" info:`
+
+ # get new threshold
+ thresh=`convert xc: -format "%[fx:($hmean+$lmean)/2]" info:`
+ diff=`convert xc: -format "%[fx:abs($thresh-$oldthresh)]" info:`
+# test=`echo "$diff <= 0.000001" | bc`
+ test=`convert xc: -format "%[fx:($diff<=0.01)?1:0]" info:`
+ k=`expr $k + 1`
+# echo "hmean=$hmean; lmean=$lmean; oldthresh=$oldthresh; thresh=$thresh; diff=$diff"
+done
+convert $tmpM1 "$outfile"
+
+echo "Image Thresholded At $thresh%"
+echo ""
+
+
+if [ "$graph" != "" ]; then
+ xx=`convert xc: -format "%[fx:floor(255*$thresh/100)]" info:`
+ convert $tmpA1 -define histogram:unique-colors=false histogram:- | \
+ convert - -negate \
+ -stroke red -strokewidth 1 -draw "line $xx,0 $xx,200" \
+ -background gray -splice 0x30 \
+ -fill white -stroke white -strokewidth 1 \
+ -font ArialB -pointsize 24 \
+ -draw "text 4,22 'threshold=$thresh%'" -resize 50% \
+ -bordercolor gray50 -border 5 \
+ $histfile
+fi
+
+if [ "$graph" = "view" ]; then
+ convert $histfile x:
+ rm -f $histfile
+fi
+
+exit 0
+
+
+
diff --git a/bin/isodatathresh b/bin/isodatathresh
new file mode 100644
index 0000000..be10c4f
--- /dev/null
+++ b/bin/isodatathresh
@@ -0,0 +1,346 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/29/2008 .......... revised 4/25/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: isodatathresh [-g graph] infile outfile
+# USAGE: isodatathresh [-help]
+#
+# OPTIONS:
+#
+# -g graph graph specifies whether to generate a
+# histogram graph image displaying the
+# location and value of the threshold;
+# choices are: view or save;
+# default is no graph
+#
+###
+#
+# NAME: ISODATATHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using the isodata technique.
+#
+# DESCRIPTION: ISODATATHRESH automatically thresholds an image to binary
+# (b/w) format. It assume the histogram is bimodal, i.e. is the composite
+# of two bell-shaped distributions representing the foreground and
+# background classes. The isodata appoach iteratively thresholds the
+# image, computes the means of the foreground (above threshold data) and
+# background (at and below threshold value), computes a new threshold
+# equal to the average of these two means and repeats until there is no
+# change in threshold between successive iterations. This script is
+# equivalent to the kmeansthresh script, but is implemented using image
+# masking rather than from the histogram of the image. It is faster
+# than the kmeansthresh script for smaller images. The timing transition
+# occurs for images somewhere between 1000-2000 pixels on a side.
+#
+# OPTIONS:
+#
+# -g graph ... GRAPH specifies whether to generate a graph (image) of
+# the histogram, displaying the location and value of the threshold.
+# The choices are: view, save and none. If graph=view is selected, the
+# graph will be created and displayed automatically, but not saved.
+# If graph=save is selected, then the graph will be created and saved
+# to a file using the infile name, with "_histog_isodata.gif" appended,
+# but the graph will not be displayed automatically. If -g option is
+# not specified, then no graph will be created.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES: see the following:
+# http://www.ph.tn.tudelft.nl/Courses/FIP/noframes/fip-Segmenta.html
+# http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MORSE/threshold.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
+graph="" #none, save or view
+
+# 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 4 ]
+ 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
+ ;;
+ -g) # get graph
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID GRAPH SPECIFICATION ---"
+ checkMinus "$1"
+ graph="$1"
+ [ "$graph" != "view" -a "$graph" != "save" ] && errMsg "--- GRAPH=$graph MUST BE EITHER VIEW OR SAVE ---"
+ ;;
+ -) # 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"
+
+# get outname from infile to use for graph
+inname=`convert $infile -format "%t" info:`
+histfile=${inname}_histog_isodata.gif
+
+tmpA1="$dir/isodatathresh_1_$$.mpc"
+tmpA2="$dir/isodatathresh_1_$$.cache"
+tmpM1="$dir/isodatathresh_M_$$.mpc"
+tmpM2="$dir/isodatathresh_M_$$.cache"
+tmpL1="$dir/isodatathresh_L_$$.mpc"
+tmpL2="$dir/isodatathresh_L_$$.cache"
+tmpH1="$dir/isodatathresh_H_$$.mpc"
+tmpH2="$dir/isodatathresh_H_$$.cache"
+trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpL1 $tmpL2 $tmpH1 $tmpH2;" 0
+trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpL1 $tmpL2 $tmpH1 $tmpH2 $histfile; exit 1" 1 2 3 15
+trap "rm -f $tmpA1 $tmpA2 $tmpM1 $tmpM2 $tmpL1 $tmpL2 $tmpH1 $tmpH2 $histfile; 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 isodatathresh.
+# with IM 6.7.4.10, 6.7.6.10, 6.7.8.10
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace Gray +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG 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`
+
+getMinMax()
+ {
+ img="$1"
+ if [ "$im_version" -ge "06030901" ]
+ then
+ min=`convert $img -format "%[min]" info:`
+ max=`convert $img -format "%[max]" info:`
+ min=`convert xc: -format "%[fx:100*$min/quantumrange]" info:`
+ max=`convert xc: -format "%[fx:100*$max/quantumrange]" info:`
+ else
+ data=`convert $img -verbose info:`
+ min=`echo "$data" | sed -n 's/^.*[Mm]in:.*[(]\([0-9.]*\).*$/\1/p ' | head -1`
+ max=`echo "$data" | sed -n 's/^.*[Mm]ax:.*[(]\([0-9.]*\).*$/\1/p ' | head -1`
+ min=`convert xc: -format "%[fx:100*$min)]" info:`
+ max=`convert xc: -format "%[fx:100*$max)]" info:`
+ fi
+ }
+
+
+getMean()
+ {
+ img="$1"
+ 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
+ }
+
+
+# process image using isodata approach
+
+echo ""
+echo "Compute Threshold"
+# begin threshold at ave of min and max
+getMinMax "$tmpA1"
+min=$min
+max=$max
+thresh=`convert xc: -format "%[fx:($min+$max)/2]" info:`
+oldthresh=0
+diff=`convert xc: -format "%[fx:abs($thresh-$oldthresh)]" info:`
+test=`echo "$diff == 0" | bc`
+k=1
+# iterate the following
+# threshold image at newmean
+# get means above and below threshold
+# get average of hmean and lmean as newmean
+# repeat until difference between oldmean and newmean is <= diffstop
+#
+# Note: low and high (below threshold and above threshold) means
+# are extracted by masking and not by histogram counting.
+# Thus they need to be corrected for mixture of black pixels.
+# So, mean from masks (Mmeasured) and actual low/high means, Mtrue, are related
+# Mmeasured=(Sum(Cw*Gw)+Sum(Cb*0))/Ct = Sum(Cw*Gw)/Ct, summed over whole image
+# where Cw=count of white mask pixels, Gw graylevel at those pixels, Ct=total image pixels
+# Mtrue=Sum(Cw*Gw)/Ctw, where Ctw is total count of only white (non-black) mask pixels
+# Ctw=Mean of the mask/100
+# All graylevels are in range 0-100
+#
+while [ $test != 1 ]; do
+ echo "Iteration $k"
+ oldthresh=$thresh
+ convert $tmpA1 -threshold ${thresh}% $tmpM1
+ getMean "$tmpM1"
+ mmean=$mean
+ convert $tmpA1 $tmpM1 -compose multiply -composite $tmpH1
+ getMean "$tmpH1"
+ hmean=$mean
+# echo "mmean=$mmean; hmean=$hmean"
+ hmean=`convert xc: -format "%[fx:(100*$hmean/$mmean)]" info:`
+ nmean=`convert xc: -format "%[fx:(100-$mmean)]" info:`
+ convert $tmpA1 \( $tmpM1 -negate \) -compose multiply -composite $tmpL1
+ getMean "$tmpL1"
+ lmean=$mean
+# echo "nmean=$nmean; lmean=$lmean"
+ lmean=`convert xc: -format "%[fx:(100*$lmean/$nmean)]" info:`
+ thresh=`convert xc: -format "%[fx:($hmean+$lmean)/2]" info:`
+ diff=`convert xc: -format "%[fx:abs($thresh-$oldthresh)]" info:`
+ test=`echo "$diff ==0" | bc`
+ k=`expr $k + 1`
+#echo "hmean=$hmean; lmean=$lmean; oldthresh=$oldthresh; thresh=$thresh; diff=$diff"
+done
+convert $tmpM1 "$outfile"
+
+echo "Image Thresholded At $thresh%"
+echo ""
+
+
+if [ "$graph" != "" ]; then
+ xx=`convert xc: -format "%[fx:floor(255*$thresh/100)]" info:`
+ convert $tmpA1 -define histogram:unique-colors=false histogram:- | \
+ convert - -negate \
+ -stroke red -strokewidth 1 -draw "line $xx,0 $xx,200" \
+ -background gray -splice 0x30 \
+ -fill white -stroke white -strokewidth 1 \
+ -font ArialB -pointsize 24 \
+ -draw "text 4,22 'threshold=$thresh%'" -resize 50% \
+ -bordercolor gray50 -border 5 \
+ "$histfile"
+fi
+
+if [ "$graph" = "view" ]; then
+ convert $histfile x:
+ rm -f "$histfile"
+fi
+
+exit 0
+
+
+
diff --git a/bin/kapurthresh b/bin/kapurthresh
new file mode 100644
index 0000000..c025ae9
--- /dev/null
+++ b/bin/kapurthresh
@@ -0,0 +1,329 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/29/2008 .......... revised 11/3/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: kapurthresh [-g graph] infile outfile
+# USAGE: kapurthresh [-help]
+#
+# OPTIONS:
+#
+# -g graph graph specifies whether to generate a
+# histogram graph image displaying the
+# location and value of the threshold;
+# choices are: view or save;
+# default is no graph
+#
+###
+#
+# NAME: KAPURTHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using Kapur's entropy technique.
+#
+# DESCRIPTION: KAPURTHRESH automatically thresholds an image to binary
+# (b/w) format. It assume the histogram is bimodal, i.e. is the composite
+# of two bell-shaped distributions representing the foreground and
+# background classes. The Kapur appoach computes computes one measure of
+# Entropy for each of the foreground (above threshold data) and background
+# (at and below threshold value) classes. The optimal threshold is the one
+# that maximizes the Sum of the Foreground and Background Entropies.
+#
+# OPTIONS:
+#
+# -g graph ... GRAPH specifies whether to generate a graph (image) of
+# the histogram, displaying the location and value of the threshold.
+# The choices are: view, save and none. If graph=view is selected, the
+# graph will be created and displayed automatically, but not saved.
+# If graph=save is selected, then the graph will be created and saved
+# to a file using the infile name, with "_histog_kapur.gif" appended,
+# but the graph will not be displayed automatically. If -g option is
+# not specified, then no graph will be created.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES: see the following:
+# http://climate.gsfc.nasa.gov/publications/fulltext/RSEpaper.pdf
+# http://www.istanbul.edu.tr/eng/ee/jeee/main/pages/issues/is62/62008.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
+graph="" #none, save or view
+
+# 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 4 ]
+ 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
+ ;;
+ -g) # get graph
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID GRAPH SPECIFICATION ---"
+ checkMinus "$1"
+ graph="$1"
+ [ "$graph" != "view" -a "$graph" != "save" ] && errMsg "--- GRAPH=$graph MUST BE EITHER VIEW OR SAVE ---"
+ ;;
+ -) # 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"
+
+# get outname from infile to use for graph
+inname=`convert $infile -format "%t" info:`
+histfile=${inname}_histog_kapur.gif
+
+tmpA1="$dir/kapurthresh_1_$$.mpc"
+tmpA2="$dir/kapurthresh_1_$$.cache"
+trap "rm -f $tmpA1 $tmpA2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2 $histfile; exit 1" 1 2 3 15
+
+# 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 kapurthresh.
+# with IM 6.6.0.10, 6.7.2.10, 6.7.3.10, 6.7.4.10, 6.7.6.10, 6.7.7.6, 6.7.7.7, 6.7.8.10
+# Note: some images (esp. b/w) do not work for older versions.
+# There seems to be a bug sometime between IM 6.7.2.10 and 6.7.3.10 through 6.7.7.7, which I cannot seem to fix
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace gray +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# get totpix in image
+width=`convert $tmpA1 -format "%w" info:`
+height=`convert $tmpA1 -format "%h" info:`
+totpix=`echo "scale=0; $width * $height / 1" | bc`
+
+
+ # get value array from IM histogram
+ valueArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk '
+ # AWK
+ { vbin[$2] += $2;}
+ END { for (i=0;i<256;i++) {print vbin[i]; } } '`)
+# echo ${valueArr[*]}
+# echo ${#valueArr[*]}
+ numvals=${#valueArr[*]}
+
+ # get count array from IM histogram
+ countArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk '
+ # AWK
+ { cbin[$2] += $1; }
+ END { for (i=0;i<256;i++) {print cbin[i]; } } '`)
+# echo ${countArr[*]}
+# echo ${#countArr[*]}
+ numcounts=${#countArr[*]}
+
+ [ $numvals -ne $numcounts ] && errMsg "--- NUMBER OF COUNTS IS NOT THE SAME AS NUMBER OF VALUES ---"
+
+ # compute normalized count array
+ ncountArr=( $(for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${countArr[$i]}"
+ done |\
+ awk -v totpix="$totpix" -v numcounts="$numcounts" '
+ # AWK
+ { bin[$1] = $2; }
+ END { for (i=0;i<numcounts;i++) {print bin[i]/totpix; } } ') )
+# echo ${ncountArr[*]}
+# echo ${#ncountArr[*]}
+
+ # compute elowArr
+ elowArr=( $(for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk -v numcounts="$numcounts" '
+ # AWK to generate a cumulative histogram 1D image...
+ { ncbin[$1] = $2; nlow += $2; nlowbin[$1] = nlow; qlow += $2*log($2); qlowbin[$1] = qlow;}
+ END { for (i=0;i<numcounts;i++) {if (ncbin[i]!=0) {elowbin[i]=log(nlowbin[i]) - qlowbin[i]/nlowbin[i]} else {elowbin[i]=0}; print elowbin[i] } } ') )
+# echo ${elowArr[*]}
+# echo ${#elowArr[*]}
+
+ # compute ehighArr
+ ehighArr=( $(for ((i=0; i<$numcounts; i++)); do
+ j=`expr $numcounts - 1 - $i`
+ echo "$j ${ncountArr[$j]}"
+ done |\
+ awk -v numcounts="$numcounts" '
+ # AWK to generate a cumulative histogram 1D image...
+ { ncbin[$1] = $2; nhigh += $2; nhighbin[$1] = nhigh; qhigh += $2*log($2); qhighbin[$1] = qhigh;}
+ END { for (i=0;i<numcounts;i++) {if (ncbin[i]!=0) {ehighbin[i]=log(nhighbin[i]) - qhighbin[i]/nhighbin[i]} else {ehighbin[i]=0}; print ehighbin[i] } } ') )
+# echo ${ehighArr[*]}
+# echo ${#ehighArr[*]}
+
+ # compute threshold
+ threshbin=$(for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${elowArr[$i]} ${ehighArr[$i]}"
+ done |\
+ awk -v numcounts="$numcounts" -v teold=0 -v threshbin=0 '
+ # AWK to compute entropy threshold...
+ { tebin[$1] = ($2 + $3); }
+ END { for (i=0;i<numcounts;i++) { if (tebin[i]>teold) {teold=tebin[i]; threshbin=i}; } print threshbin } ')
+# echo "threshbin=$threshbin"
+ thresh=${valueArr[$threshbin]}
+# echo "thresh=$thresh"
+ threshpct=`convert xc: -format "%[fx:100*$thresh/255]" info:`
+
+
+
+# compute threshold graph x coord and threshold in percent
+xx=$thresh
+threshpct=`convert xc: -format "%[fx:100*$thresh/255]" info:`
+#echo "xx=$xx; threshpct=$threshpct"
+
+
+echo "Thresholding Image At $threshpct%"
+convert $tmpA1 -threshold $threshpct% "$outfile"
+echo ""
+
+
+if [ "$graph" != "" ]; then
+ convert $tmpA1 -define histogram:unique-colors=false histogram:- | \
+ convert - -negate \
+ -stroke red -strokewidth 1 -draw "line $xx,0 $xx,200" \
+ -background gray -splice 0x30 \
+ -fill white -stroke white -strokewidth 1 \
+ -font ArialB -pointsize 24 \
+ -draw "text 4,22 'threshold=$threshpct%'" -resize 50% \
+ -bordercolor gray50 -border 5 \
+ "$histfile"
+fi
+
+if [ "$graph" = "view" ]; then
+ convert "$histfile" x:
+ rm -f "$histfile"
+fi
+
+exit 0
+
+
+
diff --git a/bin/kmeansthresh b/bin/kmeansthresh
new file mode 100644
index 0000000..cde8cf8
--- /dev/null
+++ b/bin/kmeansthresh
@@ -0,0 +1,428 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/29/2008 .......... revised 3/19/2016
+#
+# ------------------------------------------------------------------------------
+#
+# 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: kmeansthresh [-g graph] infile outfile
+# USAGE: kmeansthresh [-help]
+#
+# OPTIONS:
+#
+# -g graph graph specifies whether to generate a
+# histogram graph image displaying the
+# location and value of the threshold;
+# choices are: view or save;
+# default is no graph
+#
+###
+#
+# NAME: KMEANSTHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using the k-means technique.
+#
+# DESCRIPTION: KMEANSTHRESH automatically thresholds an image to binary
+# (b/w) format. It assume the histogram is bimodal, i.e. is the composite
+# of two bell-shaped distributions representing the foreground and
+# background classes. The k-means appoach iteratively thresholds the
+# image, computes the means of the foreground (above threshold data) and
+# background (at and below threshold value), computes a new threshold
+# equal to the average of these two means and repeats until there is no
+# change in threshold between successive iterations. This script is
+# equivalent to the isodatathresh script, which is implemented using image
+# masking rather than from the histogram of the image. The isodatathresh
+# script is faster than the kmeansthresh script for smaller images. The
+# timing transition occurs for images somewhere between 1000-2000 pixels
+# on a side. The kmeansthresh script is moderatly insensitive to image
+# size, but pays an initial timing penalty in order to compute the
+# histogram related data and get the means for each class from the
+# histogram using shell computations.
+#
+# OPTIONS:
+#
+# -g graph ... GRAPH specifies whether to generate a graph (image) of
+# the histogram, displaying the location and value of the threshold.
+# The choices are: view, save and none. If graph=view is selected, the
+# graph will be created and displayed automatically, but not saved.
+# If graph=save is selected, then the graph will be created and saved
+# to a file using the infile name, with "_histog_kmeans.gif" appended,
+# but the graph will not be displayed automatically. If -g option is
+# not specified, then no graph will be created.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES: see the following:
+# http://www.ph.tn.tudelft.nl/Courses/FIP/noframes/fip-Segmenta.html
+# http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MORSE/threshold.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
+graph="" #none, save or view
+
+# 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 4 ]
+ 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
+ ;;
+ -g) # get graph
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID GRAPH SPECIFICATION ---"
+ checkMinus "$1"
+ graph="$1"
+ [ "$graph" != "view" -a "$graph" != "save" ] && errMsg "--- GRAPH=$graph MUST BE EITHER VIEW OR SAVE ---"
+ ;;
+ -) # 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"
+
+# get outname from infile to use for graph
+inname=`convert $infile -format "%t" info:`
+histfile=${inname}_histog_kmeans.gif
+
+tmpA1="$dir/kmeansthresh_1_$$.mpc"
+tmpA2="$dir/kmeansthresh_1_$$.cache"
+trap "rm -f $tmpA1 $tmpA2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2 $histfile; exit 1" 1 2 3 15
+
+# 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 kmeansthresh.
+# with IM 6.7.4.10, 6.7.6.10, 6.7.8.10
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace gray +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# get totpix in image
+width=`convert $tmpA1 -format "%w" info:`
+height=`convert $tmpA1 -format "%h" info:`
+totpix=`echo "scale=0; $width * $height / 1" | bc`
+
+# get min and max in range 0 - 255 to correspond to 8-bit histogram and later set average to initial thresh
+if [ "$im_version" -ge "06030901" ]
+ then
+ min=`convert $tmpA1 -format "%[min]" info:`
+ max=`convert $tmpA1 -format "%[max]" info:`
+ min=`convert xc: -format "%[fx:255*$min/quantumrange]" info:`
+ max=`convert xc: -format "%[fx:255*$max/quantumrange]" info:`
+else
+ data=`convert $tmpA1 -verbose info:`
+ min=`echo "$data" | sed -n 's/^.*[Mm]in:.*[(]\([0-9.]*\).*$/\1/p ' | head -1`
+ max=`echo "$data" | sed -n 's/^.*[Mm]ax:.*[(]\([0-9.]*\).*$/\1/p ' | head -1`
+ min=`convert xc: -format "%[fx:255*$min)]" info:`
+ max=`convert xc: -format "%[fx:255*$max)]" info:`
+fi
+#echo "min=$min; max=$max;"
+
+
+# separate IM histogram into two arrays, value and count, and fill out for empty bins and normalize
+
+
+ # get filled value array from IM histogram
+ nvalueArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk '
+ # AWK
+ { vbin[$2] += $2;}
+ END { for (i=0;i<256;i++) {print vbin[i]+0; } } '`)
+# echo ${nvalueArr[*]}
+# echo ${#nvalueArr[*]}
+ numvals=${#nvalueArr[*]}
+
+
+ # get filled and normalized count array from IM histogram
+ ncountArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk -v totpix="$totpix" '
+ # AWK
+ { cbin[$2] += $1; }
+ END { for (i=0;i<256;i++) {printf ("%f\n", (cbin[i]+0)/totpix) } } '`)
+# echo ${ncountArr[*]}
+# echo ${#ncountArr[*]}
+ numcounts=${#ncountArr[*]}
+
+ [ $numvals -ne $numcounts ] && errMsg "--- NUMBER OF COUNTS IS NOT THE SAME AS NUMBER OF VALUES ---"
+
+ numbins=256
+
+# process image using k-means approach
+
+# Generate Cumulative Arrays
+# p=c(i)/N (normalized count or probability, p, at bin i)
+# v=v(i) (graylevel at bin i)
+# note that as histogram has been filled that v=i
+# t=threshold bin
+# n=p0=sum(c(i)/N)=zeroth histogram moment => cumulative normalized count (from i=0 to t) = N(t)
+# g=p1=sum(c(i)*v(i))=first histogram momement => cumulative normalized graylevel (from i=0 to t) = G(t)
+# m=p1/p0=mean
+
+
+ # compute nlowArr
+ nlowArr=( $(for ((i=0; i<numbins; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk -v numbins="$numbins" '
+ # AWK to generate a cumulative histogram 1D image...
+ { nlowbin[$1] = $2; }
+ END { for (i=0;i<numbins;i++) { nlow += nlowbin[i]; print nlow } } ') )
+# echo ${nlowArr[*]}
+# echo ${#nlowArr[*]}
+
+
+ # compute glowArr
+ glowArr=( $( for ((i=0; i<numbins; i++)); do
+ echo "$i ${nvalueArr[$i]} ${ncountArr[$i]}"
+ done |\
+ awk -v numbins="$numbins" '
+ # AWK to generate a cumulative histogram 1D image...
+ { vlowbin[$1] = $2; nclowbin[$1] = $3; }
+ END { for (i=0;i<numbins;i++) { glow += vlowbin[i]*nclowbin[i]; print glow } } ') )
+# echo ${glowArr[*]}
+# echo ${#glowArr[*]}
+
+
+ # compute mlowArr
+ [ `echo "${ncountArr[0]} == 0" | bc` -eq 1 ] && mlowold=0
+ mlowArr=( $( for ((i=0; i<numbins; i++)); do
+ echo "$i ${nlowArr[$i]} ${glowArr[$i]}"
+ done |\
+ awk -v numbins="$numbins" -v mlowold="$mlowold" '
+ # AWK to generate a cumulative histogram 1D image...
+ { nlowbin[$1] = $2; glowbin[$1] = $3; }
+ END { for (i=0;i<numbins;i++)
+ { if ( nlowbin[i] != 0 ) { mlow = glowbin[i]/nlowbin[i]; mlowold = mlow; } else { mlow = mlowold; } print mlow } }') )
+# echo ${mlowArr[*]}
+# echo ${mlowArr[*]}
+
+
+ # compute nhighArr
+ nhighArr=( $(for ((i=0; i<numbins; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk -v numbins="$numbins" '
+ # AWK to generate a cumulative histogram 1D image...
+ { nhighbin[$1] = $2; }
+ END { for (i=0;i<numbins;i++) { j = (numbins - 1 - i); nhigh += nhighbin[j]; print nhigh } } ') )
+# echo ${nhighArr[*]}
+# echo ${#nhighArr[*]}
+
+
+ # compute ghighArr
+ ghighArr=( $( for ((i=0; i<numbins; i++)); do
+ echo "$i ${nvalueArr[$i]} ${ncountArr[$i]}"
+ done |\
+ awk -v numbins="$numbins" '
+ # AWK to generate a cumulative histogram 1D image...
+ { vhighbin[$1] = $2; nchighbin[$1] = $3; }
+ END { for (i=0;i<numbins;i++) { j = (numbins - 1 - i); ghigh += vhighbin[j]*nchighbin[j]; print ghigh } } ') )
+# echo ${glowArr[*]}
+# echo ${#glowArr[*]}
+
+
+ # compute mhighArr
+ [ `echo "${ncountArr[0]} == 0" | bc` -eq 1 ] && mhighold=0
+ mhighArr=( $( for ((i=0; i<numbins; i++)); do
+ echo "$i ${nhighArr[$i]} ${ghighArr[$i]}"
+ done |\
+ awk -v numbins="$numbins" -v mhighold="$mhighold" '
+ # AWK to generate a cumulative histogram 1D image...
+ { nhighbin[$1] = $2; ghighbin[$1] = $3; }
+ END { for (i=0;i<numbins;i++)
+ { if ( nhighbin[i] != 0 ) { mhigh = ghighbin[i]/nhighbin[i]; mhighold = mhigh; } else { mhigh = mhighold; } print mhigh } }') )
+# echo ${mlowArr[*]}
+# echo ${mlowArr[*]}
+
+
+#for ((i=0;i<numbins;i++)); do
+#echo "i=$i; j=$j; ncount=${ncountArr[$i]}; nlow=${nlowArr[$i]}; nhigh=${nhighArr[$i]}; mlow=${mlowArr[$i]}; mhigh=${mhighArr[$i]}"
+#done
+
+# Compute Threshold
+# iterate the following
+# start with initial threshold at average of image min and max values
+# get low and high means corresponding to below and above threshold
+# compute new threshold=(Ml+Mh)/2
+# repeat until difference between old and new thresholds is zero
+
+lastbin=`expr $numbins - 1`
+thresh=`echo "scale=0; ($min + $max)/2" | bc`
+oldthresh=0
+diff=`echo "scale=10; ($thresh - $oldthresh)" | bc`
+diff=`echo "scale=10; sqrt($diff * $diff)" | bc`
+test=`echo "$diff == 0" | bc`
+while [ $test != 1 ]; do
+ i=$thresh
+ j=`expr $lastbin - $i - 1`
+ oldthresh=$thresh
+ mlow=${mlowArr[$i]}
+ mhigh=${mhighArr[$j]}
+ thresh=`echo "scale=0; ($mlow+$mhigh)/2" | bc`
+ diff=`echo "scale=10; ($thresh - $oldthresh)" | bc`
+ diff=`echo "scale=10; sqrt($diff * $diff)" | bc`
+ test=`echo "$diff == 0" | bc`
+#echo "i=$i; j=$j; mlow=$mlow; mhigh=$mhigh; oldthresh=$oldthresh; thresh=$thresh; diff=$diff; test=$test"
+done
+
+
+# compute threshold graph x coord and threshold in percent
+xx=$thresh
+threshpct=`convert xc: -format "%[fx:100*$thresh/255]" info:`
+#echo "xx=$xx; threshpct=$threshpct"
+
+
+echo "Thresholding Image At $threshpct%"
+convert $tmpA1 -threshold $threshpct% "$outfile"
+echo ""
+
+
+if [ "$graph" != "" ]; then
+ convert $tmpA1 -define histogram:unique-colors=false histogram:- | \
+ convert - -negate \
+ -stroke red -strokewidth 1 -draw "line $xx,0 $xx,200" \
+ -background gray -splice 0x30 \
+ -fill white -stroke white -strokewidth 1 \
+ -font ArialB -pointsize 24 \
+ -draw "text 4,22 'threshold=$threshpct%'" -resize 50% \
+ -bordercolor gray50 -border 5 \
+ "$histfile"
+fi
+
+if [ "$graph" = "view" ]; then
+ convert "$histfile" x:
+ rm -f "$histfile"
+fi
+
+exit 0
+
+
+
diff --git a/bin/localthresh b/bin/localthresh
new file mode 100644
index 0000000..03d6f06
--- /dev/null
+++ b/bin/localthresh
@@ -0,0 +1,347 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/30/2008 .......... revised 11/3/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: localthresh [-m method] [-s size] [-b bias] [-n negate] infile outfile
+# USAGE: localthresh [-help]
+#
+# OPTIONS:
+#
+# -m method method for computing threshold statistics;
+# method=1 uses only the local mean;
+# method=2 uses the local mean and standard deviation;
+# method=3 used the local mean and mean absolute deviation;
+# default=1
+# -r radius radius of window to use; float; radius>=3; default=15
+# -b bias bias parameter (in percent) for thresholding;
+# float; bias>=0; default=20
+# -n negate negate indicates whether to negate the image
+# before and after processing; negate=yes or no;
+# default=no
+#
+###
+#
+# NAME: LOCALTHRESH
+#
+# PURPOSE: To threshold an image to binary (b/w) format using a moving
+# window adaptive thresholding approach.
+#
+# DESCRIPTION: LOCALTHRESH thresholds an image to binary (b/w) format
+# using a moving window adaptive thresholding approach. For each window
+# placement the center pixel is compared to some measure of either mean or
+# combination of mean and either standard deviation or mean absolute
+# deviation within the window. If the center pixel is larger than this
+# measure by some bias value, then the center pixel is made white;
+# otherwise it is made black. The moving window is a circle with Gaussian
+# profile. NOTE: the image MUST have the "objects" or foreground as white
+# and the "non-objects" or background as black. Thus the image must either
+# be preprocessed using the IM function -negate or have the script do that
+# using its negate parameter. IMPORTANT: For acceptable results, the
+# window size generally should be larger than the dimension of the
+# "objects" to be detected in the image by the thresholding. Consequently,
+# this method is best applied to images of text, small objects or edges.
+#
+# OPTIONS:
+#
+# -m method ... METHOD specifies what statistical measure to use to
+# compute the threshold for each window placement. Method 1 compares
+# the center pixel to the window mean, and if larger than the bias,
+# the center is made white; otherwise black. Method 2 compares the
+# center pixel to the window mean plus the bias times the window
+# standard deviation, and if the center pixels is larger, it is made
+# white; otherwise black. Method 3 compares the center pixel to the
+# window mean plus the bias times the square root of window mean
+# absolute deviation and if the center pixel is larger, it is made white;
+# otherwise black. The default is method=1. Note that method=1 is similar
+# to the IM function -lat (but uses a circular gaussian weighted window
+# rather than a square uniform weighted window and more importantly
+# does not suffer from the image shift resulting with the IM -lat
+# function).
+#
+# -r radius ... RADIUS specifies the radius for the gaussian profile
+# window. The value may be a float, but must be greater than or equal
+# to 3. The default=15. For acceptable results, the radius generally
+# must be larger than the feature dimension that is to be detected by
+# the thresholding.
+#
+# -b bias ... BIAS is the bias parameter used in each of the two
+# methods to determine the threshold. Larger bias values will have
+# the effect of removing more "noise" from the result, but too large
+# a value may remove parts of the foreground objects that are detected.
+# Values for bias are expressed as (percent) floats where bias>=0.
+# The default=20.
+#
+# -n negate ... NEGATE indicates whether to negate the image before
+# and after processing, since this technique only works when the "object"
+# or foreground is white and the "non-object" or background is black.
+# Values may be either yes or no. The default=yes.
+#
+# REFERENCES: see the following:
+# http://www.dfki.uni-kl.de/~shafait/papers/Shafait-efficient-binarization-SPIE08.pdf
+# http://www.busim.ee.boun.edu.tr/~sankur/SankurFolder/Threshold_survey.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
+method=1 #1=mean, 2=mean and std, 3=mean and sqrt(mad)
+radius=15 #radius>=3
+bias=20 #percent bias>=0
+negate="no" #yes or no
+
+# 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 10 ]
+ 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
+ ;;
+ -m) # get method
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID METHOD SPECIFICATION ---"
+ checkMinus "$1"
+ method=`expr "$1" : '\([0-9]*\)'`
+ [ "$method" = "" ] && errMsg "--- METHOD=$method MUST BE A NON-NEGATIVE INTEGER ---"
+ [ $method -lt 1 -o $method -gt 3 ] && errMsg "--- METHOD=$method MUST BE EITHER 1 OR 2 ---"
+ ;;
+ -r) # get radius
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID RADIUS SPECIFICATION ---"
+ checkMinus "$1"
+ radius=`expr "$1" : '\([.0-9]*\)'`
+ [ "$radius" = "" ] && errMsg "--- RADIUS=$radius MUST BE A NON-NEGATIVE FLOAT ---"
+ radiustest=`echo "$radius < 3" | bc`
+ [ $radiustest -eq 1 ] && errMsg "--- RADIUS=$radius MUST BE A FLOAT GREATER THAN OR EQUAL TO 3 ---"
+ ;;
+ -b) # get bias
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID BIAS SPECIFICATION ---"
+ checkMinus "$1"
+ bias=`expr "$1" : '\([.0-9]*\)'`
+ [ "$bias" = "" ] && errMsg "--- BIAS=$bias MUST BE A NON-NEGATIVE FLOAT ---"
+ ;;
+ -n) # get negate
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID NEGATE SPECIFICATION ---"
+ checkMinus "$1"
+ negate="$1"
+ negate=`echo "$negate" | tr '[A-Z]' '[a-z]'`
+ case "$negate" in
+ yes|y) negate="yes";;
+ no|n) negate="no";;
+ *) errMsg "--- NEGATE=$negate MUST BE EITHER YES OR NO ---";;
+ esac
+ ;;
+ -) # 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/autothresh1_A_$$.mpc"
+tmpA2="$dir/autothresh1_A_$$.cache"
+tmpT1="$dir/autothresh1_T_$$.mpc"
+tmpT2="$dir/autothresh1_T_$$.cache"
+tmpM1="$dir/autothresh1_M_$$.mpc"
+tmpM2="$dir/autothresh1_M_$$.cache"
+tmpS1="$dir/autothresh1_S_$$.mpc"
+tmpS2="$dir/autothresh1_S_$$.cache"
+trap "rm -f $tmpA1 $tmpA2 $tmpT1 $tmpT2 $tmpM1 $tmpM2 $tmpS1 $tmpS2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2 $tmpT1 $tmpT2 $tmpM1 $tmpM2 $tmpS1 $tmpS2; exit 1" 1 2 3 15
+
+# 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 localthresh.
+# with IM 6.7.4.10, 6.7.6.10, 6.7.8.10
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace gray -alpha off +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# setup window filters
+ radius=`convert xc: -format "%[fx:$radius/3]" info:`
+ size="0x${radius}"
+
+
+# process image
+[ "$negate" = "yes" ] && convert $tmpA1 -negate $tmpA1
+
+# get mean image
+convert $tmpA1 -blur "$size" $tmpM1
+
+# NOTE: to form (image1-image2),
+# use convert image2 image1 -compose minus -composite
+# or convert image1 image2 +swap -compose minus -composite
+
+# get sos=second order statistic = either std or sqrt(mad)
+if [ $method -eq 2 ]; then
+ # get standard deviation=std image; std = sqrt( ave(x^2) - ave(x)^2 );
+ # ave=mean; -gamma 2 is equivalent to sqrt
+ convert \( $tmpA1 $tmpA1 -compose multiply -composite -blur "$size" \) \
+ \( $tmpM1 $tmpM1 -compose multiply -composite \) +swap \
+ -compose minus -composite -gamma 2 $tmpS1
+
+elif [ $method -eq 3 ]; then
+ # get sqrt(mean absolute deviation)=sqrt(mad) image;
+ # mad=absolute difference of (image - mean)
+ convert $tmpA1 $tmpM1 -compose difference -composite -gamma 2 $tmpS1
+fi
+
+
+if [ $method -eq 1 ]; then
+ # threshold to white if image > (mean + K) or
+ # image - (mean + K) > 0 or
+ # (image - mean) - K > 0 or
+ # (image - mean) > K
+ # where K is percent
+ #convert $tmpA1 \( $tmpM1 -evaluate add ${bias}% \) +swap -compose minus -composite -threshold 1 $tmpT1
+ #convert $tmpA1 $tmpM1 +swap -compose minus -composite -evaluate subtract ${bias}% -threshold 1 $tmpT1
+
+ convert $tmpA1 $tmpM1 +swap -compose minus -composite -threshold ${bias}% $tmpT1
+
+else
+ # threshold to white if image > (mean +k*sos) or
+ # image - (mean +k*sos) > 0 or
+ # (image - mean) - k*sos > 0
+ # where k is fraction, k=K/100 and K is percent
+
+ bias=`convert xc: -format "%[fx:$bias/100]" info:`
+ convert $tmpA1 $tmpM1 +swap -compose minus -composite \
+ \( $tmpS1 -evaluate multiply ${bias} \) \
+ +swap -compose minus -composite \
+ -threshold 1 \
+ $tmpT1
+fi
+
+if [ "$negate" = "yes" ]; then
+ convert $tmpT1 -negate "$outfile"
+else
+ convert $tmpT1 "$outfile"
+fi
+
+exit 0
diff --git a/bin/otsuthresh b/bin/otsuthresh
new file mode 100644
index 0000000..2889230
--- /dev/null
+++ b/bin/otsuthresh
@@ -0,0 +1,368 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/29/2008 .......... revised 11/3/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: otsuthresh [-g graph] infile outfile
+# USAGE: otsuthresh [-help]
+#
+# OPTIONS:
+#
+# -g graph graph specifies whether to generate a
+# histogram graph image displaying the
+# location and value of the threshold;
+# choices are: view or save;
+# default is no graph
+#
+###
+#
+# NAME: OTSUTHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using Otsu's between class variance technique.
+#
+# DESCRIPTION: OTSUTHRESH automatically thresholds an image to binary
+# (b/w) format. It assume the histogram is bimodal, i.e. is the composite
+# of two bell-shaped distributions representing the foreground and
+# background classes. The Otsu appoach computes the Between Class Variance
+# from the foreground (above threshold data) and background (at and below
+# threshold value) for every possible threshold value. The optimal threshold
+# is the one that maximizes the Between Class Variance. This is equivalent
+# to finding the threshold that minimizes the overlap between the two
+# bell-shaped class curves.
+#
+# OPTIONS:
+#
+# -g graph ... GRAPH specifies whether to generate a graph (image) of
+# the histogram, displaying the location and value of the threshold.
+# The choices are: view, save and none. If graph=view is selected, the
+# graph will be created and displayed automatically, but not saved.
+# If graph=save is selected, then the graph will be created and saved
+# to a file using the infile name, with "_histog_otsu.gif" appended,
+# but the graph will not be displayed automatically. If -g option is
+# not specified, then no graph will be created.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES: see the following:
+# http://www.ph.tn.tudelft.nl/Courses/FIP/noframes/fip-Segmenta.html
+# http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MORSE/threshold.pdf
+# http://www.cse.unr.edu/~bebis/CS791E/Notes/Thresholding.pdf
+# http://www.ifi.uio.no/in384/info/threshold.ps
+# http://www.supelec.fr/ecole/radio/JPPS01.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
+graph="" #none, save or view
+
+# 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 4 ]
+ 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
+ ;;
+ -g) # get graph
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID GRAPH SPECIFICATION ---"
+ checkMinus "$1"
+ graph="$1"
+ [ "$graph" != "view" -a "$graph" != "save" ] && errMsg "--- GRAPH=$graph MUST BE EITHER VIEW OR SAVE ---"
+ ;;
+ -) # 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"
+
+# get outname from infile to use for graph
+inname=`convert $infile -format "%t" info:`
+histfile=${inname}_histog_otsu.gif
+
+tmpA1="$dir/otsuthresh_1_$$.mpc"
+tmpA2="$dir/otsuthresh_1_$$.cache"
+trap "rm -f $tmpA1 $tmpA2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2 $histfile; exit 1" 1 2 3 15
+
+# 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 otsuthresh.
+# with IM 6.7.4.10, 6.7.6.10, 6.7.8.10
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace gray +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# get totpix in image
+width=`convert $tmpA1 -format "%w" info:`
+height=`convert $tmpA1 -format "%h" info:`
+totpix=`echo "scale=0; $width * $height / 1" | bc`
+
+ # get value array from IM histogram
+ valueArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk '
+ # AWK
+ { vbin[$2] += $2;}
+ END { for (i=0;i<256;i++) {print vbin[i]; } } '`)
+# echo ${valueArr[*]}
+# echo ${#valueArr[*]}
+ numvals=${#valueArr[*]}
+
+ # get count array from IM histogram
+ countArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk '
+ # AWK
+ { cbin[$2] += $1; }
+ END { for (i=0;i<256;i++) {print cbin[i]; } } '`)
+# echo ${countArr[*]}
+# echo ${#countArr[*]}
+ numcounts=${#countArr[*]}
+
+ [ $numvals -ne $numcounts ] && errMsg "--- NUMBER OF COUNTS IS NOT THE SAME AS NUMBER OF VALUES ---"
+
+ # compute normalized count array
+ ncountArr=( $(for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${countArr[$i]}"
+ done |\
+ awk -v totpix="$totpix" -v numcounts="$numcounts" '
+ # AWK
+ { bin[$1] = $2; }
+ END { for (i=0;i<numcounts;i++) {print bin[i]/totpix; } } ') )
+# echo ${ncountArr[*]}
+# echo ${#ncountArr[*]}
+
+ # get global mean from values and normalized counts
+ mean=$( for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${valueArr[$i]} ${ncountArr[$i]}"
+ done |\
+ awk -v numcounts="$numcounts" '
+ # AWK
+ { vbin[$1] = $2; ncbin[$1] = $3; }
+ END { for (i=0;i<numcounts;i++) { mean += vbin[i]*ncbin[i]; } print mean } ' )
+# echo "mean=$mean"
+
+
+#Generate Cumulative Arrays
+# p=c(i)/N (normalized count or probability, p, at bin i)
+# v=v(i) (graylevel at bin i)
+# t=threshold bin
+# n=p0=sum(c(i)/N)zeroth histogram moment => cumulative normalized count (from i=0 to t) = N(t)
+# g=p1=sum(c(i)*v(i))=first histogram momement => cumulative normalized graylevel (from i=0 to t) = G(t)
+
+
+ # compute nlowArr
+ nlowArr=( $(for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk -v numcounts="$numcounts" '
+ # AWK to generate a cumulative histogram 1D image...
+ { nlowbin[$1] = $2; }
+ END { for (i=0;i<numcounts;i++) { nlow += nlowbin[i]; print nlow } } ') )
+# echo ${nlowArr[*]}
+# echo ${#nlowArr[*]}
+
+ # compute glowArr
+ glowArr=( $( for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${valueArr[$i]} ${ncountArr[$i]}"
+ done |\
+ awk -v numcounts="$numcounts" '
+ # AWK to generate a cumulative histogram 1D image...
+ { vlowbin[$1] = $2; nclowbin[$1] = $3; }
+ END { for (i=0;i<numcounts;i++) { glow += vlowbin[i]*nclowbin[i]; print glow } } ') )
+# echo ${glowArr[*]}
+# echo ${#glowArr[*]}
+
+
+# Compute Threshold
+# loop through histogram using normalized counts and values
+# compute threshold by maximizing between class variance
+# bcv=Nl*(Ml-M)^2 + Nh*(Mh-M)^2 = (M*Nl-Gl)^2/(Nl*(1-Nl))
+
+# m=p1/p0=mean => M(t)=G(t)/N(t)
+# where Nh and Nl are normalized counts above and below threshold (zeroth moments, p0)
+# Mh and Ml are means of pixels above and below threshold
+# Gh and Gl are normalized cumulative graylevels (first moments, p1)
+# ML=Gl/Nl
+# above derived using
+# Nh=(1-Nl) for normalized histogram
+# Mh=(M-Ml*Nl)/(1-Nl) where M is overall mean of image
+
+
+ # compute threshold
+ #note: must stop at second to last bin so that nlow != 1 exactly or get divide by zero
+ threshbin=$(for ((i=0; i<$numcounts; i++)); do
+ echo "$i ${nlowArr[$i]} ${glowArr[$i]}"
+ done |\
+ awk -v numcounts="$numcounts" -v mean="$mean" -v bcvold=0 -v threshbin=0 '
+ # AWK to compute entropy threshold...
+ { nlowbin[$1] = $2; glowbin[$1] = $3; }
+ END { for (i=0;i<(numcounts-1);i++)
+ { dmean = (mean*nlowbin[i] - glowbin[i]); bcv = dmean*dmean/(nlowbin[i]*(1-nlowbin[i]));
+ if (bcv>bcvold) { bcvold=bcv; threshbin=i; } } print threshbin } ')
+# echo "threshbin=$threshbin"
+ thresh=${valueArr[$threshbin]}
+# echo "thresh=$thresh"
+ threshpct=`convert xc: -format "%[fx:100*$thresh/255]" info:`
+
+
+
+# compute threshold graph x coord and threshold in percent
+xx=$thresh
+threshpct=`convert xc: -format "%[fx:100*$thresh/255]" info:`
+#echo "xx=$xx; threshpct=$threshpct"
+
+
+echo "Thresholding Image At $threshpct%"
+convert $tmpA1 -threshold $threshpct% "$outfile"
+echo ""
+
+
+if [ "$graph" != "" ]; then
+ convert $tmpA1 -define histogram:unique-colors=false histogram:- | \
+ convert - -negate \
+ -stroke red -strokewidth 1 -draw "line $xx,0 $xx,200" \
+ -background gray -splice 0x30 \
+ -fill white -stroke white -strokewidth 1 \
+ -font ArialB -pointsize 24 \
+ -draw "text 4,22 'threshold=$threshpct%'" -resize 50% \
+ -bordercolor gray50 -border 5 \
+ "$histfile"
+fi
+
+if [ "$graph" = "view" ]; then
+ convert "$histfile" x:
+ rm -f "$histfile"
+fi
+
+exit 0
+
+
+
diff --git a/bin/ptilethresh b/bin/ptilethresh
new file mode 100644
index 0000000..9838b95
--- /dev/null
+++ b/bin/ptilethresh
@@ -0,0 +1,213 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 8/30/2012 .......... revised 11/3/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: ptilethresh [-p percentile] infile outfile
+# USAGE: ptilethresh [-h or -help]
+#
+# OPTIONS:
+#
+# -p percentile percentile on cumulative histogram for desired threshold;
+# 0<=float<=100; default=50
+#
+###
+#
+# NAME: PTILETHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# at a specified percentile on the cumulative histogram.
+#
+# DESCRIPTION: PTILETHRESH automatically thresholds an image to binary
+# (b/w) format at a specified percentile on the cumulative histogram. When
+# the percentile is 50%, this technique is also known as balanced thresholding.
+#
+# OPTIONS:
+#
+# -p percentile ... PERCENTILE is the desired threshold point on the cumulative
+# histogram. Values are floats between 0 and 100. The default=50.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES:
+# http://en.wikipedia.org/wiki/Balanced_histogram_thresholding
+# http://www.pvv.org/~perchrh/papers/datasyn/paper2/report.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
+percentile=50
+
+
+# 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 4 ]
+ 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
+ ;;
+ -p) # get percentile
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID PERCENTILE SPECIFICATION ---"
+ checkMinus "$1"
+ percentile=`expr "$1" : '\([.0-9]*\)'`
+ [ "$percentile" = "" ] && errMsg "--- PERCENTILE=$percentile MUST BE A NON-NEGATIVE FLOAT ---"
+ testA=`echo "$percentile < 0" | bc`
+ testB=`echo "$percentile > 100" | bc`
+ [ $testA -eq 1 -o $testB -eq 1 ] && errMsg "--- PERCENTILE=$percentile MUST BE A FLOAT BETWEEN 0 AND 100 ---"
+ ;;
+ -) # 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/ptilethresh_1_$$.mpc"
+tmpA2="$dir/ptilethresh_1_$$.cache"
+trap "rm -f $tmpA1 $tmpA2 exit 0;" 0
+trap "rm -f $tmpA1 $tmpA2; exit 1" 1 2 3 15
+
+# 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 ptilethresh.
+# with IM 6.7.4.10, 6.7.6.10, 6.7.9.10
+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
+
+
+if convert -quiet "$infile" +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+bpt=$percentile
+wpt=`convert xc: -format "%[fx:100-$bpt]" info:`
+# process image
+convert $tmpA1 $setcspace -colorspace gray -linear-stretch $bpt%,$wpt% "$outfile"
+
+exit 0
+
+
+
diff --git a/bin/sahoothresh b/bin/sahoothresh
new file mode 100644
index 0000000..3f89ab6
--- /dev/null
+++ b/bin/sahoothresh
@@ -0,0 +1,385 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/29/2008 .......... revised11/3/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: sahoothresh [-p power] [-g graph] infile outfile
+# USAGE: sahoothresh [-help]
+#
+# OPTIONS:
+#
+# -p power power or exponent to use with method=4
+# float; power>0; default=2
+# -g graph graph specifies whether to generate a
+# histogram graph image displaying the
+# location and value of the threshold;
+# choices are: view or save;
+# default is no graph
+#
+###
+#
+# NAME: SAHOOTHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using Sahoo's technique.
+#
+# DESCRIPTION: SAHOOTHRESH automatically thresholds an image to binary
+# (b/w) format. It assume the histogram is bimodal, i.e. is the composite
+# of two bell-shaped distributions representing the foreground and
+# background classes. The Sahoo appoach computes computes one measure of
+# Entropy for each of the foreground (above threshold data) and background
+# (at and below threshold value) classes. The optimal threshold is the one
+# that maximizes the Sum of the Foreground and Background Entropies.
+#
+# OPTIONS:
+#
+# -p power ... POWER is the exponent used in method 4. The value for
+# power may be a float greater than zero. The default is 2.
+#
+# -g graph ... GRAPH specifies whether to generate a graph (image) of
+# the histogram, displaying the location and value of the threshold.
+# The choices are: view, save and none. If graph=view is selected, the
+# graph will be created and displayed automatically, but not saved.
+# If graph=save is selected, then the graph will be created and saved
+# to a file using the infile name, with "_histog_sahoo.gif" appended,
+# but the graph will not be displayed automatically. If -g option is
+# not specified, then no graph will be created.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES: see the following:
+# http://climate.gsfc.nasa.gov/publications/fulltext/RSEpaper.pdf
+# http://www.istanbul.edu.tr/eng/ee/jeee/main/pages/issues/is62/62008.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
+pow=2 #pow>0 and cannot equal 1 exactly
+graph="" #none, save or view
+
+# 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 6 ]
+ 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
+ ;;
+ -p) # get power
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID POWER SPECIFICATION ---"
+ checkMinus "$1"
+ pow=`expr "$1" : '\([.0-9]*\)'`
+ [ "$pow" = "" ] && errMsg "--- POWER=$pow MUST BE A NON-NEGATIVE FLOAT ---"
+ powtest=`echo "$pow <= 0" | bc`
+ [ $powtest -eq 1 ] && errMsg "--- POWER=$pow MUST BE A POSITIVE FLOAT ---"
+ ;;
+ -g) # get graph
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID GRAPH SPECIFICATION ---"
+ checkMinus "$1"
+ graph="$1"
+ [ "$graph" != "view" -a "$graph" != "save" ] && errMsg "--- GRAPH=$graph MUST BE EITHER VIEW OR SAVE ---"
+ ;;
+ -) # 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"
+
+# get outname from infile to use for graph
+inname=`convert $infile -format "%t" info:`
+histfile=${inname}_histog_sahoo.gif
+
+tmpA1="$dir/sahoothresh_1_$$.mpc"
+tmpA2="$dir/sahoothresh_1_$$.cache"
+trap "rm -f $tmpA1 $tmpA2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2 $histfile; exit 1" 1 2 3 15
+
+# 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 sahoo thresh.
+# with IM 6.6.0.10, 6.7.4.10, 6.7.6.10, 6.7.5.5, 6.7.5.6, 6.7.5.7, 6.7.8.10
+# Note: some images (esp. flower) do not work the same as the examples for versions greater than 6.7.5.6.
+# There seems to be a bug 6.7.5.6, which I cannot seem to fix. But it appears very slightly different.
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace gray +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# get totpix in image
+width=`convert $tmpA1 -format "%w" info:`
+height=`convert $tmpA1 -format "%h" info:`
+totpix=`echo "scale=0; $width * $height / 1" | bc`
+
+# function to convert IM histogram into two arrays, value and count
+getHistog()
+ {
+ echo "Generate Histogram"
+ img="$1"
+ # get lists of values and counts from histogram
+ # note that IM histograms are not well sorted (and have multiple bins with counts for the same values)
+ value=`convert $img -format %c -depth 8 -define histogram:unique-colors=true histogram:info: | sort -k 2 -b | sed -n 's/^ *[0-9]*: [(]\([0-9 ]*\).*$/\1/ p'`
+ count=`convert $img -format %c -depth 8 -define histogram:unique-colors=true histogram:info: | sort -k 2 -b | sed -n 's/^ *\([0-9]*\): [(].*$/\1/ p'`
+
+ # put value and count into arrays
+ valueArr=($value)
+ countArr=($count)
+
+ # check if both arrays are the same size
+ if [ ${#valueArr[*]} -ne ${#countArr[*]} ]
+ then
+ errMsg "--- ARRAY SIZES DO NOT MATCH ---"
+ exit 1
+ else
+ numbins=${#valueArr[*]}
+#echo "numbins=$numbins"
+ fi
+}
+
+# function to normalize histog
+getNormlizedHistog()
+ {
+ echo "Normalize Histogram"
+ j=0
+ while [ $j -lt $numbins ]; do
+ countArr[$j]=`echo "scale=10; ${countArr[$j]}/$totpix" | bc`
+ j=`expr $j + 1`
+ done
+ }
+
+
+# process image using Kapur's approach
+
+echo ""
+getHistog "$tmpA1"
+getNormlizedHistog
+[ "$pow" = "1" ] && pow=0.999999
+
+echo "Generate Cumulative Arrays"
+# p=c(i)/N (normalized count or probability, p, at bin i)
+# t=threshold bin
+# n=p0=sum(c(i)/N)zeroth histogram moment => cumulative normalized count (from i=0 to t) = N(t)
+# entropy=ln(sum((p/n)^pow))=ln(sum(p^pow)/n^pow)=ln(r/n^pow)=ln(r)-ln(n^pow)
+# b^x=e^(x*ln(b)); law of base change in raising number to power
+
+i=0
+nlow=0
+nhigh=0
+rlow=0
+rhigh=0
+# test if integer; returns 1 if integer and 0 if not integer
+test=`convert xc: -format "%[fx:($pow-floor($pow))==0?1:0]" info:`
+while [ $i -lt $numbins ]; do
+ if [ $test -eq 0 ]; then
+ nlow=`echo "scale=10; $nlow + ${countArr[$i]}" | bc`
+ nlowArr[$i]=`echo "scale=10; e($pow*l($nlow))" | bc -l`
+ rlow=`echo "scale=10; $rlow + e($pow*l(${countArr[$i]}))" | bc -l`
+ rlowArr[$i]=$rlow
+ j=`expr $numbins - $i - 1`
+ nhigh=`echo "scale=6; $nhigh + ${countArr[$j]}" | bc`
+ nhighArr[$i]=`echo "scale=10; e($pow*l($nhigh))" | bc -l`
+ rhigh=`echo "scale=10; $rhigh + e($pow*l(${countArr[$j]}))" | bc -l`
+ rhighArr[$i]=$rhigh
+ else
+ nlow=`echo "scale=10; $nlow + ${countArr[$i]}" | bc`
+ nlowArr[$i]=`echo "scale=10; $nlow^$pow" | bc`
+ rlow=`echo "scale=10; $rlow + (${countArr[$i]})^$pow" | bc`
+ rlowArr[$i]=$rlow
+ j=`expr $numbins - $i - 1`
+ nhigh=`echo "scale=6; $nhigh + ${countArr[$j]}" | bc`
+ nhighArr[$i]=`echo "scale=10; $nhigh^$pow" | bc`
+ rhigh=`echo "scale=10; $rhigh + (${countArr[$j]})^$pow" | bc`
+ rhighArr[$i]=$rhigh
+ fi
+#echo "i=$i; j=$j; nlow=${nlowArr[$i]}; nhigh=${nhighArr[$i]}; rlow=${rlowArr[$i]}; rhigh=${rhighArr[$i]}"
+ i=`expr $i + 1`
+done
+
+
+echo "Compute Threshold"
+# loop through histogram
+# compute threshold by maximizing total low and high class entropies
+# te=El+Eh
+
+i=0
+teold=0
+threshbin=0
+lastbin=`expr $numbins - 1`
+frac=`echo "scale=10; 1/(1 - $pow)" | bc`
+#echo "frac=$frac"
+while [ $i -lt $lastbin ]; do
+ j=`expr $lastbin - $i - 1`
+ nlow=${nlowArr[$i]}
+ nhigh=${nhighArr[$j]}
+ rlow=${rlowArr[$i]}
+ rhigh=${rhighArr[$j]}
+ te=`echo "scale=10; $frac*(l($rlow) -l($nlow) + l($rhigh) -l($nhigh)) / 1" | bc -l`
+ test=`echo "$te > $teold" | bc`
+ if [ $test -eq 1 ]; then
+ teold=$te
+ threshbin=$i
+ fi
+#echo "i=$i; rlow=$rlow; rhigh=$rhigh; te=$te; teold=$teold; test=$test; threshbin=$threshbin"
+ i=`expr $i + 1`
+done
+thresh=${valueArr[$threshbin]}
+
+
+# compute threshold graph x coord and threshold in percent
+xx=$thresh
+threshpct=`convert xc: -format "%[fx:100*$thresh/255]" info:`
+#echo "xx=$xx; threshpct=$threshpct"
+
+
+echo "Thresholding Image At $threshpct%"
+convert $tmpA1 -threshold $threshpct% "$outfile"
+echo ""
+
+
+if [ "$graph" != "" ]; then
+ convert $tmpA1 -define histogram:unique-colors=false histogram:- | \
+ convert - -negate \
+ -stroke red -strokewidth 1 -draw "line $xx,0 $xx,200" \
+ -background gray -splice 0x30 \
+ -fill white -stroke white -strokewidth 1 \
+ -font ArialB -pointsize 24 \
+ -draw "text 4,22 'threshold=$threshpct%'" -resize 50% \
+ -bordercolor gray50 -border 5 \
+ $histfile
+fi
+
+if [ "$graph" = "view" ]; then
+ convert $histfile x:
+ rm -f $histfile
+fi
+
+exit 0
+
+
+
diff --git a/bin/trianglethresh b/bin/trianglethresh
new file mode 100644
index 0000000..7c8119a
--- /dev/null
+++ b/bin/trianglethresh
@@ -0,0 +1,419 @@
+#!/bin/bash
+#
+# Developed by Fred Weinhaus 10/29/2008 .......... revised 11/3/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: trianglethresh [-g graph] infile outfile
+# USAGE: trianglethresh [-help]
+#
+# OPTIONS:
+#
+# -g graph graph specifies whether to generate a
+# histogram graph image displaying the
+# location and value of the threshold;
+# choices are: view or save;
+# default is no graph
+#
+###
+#
+# NAME: TRIANGLETHRESH
+#
+# PURPOSE: To automatically thresholds an image to binary (b/w) format
+# using the triangle technique.
+#
+# DESCRIPTION: TRIANGLETHRESH automatically thresholds an image to binary
+# (b/w) format. It does not assume the histogram is bimodal. It finds the
+# bin with the highest value (the peak in the histogram) and also the end
+# of the histogram furthest from the peak. It then draws a line between
+# the two. It exhaustively searches along the line drawing a perpendicular
+# from the line to the top of each histogram bin and picks the threshold
+# value at that bin for which the perpendicular is the longest.
+#
+# OPTIONS:
+#
+# -g graph ... GRAPH specifies whether to generate a graph (image) of
+# the histogram, displaying the location and value of the threshold.
+# The choices are: view, save and none. If graph=view is selected, the
+# graph will be created and displayed automatically, but not saved.
+# If graph=save is selected, then the graph will be created and saved
+# to a file using the infile name, with "_histog_triangle.gif" appended,
+# but the graph will not be displayed automatically. If -g option is
+# not specified, then no graph will be created.
+#
+# NOTE: It is highly recommended that the output not be specified
+# as a JPG image as that will cause compression and potentially a
+# non-binary (i.e. a graylevel) result. GIF is the recommended
+# output format.
+#
+# REFERENCES: see the following:
+# http://www.ph.tn.tudelft.nl/Courses/FIP/noframes/fip-Segmenta.html
+#
+# 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
+graph="" #none, save or view
+
+# 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 4 ]
+ 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
+ ;;
+ -g) # get graph
+ shift # to get the next parameter
+ # test if parameter starts with minus sign
+ errorMsg="--- INVALID GRAPH SPECIFICATION ---"
+ checkMinus "$1"
+ graph="$1"
+ [ "$graph" != "view" -a "$graph" != "save" ] && errMsg "--- GRAPH=$graph MUST BE EITHER VIEW OR SAVE ---"
+ ;;
+ -) # 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"
+
+# get outname from infile to use for graph
+inname=`convert $infile -format "%t" info:`
+histfile="${inname}_histog_triangle.gif"
+
+tmpA1="$dir/trianglethresh_1_$$.mpc"
+tmpA2="$dir/trianglethresh_1_$$.cache"
+trap "rm -f $tmpA1 $tmpA2; exit 0" 0
+trap "rm -f $tmpA1 $tmpA2 $histfile; exit 1" 1 2 3 15
+
+# 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 trianglethresh.
+# with IM 6.7.4.10, 6.7.6.10, 6.7.8.10
+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
+
+
+if convert -quiet "$infile" $setcspace -colorspace gray +repage "$tmpA1"
+ then
+ : ' do nothing '
+else
+ errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAG ZERO SIZE ---"
+fi
+
+# get totpix in image
+width=`convert $tmpA1 -format "%w" info:`
+height=`convert $tmpA1 -format "%h" info:`
+totpix=`echo "scale=0; $width * $height / 1" | bc`
+
+
+# separate IM histogram into two arrays, value and count, and fill out for empty bins and normalize
+
+ # get filled value array from IM histogram
+ nvalueArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk '
+ # AWK
+ { vbin[$2] += $2;}
+ END { for (i=0;i<256;i++) {print vbin[i]+0; } } '`)
+# echo ${nvalueArr[*]}
+# echo ${#nvalueArr[*]}
+ numvals=${#nvalueArr[*]}
+
+ # get filled and normalized count array from IM histogram
+ ncountArr=(`convert $tmpA1 -depth 8 -format "%c" -define histogram:unique-colors=true histogram:info:- \
+ | tr -cs '0-9\012' ' ' | sed -n 's/[ ]*\([0-9]*\)[ ]*\([0-9]*\).*$/\1 \2/p' |\
+ awk -v totpix="$totpix" '
+ # AWK
+ { cbin[$2] += $1; }
+ END { for (i=0;i<256;i++) {print (cbin[i]+0)/totpix; } } '`)
+# echo ${ncountArr[*]}
+# echo ${#ncountArr[*]}
+ numcounts=${#ncountArr[*]}
+
+ [ $numvals -ne $numcounts ] && errMsg "--- NUMBER OF COUNTS IS NOT THE SAME AS NUMBER OF VALUES ---"
+
+
+
+ # find start bin (first one not zero)
+ startbin=`for ((i=0; i<256; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk '
+ # AWK to find startbin
+ { cbin[$1] = $2; }
+ END { for (i=0;i<256;i++) { if (cbin[i]!=0) { print i; break; } } } '`
+ eval startbincount=${ncountArr[$startbin]}
+# echo "startbin=$startbin; startbincount=$startbincount;"
+
+
+ # find end bin (last one not zero)
+ endbin=`for ((i=0; i<256; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk '
+ # AWK to find startbin
+ { cbin[$1] = $2; }
+ END { for (i=255;i>=0;i--) { if (cbin[i]!=0) { print i; break; } } } '`
+ endbincount=${ncountArr[$endbin]}
+# echo "endbin=$endbin; endbincount=$endbincount;"
+
+
+ # find max bin (bin with largest count)
+ maxbin=`for ((i=0; i<256; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk '
+ # AWK to find startbin
+ { cbin[$1] = $2; maxbincount = 0; maxbin = 0; }
+ END { for (i=0;i<256;i++) { if (cbin[i] > maxbincount) { maxbincount=cbin[i]; maxbin=i; } } print maxbin } '`
+ maxbincount=${ncountArr[$maxbin]}
+# echo "maxbin=$maxbin; maxbincount=$maxbincount;"
+
+
+ # set x1,y1 for bin with max count
+ y1=$maxbincount
+ x1=$maxbin
+
+ # set x2,y2 for furthest end bin from bin with max count
+ lowdiff=`expr $maxbin - $startbin`
+ highdiff=`expr $endbin - $maxbin`
+ if [ $lowdiff -ge $highdiff ]; then
+ x2=$startbin
+ y2=0
+ else
+ x2=$endbin
+ y2=0
+ fi
+#echo "lowdiff=$lowdiff; highdiff=$highdiff; x1=$x1; y1=$y1; x2=$x2; y2=$y2"
+
+
+# compute threshold
+
+# equation of line between x1,y1 and x2,y2 is Ax+By+C=0
+# where A=(y1-y2), B=(x2-x1), C=-(A*x1+B*y1)
+# Dist=abs(A*x1+B*y1+C)/sqrt(A^2+B^2+C^2)
+aa=`echo "scale=10; ($y1 - $y2)/1" | bc`
+bb=`echo "scale=10; ($x2 - $x1)/1" | bc`
+cc=`echo "scale=10; -($aa*$x1 + $bb*$y1)/1" | bc`
+ir=`echo "scale=10; 1/sqrt($aa*$aa + $bb*$bb + $cc*$cc)" | bc`
+#echo "aa=$aa; bb=$bb; cc=$cc; ir=$ir"
+
+
+# increment from appropriate end to bin with max count
+# and compute dist and save bin where dist is max
+
+if [ $x2 -eq $startbin ]; then
+
+ distbin=`for ((i=0; i<256; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk -v startbin="$startbin" -v maxbin="$maxbin" -v aa="$aa" -v bb="$bb" -v cc="$cc" -v ir="$ir" '
+ # AWK to find threshold
+ { x[$1] = $1; y[$1] = $2; dist=0; }
+ END { for (i=startbin;i<maxbin;i++) { dd = ir*(aa*x[i]+bb*y[i]+cc); ddabs = sqrt(dd*dd);
+ if (ddabs>dist && dd>0) { distbin=i; dist=ddabs; } } print distbin } '`
+# echo "$distbin"
+
+else
+
+ distbin=`for ((i=0; i<256; i++)); do
+ echo "$i ${ncountArr[$i]}"
+ done |\
+ awk -v endbin="$endbin" -v maxbin="$maxbin" -v aa="$aa" -v bb="$bb" -v cc="$cc" -v ir="$ir" '
+ # AWK to find threshold
+ { x[$1] = $1; y[$1] = $2; dist=0; }
+ END { for (i=endbin;i>maxbin;i--) { dd = ir*(aa*x[i]+bb*y[i]+cc); ddabs = sqrt(dd*dd);
+ if (ddabs>dist && dd<0) { distbin=i; dist=ddabs; } } print distbin } '`
+# echo "$distbin"
+fi
+
+
+# set threshold to value (0 to 255) of bin where dist is max
+x0=$distbin
+y0=${ncountArr[$distbin]}
+#echo "x0=$x0; y0=$y0"
+
+# convert threshold value to range 0 to 100
+threshpct=`convert xc: -format "%[fx:100*$x0/255]" info:`
+
+
+echo "Thresholding Image At $threshpct%"
+# threshold image
+convert $tmpA1 -threshold $threshpct% "$outfile"
+echo ""
+
+
+
+if [ "$graph" != "" ]; then
+
+# compute triangle lines for graphing
+
+ # histogram image scales maxbincount to 200 pixels
+ # counts need to be complemented with 200
+ # as 0 is at image top and 200 at image bottom
+
+ # convert y1 and y2 to points on histogram image
+ xx1=$x1
+ yy1=0
+ xx2=$x2
+ yy2=200
+# echo "xx1=$xx1; yy1=$yy1; xx2=$xx2; yy2=$yy2"
+
+ # convert x0,y0 to point on histogram image
+ xx0=$x0
+ yy0=`echo "scale=10; 200*(1 - $y0/$y1)/1" | bc`
+#echo "y0=$y0; y1=$y1; yy0=$yy0"
+
+ # find intersection of perpendicular through xx0,yy0 and line between xx1,yy1 and x2,y2
+ # get slope and intercept of line between xx1,yy1 and xx2,yy2
+ sl=`echo "scale=10; ($yy2 - $yy1)/($xx2 - $xx1)" | bc`
+ il=`echo "scale=10; ($yy1 - $sl*$xx1)/1" | bc`
+
+ # get slope and intercept of perpendicular
+ # slope is negative inverse of that of line between xx1,yy1 and xx2,yy2
+ sp=`echo "scale=10; -1/$sl" | bc`
+ ip=`echo "scale=10; ($yy0 - $sp*$xx0)/1" | bc`
+
+ # compute intersection point xx3,yy3
+ xx3=`echo "scale=10; ($ip - $il)/($sl - $sp)" | bc`
+ yy3=`echo "scale=10; ($sl*$xx3 + $il)/1" | bc`
+
+# echo "sl=$sl; il=$il; sp=$sp; ip=$ip; xx3=$xx3; yy3=$yy3"
+# echo "xx0=$xx0; yy0=$yy0; xx3=$xx3; yy3=$yy3"
+
+ convert $tmpA1 -define histogram:unique-colors=false histogram:- | \
+ convert - -negate \
+ -stroke blue -strokewidth 1 -draw "line $xx1,$yy1 $xx2,$yy2" \
+ -stroke green1 -strokewidth 1 -draw "line $xx0,$yy0 $xx3,$yy3" \
+ -stroke red -strokewidth 1 -draw "line $xx0,0 $xx0,200" \
+ -background gray -splice 0x30 \
+ -fill white -stroke white -strokewidth 1 \
+ -font ArialB -pointsize 24 \
+ -draw "text 4,22 'threshold=$threshpct%'" -resize 50% \
+ -bordercolor gray50 -border 5 \
+ "$histfile"
+fi
+
+
+if [ "$graph" = "view" ]; then
+ convert $histfile x:
+ rm -f "$histfile"
+fi
+
+exit 0
+
+
+
diff --git a/photoblaster/modules/pbbreaker/__init__.py b/photoblaster/modules/pbbreaker/__init__.py
index fb3f26a..93c7568 100755
--- a/photoblaster/modules/pbbreaker/__init__.py
+++ b/photoblaster/modules/pbbreaker/__init__.py
@@ -24,7 +24,7 @@ _BREAKTYPE_TRANSLATE = {
'RGB_WASH_2': 'psb',
'NOISY_BREAK': 'palm',
'NOISY_BREAK_2': 'fig',
- 'BROKEN_VIGNETTE': 'pbm',
+ 'BROKEN_THRESH': 'pbm',
'FAX_MACHINE': 'cals',
'STRIPES': 'exr',
'PHOTOCOPY': 'art',
diff --git a/share/frontend/imbreak/index.html b/share/frontend/imbreak/index.html
index 7bab966..db1e272 100755
--- a/share/frontend/imbreak/index.html
+++ b/share/frontend/imbreak/index.html
@@ -202,7 +202,7 @@ BREAK TYPE:&nbsp;<select id="breaktype">
<option value="RGB_WASH">RGB WASH</option>
<option value="RGB_WASH_2">CHOP AND BLUR</option>
<option value="NOISY_BREAK">NOISY BREAK</option>
- <option value="BROKEN_VIGNETTE">BROKEN VIGNETTE</option>
+ <option value="BROKEN_THRESH">BROKEN THRESH</option>
<option value="FAX_MACHINE">FAX MACHINE</option>
<option value="STRIPES">STRIPES</option>