|
#!/bin/bash
|
|
#
|
|
# Developed by Fred Weinhaus 5/2/2010 .......... 9/12/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: huemap [-h hues] [-t tolers] [-r] infile outfile
|
|
# USAGE: huemap [-help]
|
|
#
|
|
# OPTIONS:
|
|
#
|
|
# -h hues hues=srchue,dsthue; source and destination hue values;
|
|
# comma separated list; 0<=integer<=360; default=240,120
|
|
# maps blue to green
|
|
# -t tolers tolers=stoler,dtoler; source and destination range of
|
|
# hues on each side of hue values; 0<=integer<=360;
|
|
# default=10,10
|
|
# -r reverse direction of range of destination hues
|
|
#
|
|
###
|
|
#
|
|
# NAME: HUEMAP
|
|
#
|
|
# PURPOSE: To transform the hues in an image from one range to another.
|
|
#
|
|
# DESCRIPTION: HUEMAP transform the hues in an image from one range to another.
|
|
# One hue can be mapped to another single hue. One range of hues can be mapped
|
|
# to a single hue. Or one range of hues can be mapped to another range of hues.
|
|
# This is similar to GIMP's Rotate Hues.
|
|
#
|
|
#
|
|
# OPTIONS:
|
|
#
|
|
# -h hues ... HUES=SRCHUE,DSTHUE. These are source and destination hue values
|
|
# in the range of 0<=integer<=360. The source hue (range) will be replaced by
|
|
# the detination hue (range), depending upon the tolers values below. The
|
|
# default=240,120 maps blue to green.
|
|
#
|
|
# -t tolers ... TOLERS=STOLER,DTOLER. These are the source and destination
|
|
# ranges on each side of the specified hues that determine the range of hues
|
|
# to be mapped. Values are in the range of 0<=integer<=360. The default=10,10.
|
|
#
|
|
# -r ... REVERSE the direction of the range of destination hues.
|
|
#
|
|
# REQUIREMENT: IM version 6.3.5-7 or higher due to the use of -clut.
|
|
#
|
|
# NOTE: If the image has an alpha channel, it will be copied unchanged to
|
|
# the output image.
|
|
#
|
|
# 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
|
|
hues="240,120" # src,dst hues
|
|
tolers="10,10" # src and dst ranges on either side of src,dst hues
|
|
reverse="no" # reverse direction of start and end dst
|
|
|
|
# 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 7 ]
|
|
then
|
|
errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---"
|
|
else
|
|
while [ $# -gt 0 ]
|
|
do
|
|
# get parameter values
|
|
case "$1" in
|
|
-help) # help information
|
|
echo ""
|
|
usage2
|
|
exit 0
|
|
;;
|
|
-h) # get hues
|
|
shift # to get the next parameter
|
|
# test if parameter starts with minus sign
|
|
errorMsg="--- INVALID HUES SPECIFICATION ---"
|
|
checkMinus "$1"
|
|
hues=`expr "$1" : '\([,0-9]*\)'`
|
|
[ "$hues" = "" ] && errMsg "--- HUES=$hues MUST BE TWO COMMA DELIMITED NON-NEGATIVE INTEGERS ---"
|
|
;;
|
|
-t) # get tolers
|
|
shift # to get the next parameter
|
|
# test if parameter starts with minus sign
|
|
errorMsg="--- INVALID TOLERANCES SPECIFICATION ---"
|
|
checkMinus "$1"
|
|
tolers=`expr "$1" : '\([,0-9]*\)'`
|
|
[ "$tolers" = "" ] && errMsg "--- TOLERANCES=$tolers MUST BE TWO COMMA DELIMITED NON-NEGATIVE INTEGERS ---"
|
|
;;
|
|
-r) # get reverse
|
|
reverse="yes"
|
|
;;
|
|
-) # STDIN and end of arguments
|
|
break
|
|
;;
|
|
-*) # any other - argument
|
|
errMsg "--- UNKNOWN OPTION ---"
|
|
;;
|
|
*) # end of arguments
|
|
break
|
|
;;
|
|
esac
|
|
shift # next option
|
|
done
|
|
#
|
|
# get infile and outfile
|
|
infile="$1"
|
|
outfile="$2"
|
|
fi
|
|
|
|
# test that infile provided
|
|
[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED"
|
|
|
|
# test that outfile provided
|
|
[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED"
|
|
|
|
# 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`
|
|
|
|
srchue=`echo $hues | cut -d, -f 1`
|
|
dsthue=`echo $hues | cut -d, -f 2`
|
|
stoler=`echo $tolers | cut -d, -f 1`
|
|
dtoler=`echo $tolers | cut -d, -f 2`
|
|
#echo "srchue=$srchue; dsthue=$dsthue; stoler=$stoler; dtoler=$dtoler"
|
|
|
|
# set up temp files
|
|
tmpI1="$dir/huemap_I_$$.mpc"
|
|
tmpI2="$dir/huemap_I_$$.cache"
|
|
tmpH1="$dir/huemap_H_$$.mpc"
|
|
tmpH2="$dir/huemap_H_$$.cache"
|
|
tmpS1="$dir/huemap_S_$$.mpc"
|
|
tmpS2="$dir/huemap_S_$$.cache"
|
|
tmpL1="$dir/huemap_L_$$.mpc"
|
|
tmpL2="$dir/huemap_L_$$.cache"
|
|
tmpG1="$dir/huemap_G_$$.mpc"
|
|
tmpG2="$dir/huemap_G_$$.cache"
|
|
tmpT1="$dir/huemap_T_$$.mpc"
|
|
tmpT2="$dir/huemap_T_$$.cache"
|
|
tmpA1="$dir/huemap_A_$$.mpc"
|
|
tmpA2="$dir/huemap_A_$$.cache"
|
|
trap "rm -f $tmpI1 $tmpI2 $tmpH1 $tmpH2 $tmpS1 $tmpS2 $tmpL1 $tmpL2 $tmpG1 $tmpG2 $tmpT1 $tmpT2 $tmpA1 $tmpA2;" 0
|
|
trap "rm -f $tmpI1 $tmpI2 $tmpH1 $tmpH2 $tmpS1 $tmpS2 $tmpL1 $tmpL2 $tmpG1 $tmpG2 $tmpT1 $tmpT2 $tmpA1 $tmpA2; exit 1" 1 2 3 15
|
|
trap "rm -f $tmpI1 $tmpI2 $tmpH1 $tmpH2 $tmpS1 $tmpS2 $tmpL1 $tmpL2 $tmpG1 $tmpG2 $tmpT1 $tmpT2 $tmpA1 $tmpA2; exit 1" ERR
|
|
|
|
# read the input image and filter image into the temp files and test validity.
|
|
convert -quiet "$infile" +repage "$tmpI1" ||
|
|
errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"
|
|
|
|
# separate alpha channel if exists
|
|
is_alpha=`identify -ping -verbose $tmpI1 | grep "Alpha" | head -n 1`
|
|
if [ "$is_alpha" != "" ]; then
|
|
convert $tmpI1 -alpha extract $tmpA1
|
|
convert $tmpI1 -alpha off $tmpI1
|
|
fi
|
|
|
|
# convert to HSL and separate channels
|
|
convert $tmpI1 -colorspace HSL -channel R -separate $tmpH1
|
|
convert $tmpI1 -colorspace HSL -channel G -separate $tmpS1
|
|
convert $tmpI1 -colorspace HSL -channel B -separate $tmpL1
|
|
|
|
# create length 360 gradient
|
|
convert -size 1x360 gradient: -rotate 90 $tmpG1
|
|
|
|
|
|
# modify gradient as lut to apply to hue channel
|
|
|
|
# create gradient section of length=2*toler+1 and value=dst1 to dst2 scaled to percent
|
|
# insert into beginning of transparent row
|
|
# due to bug, make it red, then change to dst later
|
|
dst1=`convert xc: -format "%[fx:($dsthue-$dtoler)]" info:`
|
|
dst2=`convert xc: -format "%[fx:($dsthue+$dtoler)]" info:`
|
|
#echo "dst1=$dst1; dst2=$dst2"
|
|
if [ "$reverse" = "yes" ]; then
|
|
dst=$dst1
|
|
dst1=$dst2
|
|
dst2=$dst
|
|
fi
|
|
#echo "dst1=$dst1; dst2=$dst2"
|
|
|
|
len=$((2*$stoler+1))
|
|
|
|
if [ $stoler -eq 0 -a $dtoler -gt 0 ]; then
|
|
errMsg "--- DESTINATION TOLERANCE MUST BE 0 IF SOURCE TOLERANCE IS 0 ---"
|
|
elif [ $dtoler -eq 0 ]; then
|
|
convert -size ${len}x1 xc:red \
|
|
-background none -gravity west -extent 360x1 $tmpT1
|
|
else
|
|
# test if range crosses hue=0 red
|
|
test=`convert xc: -format "%[fx:($dst1<0||$dst2<0)?1:0]" info:`
|
|
if [ $test -eq 1 ]; then
|
|
convert -size ${len}x1 xc: \
|
|
-fx "uu=($dst2-$dst1)*i/(w-1)+$dst1; mod(uu+360,360)/360" \
|
|
-channel red -evaluate set 0 +channel \
|
|
-background none -gravity west -extent 360x1 $tmpT1
|
|
else
|
|
dst1=`convert xc: -format "%[fx:100*$dst1/360]" info:`
|
|
dst2=`convert xc: -format "%[fx:100*$dst2/360]" info:`
|
|
convert -size ${len}x1 gradient:"gray($dst1%)"-"gray($dst2%)" \
|
|
-channel red -evaluate set 0 +channel \
|
|
-background none -gravity west -extent 360x1 $tmpT1
|
|
fi
|
|
fi
|
|
|
|
# roll the above tmp to the correct start (hue) position
|
|
# and composite with gradient
|
|
# and convert red to dst
|
|
src1=`convert xc: -format "%[fx:$srchue-$stoler]" info:`
|
|
sign=`convert xc: -format "%[fx:sign($src1)<0?0:1]" info:`
|
|
abs_src1=`convert xc: -format "%[fx:abs($src1)]" info:`
|
|
if [ $sign -eq 0 ]; then
|
|
rollval="-${abs_src1}+0"
|
|
else
|
|
rollval="+${abs_src1}+0"
|
|
fi
|
|
#echo "rollval=$rollval"
|
|
|
|
if [ $dtoler -eq 0 ]; then
|
|
dst1=`convert xc: -format "%[fx:100*$dst1/360]" info:`
|
|
convert $tmpG1 \( $tmpT1 -roll $rollval \) \
|
|
-compose over -composite \
|
|
-fill "gray($dst1%)" -opaque red \
|
|
$tmpG1
|
|
else
|
|
convert $tmpG1 \( $tmpT1 -roll $rollval \) \
|
|
-compose over -composite \
|
|
-channel G -separate +channel \
|
|
$tmpG1
|
|
fi
|
|
|
|
# apply modified gradient as lut to hue channel
|
|
convert $tmpH1 $tmpG1 -interpolate nearest-neighbor -clut $tmpH1
|
|
|
|
# colorspace swapped at IM 6.7.5.5, but not properly fixed until 6.7.6.6
|
|
# before swap verbose info reported colorspace=RGB after colorspace=sRGB
|
|
if [ "$im_version" -ge "06070606" ]; then
|
|
cspace="sRGB"
|
|
else
|
|
cspace="RGB"
|
|
fi
|
|
|
|
|
|
# recombine channels and convert to RGB
|
|
convert $tmpH1 -colorspace HSL \
|
|
$tmpH1 -compose CopyRed -composite \
|
|
$tmpS1 -compose CopyGreen -composite \
|
|
$tmpL1 -compose CopyBlue -composite \
|
|
-colorspace $cspace $tmpI1
|
|
|
|
|
|
# add alpha channel back if needed
|
|
if [ "$is_alpha" = "True" ]; then
|
|
convert $tmpI1 $tmpA1 -alpha off -compose copy_opacity -composite "$outfile"
|
|
else
|
|
convert $tmpI1 "$outfile"
|
|
fi
|
|
|
|
exit 0
|
|
|