#!/bin/bash # # Developed by Fred Weinhaus 2/24/2022 .......... revised 2/24/2022 # # ------------------------------------------------------------------------------ # # 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: position [-m method] [-d direction] [-o offset] [-l leftpt] # [-r rightpt] [-b bcolor] [-f format] [-T trim] infile1 infile2 outfile # # USAGE: position [-h or -help] # # OPTIONS: # # -m method positioning method; choices are: offset or cpoints; # default=offset # -d direction positioning direction; choices are: horizontal or # vertical; default=horizontal # -o offset offset +X+Y values for left/ope edge of second image # relative to right/bottom edge of first image. Used when # method=offset; positive or negative offsets are allowed; # default=+0+0 # -l leftpt left (first) image control x,y point; default=0,0 # -r rightpt right (second) image control x,y point; default=0,0 # -b bcolor background color to fill empty spaces # -f format output color format; choices are: RG, GB, BR or RGB; # default=RGB # -T trim trim output to remove any background fill areas # ### # # NAME: POSITION # # PURPOSE: To position one image relative to another image. # # DESCRIPTION: POSITION aligns or offsets one image relative to a another # image. The second image is positioned relative to the first image either # horizontally or vertically. Positioning can be done using X and Y offsets # or by specifying one controll point for each image. # # OPTIONS: # # -m method ... positioning METHOD. The choices are: offset (o) or cpoints (c). # The default=offset. # # -d direction ... positioning DIRECTION. The choices are: horizontal (h) or # vertical (v). The default=horizontal. # # -o offset ... OFFSET +X+Y values for left/top edge of second image relative # to right/bottom edge of first image. This is used when method=offset. # Position X and Y offsets may be either positive or negative. The default=+0+0 # # -l leftpt ... LEFT (first) image control x,y POINT. Values are integers>0. # The default=0,0 # # -r rightpt ... RIGHT (second) image control x,y POINT. Values are integers>0. # The default=0,0 # # -b bcolor ... BGCOLOR is the background color to fill empty spaces. Any # Imagemagick color is allowed. The default=none (transparent) # # -f format ... output color FORMAT; The choices are: RG, GB, BR or RGB. # RGB is the normal color image. RG, for example, is first image in Red and # second image in Green and any overlay will show in yellow (mix of Red and # Green). The default=RGB. # # -T trim ... TRIM output to remove any background fill areas. # Choices are: yes (y) or no (n). The default=no. Background color must be # unique in the image for the trim to work properly. # # LIMITATIONS: TRIM option only works for Imagemagick 7.0.9-0 or higher. # # 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="offset" # offset or cpoints direction="horizontal" # horizontal or vertical offset=+0+0 # offset #offset=-90-40 # offset leftpt="0,0" # left image single control point rightpt="0,0" # right image single control point #leftpt="287,49" # left image single control point #rightpt="76,89" # right image single control point bcolor=none # background color format="RGB" # RG or GB or BR or RGB output color format trim="no" # trim output; yes or no # set directory for temporary files tmpdir="/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 19 ] 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) # method shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID METHOD SPECIFICATION ---" checkMinus "$1" method=`echo "$1" | tr "[:upper:]" "[:lower:]"` case "$method" in offset|o) method="offset" ;; cpoints|c) method="cpoints" ;; *) errMsg "--- METHOD=$method IS AN INVALID VALUE ---" esac ;; -d) # direction shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID DIRECTION SPECIFICATION ---" checkMinus "$1" direction=`echo "$1" | tr "[:upper:]" "[:lower:]"` case "$direction" in horizontal|h) direction="horizontal" ;; vertical|v) direction="vertical" ;; *) errMsg "--- DIRECTION=$direction IS AN INVALID VALUE ---" esac ;; -o) # offset shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID OFFSET SPECIFICATION ---" #checkMinus "$1" offset=`expr "$1" : '\([-+][0-9]*[-+][0-9]*\)'` [ "$offset" = "" ] && errMsg "--- OFFSET=$offset IS INVALID ---" ;; -l) # leftpt shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID LEFTPT SPECIFICATION ---" checkMinus "$1" leftpt=`expr "$1" : '\([0-9]*,[0-9]*\)'` [ "$leftpt" = "" ] && errMsg "--- LEFTPT=$leftpt IS INVALID ---" ;; -r) # rightpt shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID RIGHTPT SPECIFICATION ---" checkMinus "$1" rightpt=`expr "$1" : '\([0-9]*,[0-9]*\)'` [ "$rightpt" = "" ] && errMsg "--- RIGHTPT=$rightpt IS INVALID ---" ;; -b) # bcolor shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID BCOLOR SPECIFICATION ---" checkMinus "$1" bcolor="$1" ;; -f) # format shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID FORMAT SPECIFICATION ---" checkMinus "$1" format=`echo "$1" | tr "[:lower:]" "[:upper:]"` case "$format" in RG) ;; GB) ;; BR) ;; RGB) ;; *) errMsg "--- FORMAT=$format IS AN INVALID VALUE ---" esac ;; -T) # trim shift # to get the next parameter # test if parameter starts with minus sign errorMsg="--- INVALID TRIM SPECIFICATION ---" checkMinus "$1" trim=`echo "$1" | tr "[:upper:]" "[:lower:]"` case "$trim" in yes) ;; no) ;; *) errMsg "--- TRIM=$trim IS AN INVALID VALUE ---" esac ;; -) # STDIN and end of arguments break ;; -*) # any other - argument errMsg "--- UNKNOWN OPTION ---" ;; *) # end of arguments break ;; esac shift # next option done # # get infiles and outfile infile1="$1" infile2="$2" outfile="$3" fi # test that infile1 provided [ "$infile1" = "" ] && errMsg "--- NO INPUT FILE 1 SPECIFIED ---" # test that infile2 provided [ "$infile2" = "" ] && errMsg "--- NO INPUT FILE 2 SPECIFIED ---" # test that outfile provided [ "$outfile" = "" ] && errMsg "--- NO OUTPUT FILE SPECIFIED ---" dir="$tmpdir/POSITION.$$" mkdir "$dir" || echo "--- FAILED TO CREATE TEMPORARY FILE DIRECTORY ---" trap "rm -rf $dir; exit 0" 0 trap "rm -rf $dir; exit 1" 1 2 3 15 # read input images # test if infile exists, is readable and is not zero size convert -quiet "$infile1" +repage $dir/tmpI1.mpc || echo "--- FILE $infile1 DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---" convert -quiet "$infile2" +repage $dir/tmpI2.mpc || echo "--- FILE $infile2 DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---" # get image1 dimensions ww=`convert $dir/tmpI1.mpc -format "%w" info:` hh=`convert $dir/tmpI1.mpc -format "%h" info:` #echo "ww=$ww; hh=$hh;" # get page values for second image if [ "$method" = "cpoints" ]; then lx=`echo "$leftpt" | cut -d, -f1` ly=`echo "$leftpt" | cut -d, -f2` rx=`echo "$rightpt" | cut -d, -f1` ry=`echo "$rightpt" | cut -d, -f2` pagex=$((lx-rx)) pagey=$((ly-ry)) else # offsets xoff=`echo $offset | sed -n 's/^\([+-].*\)[+-].*$/\1/p'` yoff=`echo $offset | sed -n 's/^[+-].*\([+-].*\)$/\1/p'` if [ "$direction" = "horizontal" ]; then pagex=$((ww+xoff)) pagey=$((yoff)) else # vertical pagex=$((xoff)) pagey=$((hh+yoff)) fi fi #echo "ww=$ww; hh=$hh; xoff=$xoff; yoff=$yoff; lx=$lx; ly=$ly; rx=$rx; ry=$ry; pagex=$pagex; pagey=$pagey;" # set up for trim [ "$trim" = "yes" ] && trimming="-background $bcolor -define trim:percent-background=0% -trim +repage" # align the two images if [ "$format" = "RG" ]; then convert \ \( $dir/tmpI1.mpc -colorspace gray -set page +0+0 -write mpr:img1 +delete \) \ \( $dir/tmpI2.mpc -colorspace gray -set page +${pagex}+${pagey} -write mpr:img2 +delete \) \ \( \( mpr:img2 -background black -colorize 100 \) \( mpr:img1 +level-colors "black,red" \) \ -background "$bcolor" -layers merge +repage \) \ \( \( mpr:img1 -background black -colorize 100 \) \( mpr:img2 +level-colors "black,green1" \) \ -background "$bcolor" -layers merge +repage \) \ -compose over -compose blend -composite $trimming \ "$outfile" elif [ "$format" = "GB" ]; then convert \ \( $dir/tmpI1.mpc -colorspace gray -set page +0+0 -write mpr:img1 +delete \) \ \( $dir/tmpI2.mpc -colorspace gray -set page +${pagex}+${pagey} -write mpr:img2 +delete \) \ \( \( mpr:img2 -background black -colorize 100 \) \( mpr:img1 +level-colors "black,green1" \) \ -background "$bcolor" -layers merge +repage \) \ \( \( mpr:img1 -background black -colorize 100 \) \( mpr:img2 +level-colors "black,blue" \) \ -background "$bcolor" -layers merge +repage \) \ -compose over -compose blend -composite $trimming \ "$outfile" elif [ "$format" = "BR" ]; then convert \ \( $dir/tmpI1.mpc -colorspace gray -set page +0+0 -write mpr:img1 +delete \) \ \( $dir/tmpI2.mpc -colorspace gray -set page +${pagex}+${pagey} -write mpr:img2 +delete \) \ \( \( mpr:img2 -background black -colorize 100 \) \( mpr:img1 +level-colors "black,blue" \) \ -background "$bcolor" -layers merge +repage \) \ \( \( mpr:img1 -background black -colorize 100 \) \( mpr:img2 +level-colors "black,red" \) \ -background "$bcolor" -layers merge +repage \) \ -compose over -compose blend -composite $trimming \ "$outfile" else # RGB convert \ \( $dir/tmpI1.mpc -set page +0+0 \) \ \( $dir/tmpI2.mpc -set page +${pagex}+${pagey} \) \ -background "$bcolor" -layers merge +repage $trimming \ "$outfile" fi exit 0