Add the ability to launch into "Batch Convert" custom script

Ideas for improvements and requests for new features in XnView MP

Moderators: XnTriq, helmut, xnview

User avatar
oops66
XnThusiast
Posts: 2005
Joined: Tue Jul 17, 2007 1:17 am
Location: France

Add the ability to launch into "Batch Convert" custom script

Post by oops66 »

Hello,
It would be very interesting into XnViewMP under Tools\ "Batch Convert"\"Action tab" to allow to the user to launch a custom script (by iteration for all input files) , ... for example here:

imagemagick and exiftool bash script: autotone4xnview.sh :
http://www.fmwconcepts.com/imagemagick/ ... /index.php
autotone filter = auto brightness/contrast + auto gray balance + auto white balance + auto gamma correction + auto noise removal + auto sharpening (all in one filter).

Image ... after ...> Image

Code: Select all

#!/bin/bash


#  V002 - 22 feb 2014

# 
# Developed by Fred Weinhaus 11/15/2007 .......... revised 1/12/2014
# 
# USAGE: retinex [-m colormodel] [-f fact] [-c contrast] [-b bright] [-s sat] [-r res1,res2,res3] infile outfile
# USAGE: retinex [-h or -help]
# 
# OPTIONS:
# 
# -m      colormodel          processing colorspace; HSL or RGB; default=HSL
# -f      fact                color boost blending factor; 0 - 100; default=0
#                             0 is no color boost; 100 is full color boost
# -c      contrast            contrast gamma; contrast>0; default=1 (no change)
# -b      bright              brightness gain; bright>=0; default=100 (no change)
# -s      sat                 saturation gain; sat>=0; default=100 (no change)
# -r      res1,res2,res3      resolution levels; default=5,20,240
# 
###
# 
# NAME: RETINEX
# 
# PURPOSE: To enhance detail and color in an image using the multiscale 
# retinex algorithm.
# 
# DESCRIPTION: RETINEX converts the intensity values in the image to
# reflectance values at three resolution scales. This is achieved by
# computing the log of the ratio of the image to each of three gaussian
# blurred version of the image; one slightly blurred, one moderately
# blurred and one severely blurred. The three results are then averaged
# together. As this process sometimes desaturates the image an optional
# color boost is provided. The color boost process creates a new image
# which is the product of the resulting image with the log of the ratio of
# the original image to its grayscale version. The color boosted image is
# then blended with the previous result. The basic processing operation 
# prior to the color boost can be performed in RGB colorspace in order to 
# process each channel separately or it can be performed only on the 
# lightness channel in HSL colorspace. The latter may be prefered as 
# it tends to maintain the color balance from the original image. The 
# implementation of this script adapts the retinex approach developed 
# by NASA/Truview. See the following references:
# http://dragon.larc.nasa.gov/retinex/background/pubabs/papers/ret40.pdf
# http://visl.technion.ac.il/1999/99-07/www/
# http://dragon.larc.nasa.gov/retinex/
# http://www.truview.com/
# 
# 
# OPTIONS: 
# 
# -m colormodel ... COLORMODEL can be either RGB or HSL. If RGB is 
# selected, then each of the R, G and B channels will be processed 
# independently (possibly causing a color shift). If HSL is selected, 
# then only the Lightness channel will be processed and then recombined 
# with the Hue and Saturation channels and converted back to RGB. This 
# option may be preferred as it will better maintain the relative color 
# balance from the original image. The default is HSL.
# 
# -f fact ... FACT specifies the color boost blending percentage. Values 
# for fact may be an integer between 0 and 100. A value of 0 indicates no 
# color boost. A value of 100 indicates full color boost. The default=0
# 
# -c contrast ... CONTRAST specifies a gamma control for contrast. Values 
# for contrast must be positive floats or integers. A value of 1 indicates 
# no contrast change. Larger/smaller values indicate lower/higher contrast.
# 
# -b bright ... BRIGHT specifies a gain in brightness. Values for bright 
# must be non-negative integers. A value of 100 indicates no brightness 
# change. Larger/smaller values indicate brighter/darker results.
# 
# -s sat ... SAT specifies a gain in saturation. Values for sat 
# must be non-negative integers. A value of 100 indicates no saturation 
# change. Larger/smaller values indicate more/less saturation.
# 
# -r res1,res2,res3 ... RES1,RES2,RES3 specifies the three resolution 
# levels in the retinex processing. They correspond to the pixel 
# equivalent values to the sigma in the Gaussian blurring. These 
# resolution values do not seem to be very sensitive such that 
# variations to do not seem to make much change in the result. 
# The default values are 5,20,240.
# 
# NOTE: This script will be slow due the use of -fx, unless hdri is enabled.
# 
# 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. 
# 
######
#



################# modif  ##########################
# extract exif to mie file (save exif data from image original)
# -o = Combining the -overwrite_original option with -o causes the original source file to be erased after the output file is successfully written
initial=$1


# enlève la derniere extention ex : fic.txt.jpg = fic.txt
# ok    sansext=`echo $1 | sed 's/\.*[a-z]*$//'`
sansext=`echo $1 | sed 's/\.*[a-z]*$//'`



#exiftool -TagsFromFile -o $initial -all:all $initial.mie
# ok exiftool -TagsFromFile "$initial" -all:all "$initial.mie"
/usr/bin/exiftool -TagsFromFile "$initial" -all:all "$sansext.mie"





# set default values
colormodel="HSL"	# colorspace in which to process image
fact=0          	# color boost blending factor
contrast=1      	# contrast control (gamma value)
bright=100      	# brightness control
sat=100         	# saturation conrol
res=5,20,240    	# resolution sizes

# 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 -n '/^###/q;  /^#/!q;  s/^#//;  s/^ //;  4,$p' "$PROGDIR/$PROGNAME"
	}
usage2() 
	{
	echo >&2 ""
	echo >&2 "$PROGNAME:" "$@"
	sed >&2 -n '/^######/q;  /^#/!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 14 ]
	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 colormodel
					   shift  # to get the next parameter - colormodel
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID COLORMODEL SPECIFICATION ---"
					   checkMinus "$1"
					   # test mask values
					   colormodel="$1"
					   [ "$colormodel" != "RGB" -a "$colormodel" != "HSL" -a "$colormodel" != "rgb" -a "$colormodel" != "hsl" ] && errMsg "--- COLORMODEL=$colormodel IS NOT A VALID VALUE ---"
					   ;;
				-f)    # get fact for color boost
					   shift  # to get the next parameter - fact
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID FACT SPECIFICATION ---"
					   checkMinus "$1"
					   # test fact values
					   fact=`expr "$1" : '\([0-9]*\)'`
					   [ "$fact" = "" ] && errMsg "FACT=$fact IS NOT A NON-NEGATIVE INTEGER"
		   			   facttestA=`echo "$fact < 0" | bc`
		   			   facttestB=`echo "$fact > 100" | bc`
					   [ $facttestA -eq 1 -o $facttestB -eq 1 ] && errMsg "--- FACT=$fact MUST BE GREATER THAN OR EQUAL 0 AND LESS THAN OR EQUAL 100 ---"
					   ;;
				-c)    # get contrast
					   shift  # to get the next parameter - contrast
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID CONTRAST SPECIFICATION ---"
					   checkMinus "$1"
					   # test contrast values
					   contrast=`expr "$1" : '\([.0-9]*\)'`
					   [ "$contrast" = "" ] && errMsg "CONTRAST=$contrast IS NOT A NON-NEGATIVE FLOAT"
		   			   contrasttestA=`echo "$contrast <= 0" | bc`
					   [ $contrasttestA -eq 1 ] && errMsg "--- CONTRAST=$contrast MUST BE GREATER THAN 0 ---"
					   ;;
				-b)    # get bright
					   shift  # to get the next parameter - bright
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID BRIGHT SPECIFICATION ---"
					   checkMinus "$1"
					   # test bright values
					   bright=`expr "$1" : '\([0-9]*\)'`
					   [ "$bright" = "" ] && errMsg "BRIGHT=$bright IS NOT A NON-NEGATIVE INTEGER"
		   			   brighttestA=`echo "$bright < 0" | bc`
					   [ $brighttestA -eq 1 ] && errMsg "--- BRIGHT=$bright MUST BE GREATER THAN OR EQUAL 0 ---"
					   ;;
				-s)    # get sat
					   shift  # to get the next parameter - sat
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID SAT SPECIFICATION ---"
					   checkMinus "$1"
					   # test sat values
					   sat=`expr "$1" : '\([0-9]*\)'`
					   [ "$sat" = "" ] && errMsg "SAT=$sat IS NOT A NON-NEGATIVE INTEGER"
		   			   sattestA=`echo "$fact < 0" | bc`
					   [ $sattestA -eq 1 ] && errMsg "--- SAT=$sat MUST BE GREATER THAN OR EQUAL 0 ---"
					   ;;
				-r)    # get res
					   shift  # to get the next parameter - res
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID RES SPECIFICATION ---"
					   checkMinus "$1"
					   # test sat values
					   res=`expr "$1" : '\([0-9]*,[0-9]*,[0-9]*\)'`
					   [ "$res" = "" ] && errMsg "RES=$res IS NOT A SET OF 3 COMMA SEPARATED NON-NEGATIVE INTEGERS"
					   ;;
 				 -)    # STDIN, 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=$sansext"_retinex.jpg"
fi


# test that infile provided
[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED"





# test that outfile provided
# modif  [ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED"







# use pfm so that it works faster in HDRI mode
tmpA="$dir/retinex_$$.mpc"
tmpB="$dir/retinex_$$.cache"
tmpH="$dir/retinex_H_$$.pfm"
tmpS="$dir/retinex_S_$$.pfm"
tmp0="$dir/retinex_0_$$.pfm"
tmp1="$dir/retinex_1_$$.pfm"
tmp2="$dir/retinex_2_$$.pfm"
tmp3="$dir/retinex_3_$$.pfm"
trap "rm -f $tmpA $tmpB $tmpH $tmpS $tmp0 $tmp1 $tmp2 $tmp3; exit 0" 0
trap "rm -f $tmpA $tmpB $tmpH $tmpS $tmp0 $tmp1 $tmp2 $tmp3; 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 retinex
# with IM 6.7.4.10, 6.7.6.10, 6.7.9.1
if [ "$im_version" -lt "06070606" -o "$im_version" -gt "06070707" ]; then
	cspace="RGB"
else
	cspace="sRGB"
fi
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=""
	cspace="sRGB"
fi
#echo "cspace=$cspace"
#echo "setcspace=$setcspace"


if convert -quiet -regard-warnings "$infile" +repage "$tmpA"
	then
	if [ "$colormodel" = "HSL" -o "$colormodel" = "hsl" ]
		then
		# save original
		convert $tmpA $tmp0
		# convert to HSL reusing $tmpA for Lightness
		convert $tmpA $setcspace -colorspace HSL -channel R -separate $tmpH
		convert $tmpA $setcspace -colorspace HSL -channel G -separate $tmpS
		convert $tmpA $setcspace -colorspace HSL -channel B -separate $tmpA
	fi
else
	errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"
fi



if [ "$im_version" -lt "06040201" ]; then 
	echo ""
	echo "Please Wait - It May Take Some Time To Process The Image"
	echo ""
fi

# process image
# get 3 resolution sizes
res1=`echo "$res" | cut -d, -f1`
res2=`echo "$res" | cut -d, -f2`
res3=`echo "$res" | cut -d, -f3`

# evaluate log(2)
log2=`convert xc: -format "%[fx:log(2)]" info:`

# test for hdri enabled
hdri_on=`convert -list configure | grep "enable-hdri"`

if [ "$hdri_on" = "hdri" ]; then
	# use faster methods with hdri enabled
	if [ "$im_version" -lt "06040201" ]; then 
	
		# original fx method
		#convert $tmpA \( +clone -blur 0x$res1 \) -fx "log((u/max(v,.000001))+1)" $tmp1
		#convert $tmpA \( +clone -blur 0x$res2 \) -fx "log((u/max(v,.000001))+1)" $tmp2
		#convert $tmpA \( +clone -blur 0x$res3 \) -fx "log((u/max(v,.000001))+1)" $tmp3
	
	
		# improved fx method
		convert $tmpA \( +clone -blur 0x$res1 -evaluate max 1 \) \
			+swap -compose divide -composite -fx "log(u+1)" $tmp1
		convert $tmpA \( +clone -blur 0x$res2 -evaluate max 1 \) \
			+swap -compose divide -composite -fx "log(u+1)" $tmp2
		convert $tmpA \( +clone -blur 0x$res3 -evaluate max 1 \) \
			+swap -compose divide -composite -fx "log(u+1)" $tmp3
	
	else
		# non-fx method
		convert $tmpA \( +clone -blur 0x$res1 -evaluate max 1 \) \
			+swap -compose divide -composite -evaluate log 1 -evaluate multiply $log2 $tmp1
		convert $tmpA \( +clone -blur 0x$res2 -evaluate max 1 \) \
			+swap -compose divide -composite -evaluate log 1 -evaluate multiply $log2 $tmp2
		convert $tmpA \( +clone -blur 0x$res3 -evaluate max 1 \) \
			+swap -compose divide -composite -evaluate log 1 -evaluate multiply $log2 $tmp3
	fi

else
	# process image
	echo ""
	echo "Please Wait - It May Take Some Time To Process The Image"
	echo ""
	
	# use original fx method
	convert $tmpA \( +clone -blur 0x$res1 \) -fx "log((u/max(v,.000001))+1)" $tmp1
	convert $tmpA \( +clone -blur 0x$res2 \) -fx "log((u/max(v,.000001))+1)" $tmp2
	convert $tmpA \( +clone -blur 0x$res3 \) -fx "log((u/max(v,.000001))+1)" $tmp3
fi

# average results (normalize as log reduces range of values)
if [ "$im_version" -ge "06060904" ]; then 
	convert $tmp1 $tmp2 $tmp3 -evaluate-sequence mean $setcspace -normalize $tmp1
else
	convert $tmp1 $tmp2 $tmp3 -average -normalize $tmp1
fi


# convert back to RGB
if [ "$colormodel" = "HSL" -o "$colormodel" = "hsl" ]
	then	
	convert $tmp0 -colorspace HSL \
		$tmpH -compose CopyRed -composite \
		$tmpS -compose CopyGreen -composite \
		$tmp1 -compose CopyBlue -composite \
		-colorspace $cspace $tmp1
fi

# color boost blend only if needed
if [ "$hdri_on" != "" ]; then
	# use faster methods with hdri enabled
	if [ $fact -ne 0 ]
		then
		# create recolored image (normalize as log reduces range of values)
		if [ "$colormodel" = "HSL" -o "$colormodel" = "hsl" ]; then	
			if [ "$im_version" -lt "06040201" ]; then 
				# convert $tmp0 \( +clone $setcspace -colorspace Gray \) -fx "log((u/max(v,.000001))+1)" $tmp2
				convert $tmp0 \( +clone $setcspace -colorspace Gray -evaluate max 1 \) \
					+swap $setcspace -compose divide -composite -fx "log(u+1)" $tmp2
			else
				convert $tmp0 \( +clone $setcspace -colorspace Gray -evaluate max 1 \) \
					+swap $setcspace -compose divide -composite -evaluate log 1 -evaluate multiply $log2 $tmp2
			fi
		else
			if [ "$im_version" -lt "06040201" ]; then 
				# convert $tmpA \( +clone $setcspace -colorspace Gray \) -fx "log((u/max(v,.000001))+1)" $tmp2
				convert $tmpA \( +clone $setcspace -colorspace Gray -evaluate max 1 \) \
					+swap $setcspace -compose divide -composite -fx "log(u+1)" $tmp2
			else
				convert $tmpA \( +clone $setcspace -colorspace Gray -evaluate max 1 \) \
					+swap $setcspace -compose divide -composite -evaluate log 1 -evaluate multiply $log2 $tmp2
			fi
		fi
		convert $tmp2 $tmp1 $setcspace -compose multiply -composite -normalize $tmp3
	
		# blend recolored with unrecolored	
		if [ "$im_version" -lt "06050304" ]; then
			composite -blend $fact% $tmp3 $tmp1 $tmp1
		else
			convert $tmp1 $tmp3 -define compose:args=$fact% -compose blend -composite $tmp1
		fi
	
	fi
else
	# use original fx method
	if [ $fact -ne 0 ]
		then
		# create recolored image (normalize as log reduces range of values)
		if [ "$colormodel" = "HSL" -o "$colormodel" = "hsl" ]
			then	
			convert $tmp0 \( +clone $setcspace -colorspace Gray \) -fx "log((u/max(v,.000001))+1)" $tmp2
		else
			convert $tmpA \( +clone $setcspace -colorspace Gray \) -fx "log((u/max(v,.000001))+1)" $tmp2
		fi
	
		convert $tmp2 $tmp1 $setcspace -compose multiply -composite -normalize $tmp3
	
		# blend recolored with unrecolored	
		if [ "$im_version" -lt "06050304" ]; then
			composite -blend $fact% $tmp3 $tmp1 $tmp1
		else
			convert $tmp1 $tmp3 -define compose:args=$fact% -compose blend -composite $tmp1
		fi
	fi
fi

if [ $bright -ne 100 -o $sat -ne 100 ]
	then
	modulate="-modulate ${bright},${sat}"
else
	modulate=""
fi

if [ `echo "$contrast != 1.0" | bc` -eq 1 ]
	then
	gamma="-gamma $contrast"
else
	gamma=""
fi

convert $tmp1 $gamma $modulate $sansext"_retinex.jpg"


# exit 0







############################################################!/bin/bash
#
# Developed by Fred Weinhaus 8/29/2012 .......... revised 9/23/2013
#
# USAGE: autotone [-b] [-g] [-w] [-G] [-n] [-s] [-p] [-R resize] [-P percent] 
# [-M midrange] [-N noise] [-S sharp] [-WN whitenorm] [-GN graynorm] 
# infile outfile
#
# USAGE: autotone [-h or -help]
#
# OPTIONS:
#
# -b                      disable auto brightness/contrast
# -g                      disable auto gray balance
# -w                      disable auto white balance
# -G                      disable auto gamma correction
# -n                      disable auto noise removal
# -s                      disable auto sharpening
# -p                      enable progress monitoring
# -R      resize          limit the output size to this value; integer>0; 
#                         will resize if the larger dimension of the image 
#                         exceeds this value
# -P      percent         percent threshold for detecting gray/white 
#                         for auto gray and auto white balance; 0<float<100;
#                         default=1
# -M      midrange        midrange value for auto gamma correction;
#                         0<float<1; default=0.425
# -N      noise           noise removal factor; integer>0; default is 
#                         automatically computed; range is about 1 to 4
# -S      sharp           sharpening amount; float>=0; default is 
#                         automatically computed; range is about 1 to 4
# -WN     whitenorm       white balance normalization method; choices are: 
#                         none (n), ave (a), max (m); default=none
# -GN     graynorm        gray balance normalization method; choices are: 
#                         none (n), ave (a), max (m); default=none
#
###
#
# NAME: AUTOTONE 
# 
# PURPOSE: To automatically tone balance an image.
# 
# DESCRIPTION: AUTOTONE attempts to automatically tone balance and image. This 
# includes auto brightness/contrast adjustment, auto gray balance, auto white 
# balance, auto gamma correction, auto noise removal and auto sharpening.
# 
# OPTIONS: 
# 
# -b ... disable auto brightness/contrast
# 
# -g ... disable auto gray balance
# 
# -w ... disable auto white balance
# 
# -G ... disable auto gamma correction
# 
# -n ... disable auto noise removal
# 
# -s ... disable auto sharpening
# 
# -r ...  enable white and gray balance r,g,b ratio normalization
# 
# -p ... enable progress monitoring
# 
# -R resize ... RESIZE is the limit on the output image size. Values are 
# integer>0. This will only resize if the larger dimension of the image 
# exceeds this value. The default is no limit (i.e. no resize).
# 
# -P percent ... PERCENT is the percent threshold for detecting gray/white 
# in the image for auto gray and auto white balance. Values are 0<floats<100.
# The default=1.
# 
# -M midrange ... MIDRANGE value for auto gamma correction. Values are 
# 0<floats<1. The default=0.425.
# 
# -N (noise) repeats ... NOISE REPEATS removal factor. Values are integers>0.  
# The default is automatically computed. The nominal range is about 1 to 4 
# depending upon image size. Larger values are used for larger images.
# 
# -S sharp ... SHARP is the sharpening amount. Values are floats>=0. The 
# default is automatically computed. The nominal range is about 1 to 4. 
# Larger values are used for larger images.
# 
# -WN whitenorm ... WHITENORM is the white balance r,g,b ratio normalization 
# method. The choices are: none (n), ave (a), max (m). The default=none
# 
# -GN graynorm ... GRAYNORM is the gray balance r,g,b ratio normalization  
# method. The choices are: none (n), ave (a), max (m). The default=none
# 
# NOTE: The white balance and gray balance techniques now have optional r,g,b
# ratio normalizations that help prevent brightness increases and 
# over-saturation at white from these parts of the script. Furthermore, to
# avoid possible excess color shifts as well as mitigate over-saturation of
# white, one may reduce the percent argument to about 0.1. All of the above
# as well as the midrange argument are subject to personal tastes and tuning.
# The defaults have been left in the old mode for backward compatibility. 
# I thank Dr. Guenter Grau for the suggestion to normalize the r,g,b ratios 
# in the white balance and gray balance parts of the code. My current 
# recommendation is to use whitenorm=ave, graynorm=ave and percent=0.1.
# 
# 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. 
# 
######
#

# on/off parameters and resize input
resize=""  			#resizes input if larger than this; pixel dimension or "" for no resize
bright="yes"		#auto brighten; yes/no
gray="yes"			#auto gray balance; yes/no
white="yes"			#auto white balance; yes/no
gamma="yes"			#auto gamma; yes/no
noise="yes"			#auto noise removal; yes/no
sharpen="yes"		#auto sharpen; yes/no
progress="false"	#echo progress comments

# tuning parameters
percent=1			# percent near white from combined S and B channels of HSB to use for whitebalance
gammamid=0.425		# midrange for -gamma processing
skythresh=80		# percent threshold to isolate sky for gamma processing from combination of S and B channels of HSB
skyheight=25		# percent of height at top of image considered as sky
maskthresh=0.125	# threshold on sky mask to continue processing for sky; process only if larger than threshold; avoids compensation for too little sky
repeats=""			# denoise -enhance repeats; "" means autocompute the repeats; repeats>0
gain=0.75			# sharpening gain/attenuation factor; >1 is gain; <1 is attenuate; applied towards the automatic amount
whitenorm="none"	# white balance rgb ratio normalization; none, ave, max.
graynorm="none"		# gray balance rgb ratio normalization; none, ave, max.

# 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 -n '/^###/q;  /^#/!q;  s/^#//;  s/^ //;  4,$p' "$PROGDIR/$PROGNAME"
	}
usage2() 
	{
	echo >&2 ""
	echo >&2 "$PROGNAME:" "$@"
	sed >&2 -n '/^######/q;  /^#/!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 23 ]
	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
					   ;;
				-b)    # disable auto brightness/contrast
					   bright="no"
					   ;;
				-g)    # disable auto gray balance
					   gray="no"
					   ;;
				-w)    # disable auto white balance
					   white="no"
					   ;;
				-G)    # disable auto gamma correction
					   gamma="no"
					   ;;
				-n)    # disable auto noise removal
					   noise="no"
					   ;;
				-s)    # disable auto sharpening
					   sharp="no"
					   ;;
				-p)    # enable progress monitoring
					   progress="true"
					   ;;
				-R)    # get resize
					   shift  # to get the next parameter 
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID RESIZE SPECIFICATION ---"
					   checkMinus "$1"
					   resize=`expr "$1" : '\([0-9]*\)'`
					   [ "$resize" = "" ] && errMsg "--- RESIZE=$resize MUST BE A NON-NEGATIVE INTEGER ---"
					   testA=`echo "$resize <= 0" | bc`
					   [ $testA -eq 1 ] && errMsg "--- RESIZE=$resize MUST BE AN INTEGER GREATER THAN 0 ---"
					   ;;
				-P)    # get percent
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID PERCENT SPECIFICATION ---"
					   checkMinus "$1"
					   percent=`expr "$1" : '\([.0-9]*\)'`
					   [ "$percent" = "" ] && errMsg "--- PERCENT=$percent MUST BE A NON-NEGATIVE FLOAT ---"
					   testA=`echo "$percent <= 0" | bc`
					   testB=`echo "$percent >= 100" | bc`
					   [ $testA -eq 1 -o $testB -eq 1 ] && errMsg "--- PERCENT=$percent MUST BE A FLOAT GREATER THAN 0 AND SMALLER THAN 100 ---"
					   ;;
				-M)    # get midrange
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID MIDRANGE SPECIFICATION ---"
					   checkMinus "$1"
					   gammamid=`expr "$1" : '\([.0-9]*\)'`
					   [ "$gammamid" = "" ] && errMsg "--- MIDRANGE=$gammamid MUST BE A NON-NEGATIVE FLOAT ---"
					   testA=`echo "$gammamid <= 0" | bc`
					   testB=`echo "$gammamid >= 1" | bc`
					   [ $testA -eq 1 -o $testB -eq 1 ] && errMsg "--- MIDRANGE=$gammamid MUST BE A FLOAT GREATER THAN 0 AND SMALLER THAN 1 ---"
					   ;;
				-N)    # get noise repeats
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID NOISE REPEATS SPECIFICATION ---"
					   checkMinus "$1"
					   repeats=`expr "$1" : '\([0-9]*\)'`
					   [ "$repeats" = "" ] && errMsg "--- NOISE REPEATS=$repeats MUST BE A NON-NEGATIVE INTEGER ---"
					   testA=`echo "$repeats <= 0" | bc`
					   [ $testA -eq 1 ] && errMsg "--- NOISE REPEATs=$repeats MUST BE AN INTEGER GREATER THAN 0 ---"
					   ;;
				-S)    # get sharp
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID SHARP SPECIFICATION ---"
					   checkMinus "$1"
					   sharp=`expr "$1" : '\([.0-9]*\)'`
					   [ "$sharp" = "" ] && errMsg "--- SHARP=$sharp MUST BE A NON-NEGATIVE FLOAT ---"
					   ;;
			   -WN)    # get  whitenorm
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID WHITENORM SPECIFICATION ---"
					   checkMinus "$1"
					   whitenorm=`echo "$1" | tr '[A-Z]' '[a-z]'`
					   case "$whitenorm" in 
					   		none|n) whitenorm="none";;
					   		ave|a) whitenorm="ave";;
					   		max|m) whitenorm="max";;
					   		*) errMsg "--- WHITENORM=$whitenorm IS AN INVALID VALUE ---" 
					   	esac
					   ;;
			   -GN)    # get  graynorm
					   shift  # to get the next parameter
					   # test if parameter starts with minus sign 
					   errorMsg="--- INVALID GRAYNORM SPECIFICATION ---"
					   checkMinus "$1"
					   graynorm=`echo "$1" | tr '[A-Z]' '[a-z]'`
					   case "$graynorm" in 
					   		none|n) graynorm="none";;
					   		ave|a) graynorm="ave";;
					   		max|m) graynorm="max";;
					   		*) errMsg "--- GRAYNORM=$graynorm 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 infile and outfile
	infile=$sansext"_retinex.jpg"
	outfile=$2
fi

# test that infile provided
[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED"


# test that outfile provided
# modif [ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED"



# set directory for temporary files
tmpdir="." # suggestions are dir="." or dir="/tmp"

dir="$tmpdir/AUTOTONE.$$"

mkdir "$dir" || errMsg "--- FAILED TO CREATE TEMPORARY FILE DIRECTORY ---"
trap "rm -rf $dir; exit 0" 0
trap "rm -rf $dir; exit 1" 1 2 3 15

# Process Resize
if [ "$resize" != "" ]; then
	resizing="-resize $resize>"
else
	resizing=""
fi

# read input image into temporary memory mapped (mpc) format image
convert -quiet -regard-warnings "$infile" +repage $resizing $dir/tmpI.mpc ||
	echo  "--- FILE $thefile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE  ---"

# get max dimension
dim=`convert $dir/tmpI.mpc -format "%[fx:max(w,h)]" info:`
#echo "$dim"

# set up to reduce image size if larger than 1000 pixels for certain processes to speed them up
if [ $dim -gt 1000 ]; then
	reducing="-resize 25%"
else
	reducing=""
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`

# 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 
# with IM 6.7.4.10, 6.7.6.10, 6.7.8.6
if [ "$im_version" -lt "06070607" -o "$im_version" -gt "06070707" ]; then
	setcspace="-set colorspace RGB"
else
	setcspace=""
fi
if [ "$im_version" -lt "06070607" ]; then
	cspace="RGB"
else
	cspace="sRGB"
fi
# no need for setcspace for grayscale or channels after 6.8.5.4
if [ "$im_version" -gt "06080504" ]; then
	setcspace=""
	cspace="sRGB"
fi


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
	}

# function to get mean of image
getMean()
	{
	img="$1"
	if [ "$im_version" -ge "06030901" ]
		then 
		mean=`convert $img -format "%[mean]" info:`
		mean=`convert xc: -format "%[fx:$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:$mean]" info:`
	fi
	}

# function to get color ratios for white balancing
getRatio()
	{
	getMean "$1"
	ref="$2"
	# get ave in range 0-100
	# note both mean and mask_mean are in range 0-100
	# note average of just near_gray values mean of masked image divided by
	# the fraction of white pixels (from mask)
	# which is the mean in range 0 to 1 divided by 100
	ave=`convert xc: -format "%[fx:$mean/$maskmean]" info:`
	[ "$ave" = "0" -o "$ave" = "0.0" ] && ave=100
	ratio=`convert xc: -format "%[fx:$ref/$ave]" info:`
	}


# set up -recolor or -color-matrix for color balancing
if [ "$im_version" -lt "06060100" ]; then
	process="-recolor"
else
	process="-color-matrix"
fi


# Process Brightness
if [ "$bright" = "yes" ]; then
	$progress && echo "brighten"
	if [ "$im_version" -lt "06050501" ]; then
		getMinMax $dir/tmpI.mpc
		convert $dir/tmpI.mpc -level $min%,$max% $dir/tmpI.mpc
	else 
		convert $dir/tmpI.mpc -auto-level $dir/tmpI.mpc
	fi
fi


# Process graybalance
if [ "$gray" = "yes" ]; then
	$progress && echo "graybalance"

	# separate channels
	convert $dir/tmpI.mpc $setcspace $reducing -channel RGB -separate $dir/tmpRGB.mpc
	
	# get ratios for graybalance
	# get mask of top percent closest to gray
	# approximation using negated saturation and solarized brightness multiplied
	convert $dir/tmpI.mpc $setcspace $reducing \
		\( -clone 0 -colorspace HSB -channel G -negate -separate +channel \) \
		\( -clone 0 -colorspace HSB -channel B -separate +channel -solarize 50% -level 0x50% \) \
		\( -clone 1 -clone 2 -compose multiply -composite \) \
		\( -clone 3 -contrast-stretch 0,${percent}% -fill black +opaque white \) \
		-delete 0-3 $dir/tmpM.mpc

	
	# get mean of mask
	getMean $dir/tmpM.mpc
	maskmean=$mean
	#echo "maskmean=$maskmean"
	
	# use mask image to isolate user supplied percent of pixels closest to white
	# then get ave graylevel for each channel of mask selected pixels
	
	convert $dir/tmpRGB.mpc $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	
	convert $dir/tmpRGB.mpc[0] $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	getRatio "$dir/tmpT.mpc[0]" "0.5"
	redave=$ave
	redratio=$ratio
	#echo "R: ave=$ave; redratio=$ratio"
	
	convert $dir/tmpRGB.mpc[1] $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	getRatio "$dir/tmpT.mpc[1]" "0.5"
	greenave=$ave
	greenratio=$ratio
	#echo "G: ave=$ave; greenratio=$ratio;"
	
	convert $dir/tmpRGB.mpc[2] $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	getRatio "$dir/tmpT.mpc[2]" "0.5"
	blueave=$ave
	blueratio=$ratio
	#echo "B: ave=$ave; blueratio=$ratio;"

	# normalize r,g,b ratios by maximum ratio, so no added increase in brightness
	if [ "$graynorm" != "none" ]; then
		if [ "$graynorm" = "ave" ]; then
			gnormfact=`convert xc: -format "%[fx: ($blueratio+$greenratio+$redratio)/3]" info:`
		elif [ "$graynorm" = "max" ]; then
			gnormfact=`convert xc: -format "%[fx: max(max($blueratio,$greenratio),$redratio)]" info:`
		fi
		redratio=`convert xc: -format "%[fx: $redratio/$gnormfact]" info:`
		greenratio=`convert xc: -format "%[fx: $greenratio/$gnormfact]" info:`
		blueratio=`convert xc: -format "%[fx: $blueratio/$gnormfact]" info:`
		#echo "R: ave=$redave; ratio=$redratio"
		#echo "G: ave=$greenave; ratio=$greenratio;"
		#echo "B: ave=$blueave; ratio=$blueratio;"
	fi
		
	#unused test
	#rmse=`convert xc: -format "%[fx:sqrt( (0.5-$redave)^2 + (0.5-$greenave)^2 + (0.5-$blueave)^2 )/3]" info:`
	#test1=`convert xc: -format "%[fx:$rmse>0?1:0]" info:`
	#echo "rmse=$rmse; test1=$test1"
	test1=1
	
	if [ $test1 -eq 1 ]; then 
		convert $dir/tmpI.mpc $process "$redratio 0 0 0 $greenratio 0 0 0 $blueratio" $dir/tmpI.mpc
	fi
fi

# Process whitebalance
if [ "$white" = "yes" ]; then
	$progress && echo "whitebalance"

	# separate channels
	convert $dir/tmpI.mpc $setcspace $reducing -channel RGB -separate $dir/tmpRGB.mpc
	
	# get ratios for whitebalance
	# get mask of top percent closest to white
	# approximation using negated saturation and brightness channels multiplied
	convert $dir/tmpI.mpc $setcspace $reducing \
		-colorspace HSB -channel G -negate -channel GB -separate +channel \
		-compose multiply -composite \
		-contrast-stretch 0,${percent}% -fill black +opaque white \
		$dir/tmpM.mpc
	
	# get mean of mask
	getMean $dir/tmpM.mpc
	maskmean=$mean
	#echo "maskmean=$maskmean"
	
	# use mask image to isolate user supplied percent of pixels closest to white
	# then get ave graylevel for each channel of mask selected pixels
	
	convert $dir/tmpRGB.mpc $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	
	convert $dir/tmpRGB.mpc[0] $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	getRatio "$dir/tmpT.mpc[0]" "1"
	redave=$ave
	redratio=$ratio
	#echo "R: ave=$ave; redratio=$ratio"
	
	convert $dir/tmpRGB.mpc[1] $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	getRatio "$dir/tmpT.mpc[1]" "1"
	greenave=$ave
	greenratio=$ratio
	#echo "G: ave=$ave; greenratio=$ratio;"
	
	convert $dir/tmpRGB.mpc[2] $dir/tmpM.mpc -compose multiply -composite $dir/tmpT.mpc
	getRatio "$dir/tmpT.mpc[2]" "1"
	blueave=$ave
	blueratio=$ratio
	#echo "B: ave=$ave; blueratio=$ratio;"

	# normalize r,g,b ratios by maximum ratio, so no added increase in brightness
	if [ "$whitenorm" != "none" ]; then
		if [ "$whitenorm" = "ave" ]; then
			wnormfact=`convert xc: -format "%[fx: ($blueratio+$greenratio+$redratio)/3]" info:`
		elif [ "$whitenorm" = "max" ]; then
			wnormfact=`convert xc: -format "%[fx: max(max($blueratio,$greenratio),$redratio)]" info:`
		fi
		redratio=`convert xc: -format "%[fx: $redratio/$wnormfact]" info:`
		greenratio=`convert xc: -format "%[fx: $greenratio/$wnormfact]" info:`
		blueratio=`convert xc: -format "%[fx: $blueratio/$wnormfact]" info:`
		#echo "R: ave=$redave; ratio=$redratio"
		#echo "G: ave=$greenave; ratio=$greenratio;"
		#echo "B: ave=$blueave; ratio=$blueratio;"
	fi
	
	#unused test
	#rmse=`convert xc: -format "%[fx:sqrt( (1-$redave)^2 + (1-$greenave)^2 + (1-$blueave)^2 )/3]" info:`
	#test1=`convert xc: -format "%[fx:$rmse>0?1:0]" info:`
	#echo "rmse=$rmse; test1=$test1"
	test1=1
	
	if [ $test1 -eq 1 ]; then 
		convert $dir/tmpI.mpc $process "$redratio 0 0 0 $greenratio 0 0 0 $blueratio" $dir/tmpI.mpc
	fi
fi


# Process Gamma
if [ "$gamma" != "no" ]; then
	$progress && echo "gamma"
	# get gammaval for -gamma processing
	# gamma is the ratio of logs of the mean and mid value of the dynamic range
	# where we normalize both to the range between 0 and 1
	# ref: http://rsb.info.nih.gov/ij/plugins/auto-gamma.html
	# However, I have inverted his formula for use with values 
	# in range 0 to 1, which works much better my way
	
	# reduce image and get mean
	convert $dir/tmpI.mpc $setcspace $reducing $dir/tmpT.mpc
	getMean "$dir/tmpT.mpc"
	gmean=$mean
	#echo "gmean=$gmean;"

	# test for mask processing of upper portion of negated saturation * brightness 
	# to remove too much white from affecting the gamma processing
	
	# process only if gmean>gammamid; ie. overly bright image mean
	test1=`convert xc: -format "%[fx:$gmean>$gammamid?1:0]" info:`
	#echo "test1=$test1;"
	if [ $test1 -eq 1 ]; then
		# get mask from saturation and brightness channels
		convert $dir/tmpT.mpc -colorspace HSB -channel G -negate -channel GB -separate +channel \
			-compose multiply -composite -threshold $skythresh% $dir/tmpM.mpc

		# get mean of mask
		getMean "$dir/tmpM.mpc"
		maskmean=$mean
		
		# Process only if maskmean is larger than some threshold -- ie don't process if too little sky
		test2=`convert xc: -format "%[fx:$maskmean>$maskthresh?1:0 ]" info:`
		#echo "maskmean=$maskmean; test2=$test2"
		if [ $test2 -eq 1 ]; then
			# get dimensions
			ww=`identify -ping -format "%w" $dir/tmpM.mpc`
			hh=`identify -ping -format "%h" $dir/tmpM.mpc`
			hhh=`convert xc: -format "%[fx:round($skyheight*$hh)]" info:`
			convert \( -size ${ww}x${hh} xc:black \) \( -size ${ww}x${hhh} xc:white \) \
				-compose over -composite $dir/tmpM.mpc -compose multiply -composite -negate $dir/tmpM.mpc

			# get mean of mask
			getMean "$dir/tmpM.mpc"
			maskmean=$mean
			# compute new mean of masked image
			newmean=`convert $dir/tmpT.mpc $dir/tmpM.mpc -compose multiply -composite -format "%[fx:mean]" info:`
			gmean=`convert xc: -format "%[fx:$newmean/$maskmean]" info:`
		fi
	fi

	gammaval=`convert xc: -format "%[fx:log($gmean)/log($gammamid)]" info:`
	convert "$dir/tmpI.mpc" -gamma $gammaval "$dir/tmpI.mpc"
	#echo "gmean=$gmean; gammaval=$gammaval;"
fi


# Process Denoise
if [ "$noise" = "yes" ]; then
	$progress && echo "denoise"
	if [ "$repeats" = "" ]; then
		repeats=`convert xc: -format "%[fx:max(1,floor($dim/1000))]" info:`
	fi
	denoise=""
	for ((i=0;i<repeats;i++)) do
		denoise="$denoise -enhance"
	done
	convert $dir/tmpI.mpc $denoise $dir/tmpI.mpc
	#echo "denoise=$denoise"
fi


# Process Sharpening
if [ "$sharpen" = "yes" ]; then
	$progress && echo "sharpen"
	# get sharpening amount
	sharp=`convert $dir/tmpI.mpc -format "%[fx:max(0.5,$gain*$dim/1000)]" info:`
	#echo "sharp=$sharp"
	convert $dir/tmpI.mpc -unsharp 0x$sharp $dir/tmpI.mpc
fi

# Save to Output
convert $dir/tmpI.mpc $sansext"_retinex-autotone.jpg"


##################### modif#####################################
# http://www.sno.phy.queensu.ca/~phil/exiftool/exiftool_pod.html
#exiftool -tagsfromfile "$1" -all:all "$1_autotone.jpg"

# ko -o = Combining the -overwrite_original option with -o causes the original source file to be erased after the output file is successfully written
# ok   /usr/bin/exiftool -tagsfromfile $initial -all:all $initial"_retinex.jpg"
/usr/bin/exiftool -tagsfromfile $initial -all:all $sansext"_retinex.jpg"


##################### modif ###################################
# http://www.sno.phy.queensu.ca/~phil/exiftool/exiftool_pod.html
#exiftool -tagsfromfile "$1" -all:all "$1_autotone.jpg"

# ko -o = Combining the -overwrite_original option with -o causes the original source file to be erased after the output file is successfully written
/usr/bin/exiftool -tagsfromfile $initial -all:all $sansext"_retinex-autotone.jpg"


# efface fics exiftool d'origine
rm $sansext"_retinex.jpg_original"
rm $sansext"_retinex-autotone.jpg_original"


# Rebuild thumbnail
#Thumbnail length	7952	
convert $sansext"_retinex.jpg" -thumbnail 150 x 150 -strip -quality 80 jpg:- | exiftool "-ThumbnailImage<=-" -m -overwrite_original $sansext"_retinex.jpg"

# Rebuild thumbnail
#Thumbnail length	7952	
convert $sansext"_retinex-autotone.jpg" -thumbnail 150 x 150 -strip -quality 80 jpg:- | exiftool "-ThumbnailImage<=-" -m -overwrite_original $sansext"_retinex-autotone.jpg"

exit 0

... Or to extract metadata from files (here to: .txt, .html and .mie files):

Code: Select all

#!/bin/bash

sansext=`echo $1 | sed 's/\.*[a-z]*$//'`

################# ##########################
# extract exif to mie file (save exif data from image original)


############## .mie file ##############################
/usr/bin/exiftool -TagsFromFile $1 -all:all $sansext".mie"


############## txt file ##############################
exiftool -a -U -g1 -H $1 -w __00.txt


############## html file ##############################
exiftool -a -U -g1 -H -h $1 -w __00.html


exit 0
etc...
XnViewMP Linux X64 - Debian - X64
User avatar
m.Th.
XnThusiast
Posts: 1663
Joined: Wed Aug 16, 2006 6:31 am
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by m.Th. »

Basically you want to transform the XnView MP to a front-end for ImageMagick (and not only).

Well, this is theoretically very ok to me, but I see a very sensible point: How to properly insert the current file name.

This thing isn't so difficult to implement, isn't so difficult to make a GUI for it, is very sensible to design correctly.

Let's say that "the GUI" is a simple memo/edit box in which one copy/pastes the script.

And now see the sentences bellow:

Because on Windows we pass the parameters in scripts with % in front (%1, %2 etc.), perhaps the best solution...
...but XnView MP has already in place a macro engine with { } separators.

Ok, the first macro would be {filename} which is very nice...
...but...
...what if we want to use a script to convert the files files to eg. 'png' format?
We'll have some form of "ncovert {filename} -c {filenamewithoutextension}.PNG" ??

...what if we want to use a script to do some processing and put the results in another directory?
We'll have some form of "ncovert {filename} -c F:\result\{filenamewithoutpathandwithoutextension}.PNG" ??
And then, what will be the purpose of the...
...destination directory in the GUI? Perhaps we need a macro also for this one?
...output file format in GUI? Another macro?

...how do you think that the program must handle the following series of actions:

Resize (take the original image and resize it - ok so far)
Custom Script (can do anything and put a file in an unknown place with an unknown file name)
Rotate (or any other processing - this will process/rotate... ...what?)

...also istm that the thing will be very slow because a clean command line batch runs/loads in the memory the imagemagick.exe just once for all files, but with your approach it will (un)load every time for every file.

Don't you think that a much better solution would be an enhanced Tool | Open With... engine?
m. Th.

- Dark Themed XnViewMP 1.6 64bit on Win11 x64 -
User avatar
m.Th.
XnThusiast
Posts: 1663
Joined: Wed Aug 16, 2006 6:31 am
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by m.Th. »

...or perhaps to incorporate the Retinex code and/or other interesting code from ImageMagick in XnView either directly either via a plug-in system?
m. Th.

- Dark Themed XnViewMP 1.6 64bit on Win11 x64 -
User avatar
oops66
XnThusiast
Posts: 2005
Joined: Tue Jul 17, 2007 1:17 am
Location: France

Re: Add the ability to launch into "Batch Convert" custom sc

Post by oops66 »

m.Th. wrote:Basically you want to transform the XnView MP to a front-end for ImageMagick (and not only). ...
Hello, ... yes, i already do that via "LMC\context menu\open with...\my_custom_script.sh and that works* (*but only file by file with the $1 parameter)... so my request for a recursive behavior for a single scrit.sh under XnViewMP "Batch Converter". ;-)
For example to split/tile an image and recompose it after:
http://www.imagemagick.org/Usage/montage/#tile

Code: Select all

#!/bin/sh
#---------------------------------------
# enlève la derniere extention ex : fic.txt.jpg = fic.txt
# ok    sansext=`echo $1 | sed 's/\.*[a-z]*$//'`
sansext=`echo $1 | sed 's/\.*[a-z]*$//'`

# www.imagemagick.org/Usage/montage/
TILESW=8
TILESH=6

#http://michael.otacoo.com/linux-2/split-an-image-file-into-tiles/
#Split an image file into tiles
convert -crop $TILESW"x"$TILESH"@" $1 $sansext"_tile_"%03d.jpg | convert $1 -thumbnail 150 x 150 -strip -quality 80 jpg:- | exiftool "-ThumbnailImage<=-" -m -overwrite_original $1

#*************** then to rebuild the image ***************************************************************
# montage -mode concatenate -tile 8x6 -geometry +0+0 lioncuddle1_tile_*.jpg out4.jpg ; exiftool -overwrite_original -tagsfromfile lioncuddle1.jpg -all:all out4.jpg ;convert out4.jpg -thumbnail 150 x 150 -strip -quality 80 jpg:- | exiftool "-ThumbnailImage<=-" -m -overwrite_original out4.jpg
# or
# montage -mode concatenate -tile 8x6 -geometry +1+1 lioncuddle1_tile_*.jpg out5.jpg ; exiftool -overwrite_original -tagsfromfile lioncuddle1.jpg -all:all out5.jpg ;convert out5.jpg -thumbnail 150 x 150 -strip -quality 80 jpg:- | exiftool "-ThumbnailImage<=-" -m -overwrite_original out5.jpg
exit 0
Or to make 8 photo ID from a single one:

Code: Select all

#!/bin/sh
# http://www.commandlinefu.com/commands/tagged/41/imagemagick/25
 
sansext=`echo $1 | sed 's/\.*[a-z]*$//'`

# Repeat a portrait eight times so it can be cut out from a 6"x4" photo and used for visa or passport photos
#Yes, You could do it in the GIMP or even use Inkscape to 
#montage 2007-08-25-3685.jpg +clone -clone 0-1 -clone 0-3 -geometry 500 -frame 5 output.jpg

#NOTE: The +clone and -clone options are just to shorten the command line instead of typing the same filename eight times. It might also speed up the montage by only processing the image once, but I'm not sure. "+clone" duplicates the previous image, the following two "-clone"s duplicate the first two and then the first four images.
#NOTE2: The -frame option is just so that I have some lines to cut along.
#BUG: I haven't bothered to calculate the exact geometry (width and height) of each image since that was not critical for the visa photos I need. If it matters for you, it should be easy enough to set using the -geometry flag near the end of the command. For example, if you have your DPI set to 600, you could use "-geometry 800x1200!" to make each subimage 1⅓ x 2 inches. You may want to use ImageMagick's "-density 600" option to put a flag in the JPEG file cuing the printer that it is a 600 DPI image.
#BUG2: ImageMagick does not autorotate images based on the EXIF information. Since the portrait photo was taken with the camera sideways, I made the JPEG rotate using jhead like so: jhead -autorot 2007-08-25-3685.jpg

montage $1 +clone -clone 0-1 -clone 0-3 -geometry 500 -frame 5 $sansext"_photo-id.jpg"
exit 0
m.Th. wrote:...or perhaps to incorporate the Retinex code and/or other interesting code from ImageMagick in XnView either directly either via a plug-in system?
... Why not (but it's a big work for Pierre), but also the ability to use custom batch_scripts files recursively. (of course with a custom script, some restrictions exist, files formats , exif data, etc... and it can be dangerous too ) ... FI: I mostly use .jpg file into my custom scripts
...also istm that the thing will be very slow because a clean command line batch runs/loads in the memory the imagemagick.exe just once for all files, but with your approach it will (un)load every time for every file.
... Yes but custom scripts are basically used in rares/custom circumstances ...
Don't you think that a much better solution would be an enhanced Tool | Open With... engine?
... Why not too may be it is the best solution, ... the main goal is to have the ability to launch a batch custom file script recursively ;-) ... or maybe under Batch Convert? because in this case, you can cumulate with some other filters/functions before only (not after) this custom one ;-) (of course without the possibility of a preview (before/after) in case of custom script)
XnViewMP Linux X64 - Debian - X64
User avatar
m.Th.
XnThusiast
Posts: 1663
Joined: Wed Aug 16, 2006 6:31 am
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by m.Th. »

By "recursive" do you mean "serial" mode of calling, isn't it?

"Recursive" means that XnView is calling myScript.cmd and after that myScript.cmd is calling myScript.cmd which is calling myScript.cmd which is calling myScript.cmd which is calling myScript.cmd...

"Serial" means that for Img_001.jpg XnViewMP calls "myScript.cmd img_001.jpg and waits for the script to finish and after that for Img_002.jpg XnView calls "myScript.cmd img_002.jpg and waits for the script to finish and after that for Img_003.jpg XnView calls "myScript.cmd img_003.jpg and waits for the script to finish and after that for Img_004.jpg XnView calls "myScript.cmd img_004.jpg and waits for the script to finish...

"Parallel" means that XnViewMP calls simultaneously "myScript.cmd img_001.jpg", "myScript.cmd img_002.jpg", "myScript.cmd img_003.jpg", "myScript.cmd img_004.jpg"...

That said:

The Batch Convert works "Serial"
The Open With... works "Parallel" - so you can use Open With... with multiple files - just checked - however isn't the best approach because it can bring down your PC or generate various conflicts etc.

So, as you say, perhaps the best solution would be (besides having certain code inside) a Batch Convert Action like this one:
Script Action.png
Script Action.png (17.05 KiB) Viewed 4773 times
Which will do the following:

1. Save the image from the memory in the state in which it reached in the Batch Convert pipeline to a temporary file with the format specified in the dialog above

2. Run the command with the full file name as argument (don't forget to quote it) and wait for the script/process to finish
- It is the script responsibility to handle any other parameters / processing etc. as well as saving back the changes to the temporary file

3. If the temporary file is changed (and still exists, of course), reload it in the image from memory.
m. Th.

- Dark Themed XnViewMP 1.6 64bit on Win11 x64 -
User avatar
oops66
XnThusiast
Posts: 2005
Joined: Tue Jul 17, 2007 1:17 am
Location: France

Re: Add the ability to launch into "Batch Convert" custom sc

Post by oops66 »

m.Th. wrote:By "recursive" do you mean "serial" mode of calling, isn't it?...
... Not exactly, "Recursive" for me means to treat all the files selected (here into BatchConvert) ... but "serial" is enough for a "custom script" especially in case of multi thread behavior (in background), but if not, maybe this can by managed by an option too (serial or parallel)
The Batch Convert works "Serial"
The Open With... works "Parallel" - so you can use Open With... with multiple files - just checked - however isn't the best approach because it can bring down your PC or generate various conflicts etc.
... Yes that can be bring down the PC or generate various conflicts ;-)
So, as you say, perhaps the best solution would be (besides having certain code inside) a Batch Convert Action like this one:...
Which will do the following:
1. Save the image from the memory in the state in which it reached in the Batch Convert pipeline to a temporary file with the format specified in the dialog above
2. Run the command with the full file name as argument (don't forget to quote it) and wait for the script/process to finish
- It is the script responsibility to handle any other parameters / processing etc. as well as saving back the changes to the temporary file
3. If the temporary file is changed (and still exists, of course), reload it in the image from memory.
... Yes I prefer this approach ... (so the last "filter" in BatchConvert is the custom script in option) ;-)
XnViewMP Linux X64 - Debian - X64
User avatar
m.Th.
XnThusiast
Posts: 1663
Joined: Wed Aug 16, 2006 6:31 am
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by m.Th. »

... Yes I prefer this approach ... (so the last "filter" in BatchConvert is the custom script in option) ;-)
Isn't needed to be the last "filter" :) ...of course if you're "good guy". :)

Because as you see at the step 3., the temporary file is reloaded. Or perhaps your script goes out from the processing pipeline like in the example of Multiple Crop which you posted above?
m. Th.

- Dark Themed XnViewMP 1.6 64bit on Win11 x64 -
User avatar
oops66
XnThusiast
Posts: 2005
Joined: Tue Jul 17, 2007 1:17 am
Location: France

Re: Add the ability to launch into "Batch Convert" custom sc

Post by oops66 »

m.Th. wrote: ... Isn't needed to be the last "filter" :) ...
... It's mostly to allow the preview before the last optional custom step ;-)
XnViewMP Linux X64 - Debian - X64
User avatar
oops66
XnThusiast
Posts: 2005
Joined: Tue Jul 17, 2007 1:17 am
Location: France

Re: Add the ability to launch into "Batch Convert" custom sc

Post by oops66 »

Hello,
Bump ... any new?

It will be interesting to have a way to execute a script after all others batch-convert operations, (a (or some) post process, lines script) ... for example to optimize jpg files (same nb pixels, same eyes perception but MB size divided by 4 to up to 6) ...

Code: Select all

# Re-compress JPEGs and replace the originals
ladon "*.jpg" -- jpeg-recompress FULLPATH FULLPATH
See here for jpegmini or similar :
http://newsgroup.xnview.com/viewtopic.p ... 23#p124423

-----------------------------------------
https://github.com/imagemin/jpeg-recompress-bin
" jpeg-recompress-bin Build Status Build status
Compress JPEGs by re-encoding to the smallest JPEG quality while keeping perceived visual quality the same and by making sure huffman tables are optimized ..."
https://github.com/danielgtaylor/ladon
+
https://github.com/danielgtaylor/jpeg-archive/releases
XnViewMP Linux X64 - Debian - X64
User avatar
xnview
Author of XnView
Posts: 43444
Joined: Mon Oct 13, 2003 7:31 am
Location: France
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by xnview »

something like that?
Image
Pierre.
User avatar
oops66
XnThusiast
Posts: 2005
Joined: Tue Jul 17, 2007 1:17 am
Location: France

Re: Add the ability to launch into "Batch Convert" custom sc

Post by oops66 »

xnview wrote:something like that?
Yes in a first time/step ;-)
XnViewMP Linux X64 - Debian - X64
User avatar
m.Th.
XnThusiast
Posts: 1663
Joined: Wed Aug 16, 2006 6:31 am
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by m.Th. »

xnview wrote: Wed Feb 25, 2015 2:42 pm something like that?
Image
Bump here.

This is very, very, very useful.

Some usage cases:

In just one Batch we will
- generate all the image sizes of an application (small, medium, large etc.) to publish on Google Play, Apple Store, Windows Store etc.
- generate all the image sizes for a site (thumbnails, full, watermarked etc.)

We will have the following actions:

Resize to: Height = 1080

Custom Script: copy {filename} {filename_noExt}_screen

Resize to: 640

Custom Script: copy {filename} {filename_noExt}_640x320

Resize to: 128

Custom Script: copy {filename} {filename_noExt}_128x128

Something like this.
m. Th.

- Dark Themed XnViewMP 1.6 64bit on Win11 x64 -
User avatar
xnview
Author of XnView
Posts: 43444
Joined: Mon Oct 13, 2003 7:31 am
Location: France
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by xnview »

m.Th. wrote: Sat Nov 30, 2019 9:44 am This is very, very, very useful.

Some usage cases:

In just one Batch we will
- generate all the image sizes of an application (small, medium, large etc.) to publish on Google Play, Apple Store, Windows Store etc.
- generate all the image sizes for a site (thumbnails, full, watermarked etc.)
i don't understand you means external script file?
Pierre.
User avatar
m.Th.
XnThusiast
Posts: 1663
Joined: Wed Aug 16, 2006 6:31 am
Contact:

Re: Add the ability to launch into "Batch Convert" custom sc

Post by m.Th. »

xnview wrote: Fri Dec 06, 2019 12:06 pm
m.Th. wrote: Sat Nov 30, 2019 9:44 am This is very, very, very useful.

Some usage cases:

In just one Batch we will
- generate all the image sizes of an application (small, medium, large etc.) to publish on Google Play, Apple Store, Windows Store etc.
- generate all the image sizes for a site (thumbnails, full, watermarked etc.)
i don't understand you means external script file?
Yes.
m. Th.

- Dark Themed XnViewMP 1.6 64bit on Win11 x64 -
User avatar
m.Th.
XnThusiast
Posts: 1663
Joined: Wed Aug 16, 2006 6:31 am
Contact:

Re: Add the ability to launch into "Batch Convert" custom script

Post by m.Th. »

...to complete: in the example described the custom script file would be just a copy in order to save the intermediated result in a separate file.
m. Th.

- Dark Themed XnViewMP 1.6 64bit on Win11 x64 -
Post Reply