gnuastro (0.22)

(root)/
bin/
astscript-color-faint-gray
#!/bin/sh
#
# Build a color image using asinh function to manipulate the pixel values
# with the goal of showing the low and high pixel values at the same time.
# Script based in Lupton et al. (2004) http://doi.org/10.1086/382245
#
# Run with '--help' for more information.
#
# Current maintainer:
#     Raul Infante-Sainz <infantesainz@gmail.com>
# Contributing authors:
#     Mohammad Akhlaghi <mohammad@akhlaghi.org>
#     Samane Raji <samaneraji@gmail.com>
# Copyright (C) 2021-2024 Free Software Foundation, Inc.
#
# Gnuastro is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Gnuastro is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
# more details.
#
# You should have received a copy of the GNU General Public License along
# with Gnuastro. If not, see <http://www.gnu.org/licenses/>.



# Exit the script in the case of failure
set -e


# Save the current system language, and then change it to English to avoid
# problems in some parts of the code (AWK with `,' instead of `.' for
# decimal separator).
system_lang=$LANG
export LANG=C





# Default option values (can be changed with options on the command-line).
hdu=""
rhdu=1
globalhdu=""

# Minimum, weights, and zeropoint values
weight=""
minimum=""
zeropoint=""

# To control the asinh transformation, set both to 1 (scientific notation)
qbright_default=1.000000e+00
stretch_default=1.000000e+00

# For color, black, and gray regions
regions=""
grayval=""
colorval=""
coloronly=0
graykernelfwhm=0
colorkernelfwhm=0

# Linear range values. When normalizing the images to a new range, use the
# range 'newmin-newmax'. It is obtained by the following operations:
# OldRange = (OldMax - OldMin)
# NewRange = (NewMax - NewMin)
# NewValue = (((OldValue - OldMin) * NewRange) / OldRange) + NewMin
minvalrange=0.000
maxvalrange=100.0

# To enhance the image
bias=0.0
gamma=1.0
contrast=1.0
markoptions=""

quiet=""
tmpdir=""
keeptmp=0
checkparams=0
output="color-faint-gray.pdf"

version=0.22
scriptname=astscript-color-faint-gray





# Output of `--usage' and `--help':
print_usage() {
    cat <<EOF
$scriptname: run with '--help' for list of options
EOF
}

print_help() {
    cat <<EOF
Usage: $scriptname [OPTION] R.fits G.fits B.fits K.fits

This script is part of GNU Astronomy Utilities $version.

This script generates a color image from three different images (RGB). In order
to show the entire range of pixel values, an asinh transformation is used. For
more on the definiton of this algorithm see Lupton et al. 2004:

  https://doi.org/10.1086/382245

For more information, please run any of the following commands. In
particular the first contains a very comprehensive explanation of this
script's invocation: expected input(s), output(s), and a full description
of all the options.

     Inputs/Outputs and options:           $ info $scriptname
     Full Gnuastro manual/book:            $ info gnuastro

If you couldn't find your answer in the manual, you can get direct help from
experienced Gnuastro users and developers. For more information, please run:

     $ info help-gnuastro

$scriptname options:
 Input:
  -h, --hdu=STR           HDU/extension for the input channels.
  -t, --rhdu=STR          HDU/extension for the regions image.
  -g, --globalhdu=STR/INT Use this HDU for all inputs, ignore '--hdu'.
  -w, --weight=FLT        Relative weight for each input channel.
  -m, --minimum=FLT       Minimum value for each input channel.
  -z, --zeropoint=FLT     Zero point magnitude of each input channel.

 Asinh scaling parameters
  -Q, --qbright=FLT       Parameter for bringing out brighter features.
  -s, --stretch=FLT       Linear stretching parameter for faint features.

 Contrast, bias, and marks
  -b, --bias              Constant (bias) to add to all the pixels (linear).
  -c, --contrast          Change the contrast of the final image (linear).
  -G, --gamma             Gamma (nonlinear, overrides bias/contrast).
      --markoptions=STR   Options to add marks (passed to ConvertType).

 Color and gray parameters
      --coloronly         No grayscale regions, background in color (black).
      --regions=STR       Regions labeled image (color=2, black=1, gray=0).
      --grayval=FLT       Gray threshold (highest value to use grayscale).
      --colorval=FLT      Color threshold (lowest value to have color).
      --graykernelfwhm=FLT  Kernel FWHM for convolving the background image.
      --colorkernelfwhm=FLT Kernel FWHM for color separation ref. image.

 Output:
  -k, --keeptmp           Keep temporary/auxiliar files.
  -o, --output            Output color image name.
      --checkparams       Print distribution of values used to find params.

 Operating mode:
  -h, --help              Print this help list.
  -q, --quiet             Don't print the list.
  -V, --version           Print program version.
      --cite              BibTeX citation for this program.

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

GNU Astronomy Utilities home page: http://www.gnu.org/software/gnuastro/

Report bugs to bug-gnuastro@gnu.org.
EOF
}





# Output of `--version':
print_version() {
    cat <<EOF
$scriptname (GNU Astronomy Utilities) $version
Copyright (C) 2021-2024, Free Software Foundation, Inc.
License GPLv3+: GNU General public license version 3 or later.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Written/developed by Raul Infante-Sainz and Mohammad Akhlaghi.
EOF
}


print_citation() {
    empty="" # needed for the ascii art!
    cat <<EOF

Thank you for using $scriptname (GNU Astronomy Utilities) $version

Citations and acknowledgement are vital for the continued work on Gnuastro.

Please cite the following record(s) and add the acknowledgement statement below in your work to support us. Please note that different Gnuastro programs may have different corresponding papers. Hence, please check all the programs you used. Don't forget to also include the version as shown above for reproducibility.

Paper introducing this script
-----------------------------
  @ARTICLE{astscript-color-faint-gray,
         author = {{Infante-Sainz}, Ra{\'u}l and {Akhlaghi}, Mohammad},
          title = "{Gnuastro: Visualizing the Full Dynamic Range in Color Images}",
        journal = {Research Notes of the American Astronomical Society},
       keywords = {Astronomy software, Astronomy data visualization, Open source software, Low surface brightness galaxies, 1855, 1968, 1866, 940, Astrophysics - Instrumentation and Methods for Astrophysics, Astrophysics - Astrophysics of Galaxies, Computer Science - Computer Vision and Pattern Recognition},
           year = 2024,
          month = jan,
         volume = {8},
         number = {1},
            eid = {10},
          pages = {10},
            doi = {10.3847/2515-5172/ad1aae},
  archivePrefix = {arXiv},
         eprint = {2401.03814},
   primaryClass = {astro-ph.IM},
         adsurl = {https://ui.adsabs.harvard.edu/abs/2024RNAAS...8...10I},
        adsnote = {Provided by the SAO/NASA Astrophysics Data System}
  }

Paper introducing Gnuastro (currently main citation)
----------------------------------------------------
  @ARTICLE{gnuastro,
     author = {{Akhlaghi}, M. and {Ichikawa}, T.},
      title = "{Noise-based Detection and Segmentation of Nebulous Objects}",
    journal = {ApJS},
  archivePrefix = "arXiv",
     eprint = {1505.01664},
   primaryClass = "astro-ph.IM",
       year = 2015,
      month = sep,
     volume = 220,
        eid = {1},
      pages = {1},
        doi = {10.1088/0067-0049/220/1/1},
     adsurl = {https://ui.adsabs.harvard.edu/abs/2015ApJS..220....1A},
    adsnote = {Provided by the SAO/NASA Astrophysics Data System}
  }

Acknowledgement
---------------
This work was partly done using GNU Astronomy Utilities (Gnuastro, ascl.net/1801.009) version $version. Work on Gnuastro has been funded by the Japanese Ministry of Education, Culture, Sports, Science, and Technology (MEXT) scholarship and its Grant-in-Aid for Scientific Research (21244012, 24253003), the European Research Council (ERC) advanced grant 339659-MUSICOS, the Spanish Ministry of Economy and Competitiveness (MINECO, grant number AYA2016-76219-P) and the NextGenerationEU grant through the Recovery and Resilience Facility project ICTS-MRR-2021-03-CEFCA.
                                               ,
                                              {|'--.
                                             {{\    \ $empty
      Many thanks from all                   |/\`'--./=.
      Gnuastro developers!                   \`\.---' \`\\
                                                  |\  ||
                                                  | |//
                                                   \//_/|
                                                   //\__/
                                                  //
                   (http://www.chris.com/ascii/) |/

EOF
}





# Functions to check option values and complain if necessary.
on_off_option_error() {
    if [ "x$2" = x ]; then
        echo "$scriptname: '$1' doesn't take any values."
    else
        echo "$scriptname: '$1' (or '$2') doesn't take any values."
    fi
    exit 1
}

check_v() {
    if [ x"$2" = x ]; then
        echo "$scriptname: option '$1' requires an argument."
        echo "Try '$scriptname --help' for more information."
        exit 1;
    fi
}





# Separate command-line arguments from options. Then put the option
# value into the respective variable.
#
# OPTIONS WITH A VALUE:
#
#   Each option has three lines because we want to all common formats: for
#   long option names: `--longname value' and `--longname=value'. For short
#   option names we want `-l value', `-l=value' and `-lvalue' (where `-l'
#   is the short version of the hypothetical `--longname' option).
#
#   The first case (with a space between the name and value) is two
#   command-line arguments. So, we'll need to shift it two times. The
#   latter two cases are a single command-line argument, so we just need to
#   "shift" the counter by one. IMPORTANT NOTE: the ORDER OF THE LATTER TWO
#   cases matters: `-h*' should be checked only when we are sure that its
#   not `-h=*').
#
# OPTIONS WITH NO VALUE (ON-OFF OPTIONS)
#
#   For these, we just want the two forms of `--longname' or `-l'. Nothing
#   else. So if an equal sign is given we should definitely crash and also,
#   if a value is appended to the short format it should crash. So in the
#   second test for these (`-l*') will account for both the case where we
#   have an equal sign and where we don't.
while [ $# -gt 0 ]
do
    case "$1" in
        # Input parameters.
        -g|--globalhdu)      globalhdu="$2";                            check_v "$1" "$globalhdu";  shift;shift;;
        -g=*|--globalhdu=*)  globalhdu="${1#*=}";                       check_v "$1" "$globalhdu";  shift;;
        -g*)                 globalhdu=$(echo "$1" | sed -e's/-g//');   check_v "$1" "$globalhdu";  shift;;
        -h|--hdu)            aux="$2";                                  check_v "$1" "$aux"; hdu="$hdu $aux"; shift;shift;;
        -h=*|--hdu=*)        aux="${1#*=}";                             check_v "$1" "$aux"; hdu="$hdu $aux"; shift;;
        -h*)                 aux="$(echo "$1"  | sed -e's/-h//')";      check_v "$1" "$aux"; hdu="$hdu $aux"; shift;;
        -t|--rhdu)           rhdu="$2";                                 check_v "$1" "$rhdu";  shift;shift;;
        -t=*|--rhdu=*)       rhdu="${1#*=}";                            check_v "$1" "$rhdu";  shift;;
        -t*)                 rhdu=$(echo "$1" | sed -e's/-t//');        check_v "$1" "$rhdu";  shift;;
        -w|--weight)         aux="$2";                                  check_v "$1" "$aux"; weight="$weight $aux"; shift;shift;;
        -w=*|--weight=*)     aux="${1#*=}";                             check_v "$1" "$aux"; weight="$weight $aux"; shift;;
        -w*)                 aux="$(echo "$1"  | sed -e's/-w//')";      check_v "$1" "$aux"; weight="$weight $aux"; shift;;
        -m|--minimum)        aux="$2";                                  check_v "$1" "$aux"; minimum="$minimum $aux"; shift;shift;;
        -m=*|--minimum=*)    aux="${1#*=}";                             check_v "$1" "$aux"; minimum="$minimum $aux"; shift;;
        -m*)                 aux="$(echo "$1"  | sed -e's/-m//')";      check_v "$1" "$aux"; minimum="$minimum $aux"; shift;;
        -z|--zeropoint)      aux="$2";                                  check_v "$1" "$aux"; zeropoint="$zeropoint $aux"; shift;shift;;
        -z=*|--zeropoint=*)  aux="${1#*=}";                             check_v "$1" "$aux"; zeropoint="$zeropoint $aux"; shift;;
        -z*)                 aux="$(echo "$1"  | sed -e's/-z//')";      check_v "$1" "$aux"; zeropoint="$zeropoint $aux"; shift;;

        -s|--stretch)        stretch="$2";                              check_v "$1" "$stretch";  shift;shift;;
        -s=*|--stretch=*)    stretch="${1#*=}";                         check_v "$1" "$stretch";  shift;;
        -s*)                 stretch=$(echo "$1"  | sed -e's/-s//');    check_v "$1" "$stretch";  shift;;
        -Q|--qbright)        qbright="$2";                              check_v "$1" "$qbright";  shift;shift;;
        -Q=*|--qbright=*)    qbright="${1#*=}";                         check_v "$1" "$qbright";  shift;;
        -Q*)                 qbright=$(echo "$1"  | sed -e's/-Q//');    check_v "$1" "$qbright";  shift;;

        -G|--gamma)          gamma="$2";                                check_v "$1" "$gamma";  shift;shift;;
        -G=*|--gamma=*)      gamma="${1#*=}";                           check_v "$1" "$gamma";  shift;;
        -G*)                 gamma=$(echo "$1"  | sed -e's/-G//');      check_v "$1" "$gamma";  shift;;
        -c|--contrast)       contrast="$2";                             check_v "$1" "$contrast";  shift;shift;;
        -c=*|--contrast=*)   contrast="${1#*=}";                        check_v "$1" "$contrast";  shift;;
        -c*)                 contrast=$(echo "$1"  | sed -e's/-c//');   check_v "$1" "$contrast";  shift;;
        -b|--bias)           bias="$2";                                 check_v "$1" "$bias";  shift;shift;;
        -b=*|--bias=*)       bias="${1#*=}";                            check_v "$1" "$bias";  shift;;
        -b*)                 bias=$(echo "$1"  | sed -e's/-b//');       check_v "$1" "$bias";  shift;;
        --markoptions)       markoptions="$2";                          check_v "$1" "$markoptions";  shift;shift;;
        --markoptions=*)     markoptions="${1#*=}";                     check_v "$1" "$markoptions";  shift;;

        --coloronly)        coloronly=1; shift;;
        --regions)          regions="$2";                              check_v "$1" "$regions";  shift;shift;;
        --regions=*)        regions="${1#*=}";                         check_v "$1" "$regions";  shift;;
        --grayval)          grayval="$2";                              check_v "$1" "$grayval";  shift;shift;;
        --grayval=*)        grayval="${1#*=}";                         check_v "$1" "$grayval";  shift;;
        --colorval)         colorval="$2";                             check_v "$1" "$colorval";  shift;shift;;
        --colorval=*)       colorval="${1#*=}";                        check_v "$1" "$colorval";  shift;;
        --graykernelfwhm)   graykernelfwhm="$2";                       check_v "$1" "$graykernelfwhm";  shift;shift;;
        --graykernelfwhm=*) graykernelfwhm="${1#*=}";                  check_v "$1" "$graykernelfwhm";  shift;;
        --colorkernelfwhm)   colorkernelfwhm="$2";                     check_v "$1" "$colorkernelfwhm";  shift;shift;;
        --colorkernelfwhm=*) colorkernelfwhm="${1#*=}";                check_v "$1" "$colorkernelfwhm";  shift;;

        # Output parameters
        --checkparams)    checkparams=1; shift;;
        -k|--keeptmp)     keeptmp=1; shift;;
        -k*|--keeptmp=*)  on_off_option_error --keeptmp -k;;
        -t|--tmpdir)      tmpdir="$2";                          check_v "$1" "$tmpdir";  shift;shift;;
        -t=*|--tmpdir=*)  tmpdir="${1#*=}";                     check_v "$1" "$tmpdir";  shift;;
        -t*)              tmpdir=$(echo "$1" | sed -e's/-t//'); check_v "$1" "$tmpdir";  shift;;
        -o|--output)      output="$2";                          check_v "$1" "$output"; shift;shift;;
        -o=*|--output=*)  output="${1#*=}";                     check_v "$1" "$output"; shift;;
        -o*)              output=$(echo "$1" | sed -e's/-o//'); check_v "$1" "$output"; shift;;

        # Non-operating options.
        -q|--quiet)       quiet="--quiet"; shift;;
        -q*|--quiet=*)    on_off_option_error --quiet -q;;
        -?|--help)        print_help; exit 0;;
        -'?'*|--help=*)   on_off_option_error --help -?;;
        -V|--version)     print_version; exit 0;;
        -V*|--version=*)  on_off_option_error --version -V;;
        --cite)           print_citation; exit 0;;
        --cite=*)         on_off_option_error --cite;;

        # Unrecognized option:
        -*) echo "$scriptname: unknown option '$1'"; exit 1;;

        # Not an option (not starting with a `-'): assumed to be input FITS
        # file name.
        *) inputs="$inputs $1"; shift;;
    esac
done





# Basic sanity checks
# -------------------
#
# If no input is given, let the user know.
if [ x"$inputs" = x ]; then
    echo "$scriptname: no input FITS files."
    echo "Run with '--help' for more information on how to run."
    exit 1
fi


# Inputs. Make sure three or four inputs have been given.
ninputs=$(echo "$inputs" | awk '{print NF}')
if [ $ninputs != 3 ] && [ $ninputs != 4 ]; then
    cat <<EOF
$scriptname: $ninputs inputs given, but 3 (or 4) inputs are necessary: one for each of the R,G,B,(K) channels, respectively. Run with '--help' for more information on how to run.
EOF
    exit 1
else
    rimage=$(echo $inputs | awk '{print $1}')
    gimage=$(echo $inputs | awk '{print $2}')
    bimage=$(echo $inputs | awk '{print $3}')
    kimage=$(echo $inputs | awk '{print $4}')
fi


# HDU. If the user provides a globalhdu use it, otherwise check that HDU
# number matches with input images
if [ x"$globalhdu" != x ]; then
    rhdu=$globalhdu
    ghdu=$globalhdu
    bhdu=$globalhdu
    khdu=$globalhdu
else
    nhdu=$(echo "$hdu" | awk '{print NF}')
    if [ x"$nhdu" != x"$ninputs" ]; then
        cat <<EOF
$scriptname: number of inputs and HDUs does not match. Every input FITS image needs a HDU (identified by name or number, counting from zero). You can use multiple calls to the '--hdu' ('-h') option for each input FITS image (in the same order as the input FITS files), or use '--globalhdu' ('-g') once when the same HDU should be used for all of them. Run with '--help' for more information on how to run.
EOF
        exit 1
    fi
    rhdu=$(echo "$hdu" | awk '{print $1}')
    ghdu=$(echo "$hdu" | awk '{print $2}')
    bhdu=$(echo "$hdu" | awk '{print $3}')
    khdu=$(echo "$hdu" | awk '{print $4}')
fi


# Minimum. If the user provides a single value for --minimum, use it for
# all channels. Otherwise, check that the number of minimums matches with
# the number of inputs.
nminimum=$(echo "$minimum" | awk '{print NF}')
if [ x$nminimum = x1 ]; then
    rmin=$minimum
    gmin=$minimum
    bmin=$minimum
    kmin=$minimum
elif [ $nminimum -gt 1 ] && [ x"$nminimum" != x"$ninputs" ]; then
        cat <<EOF
$scriptname: number of inputs ($ninputs) and minimum values ($nminimum) does not match. Every input FITS image needs a minimum value. You can use multiple calls to the '--minimum' ('-m') option for each input FITS image (in the same order as the input FITS files). If a single value is provided, it will be used for all channels. Run with '--help' for more information on how to run.
EOF
        exit 1
else
    rmin=$(echo "$minimum" | awk '{print $1}')
    gmin=$(echo "$minimum" | awk '{print $2}')
    bmin=$(echo "$minimum" | awk '{print $3}')
    kmin=$(echo "$minimum" | awk '{print $4}')

fi


# Zeropoint. If the user provides a single value for --zeropoint, use it
# for all channels. Otherwise, check that the number of zeropoints matches
# with the number of inputs.
nzeropoint=$(echo "$zeropoint" | awk '{print NF}')
if [ x$nzeropoint = x1 ]; then
    rzero=$zeropoint
    gzero=$zeropoint
    bzero=$zeropoint
    kzero=$zeropoint
elif [ $nzeropoint -gt 1 ] && [ x"$nzeropoint" != x"$ninputs" ]; then
        cat <<EOF
$scriptname: number of inputs ($ninputs) and zeropoint values ($nzeropoint) does not match. Every input FITS image needs a zeropoint value. You can use multiple calls to the '--zeropoint' ('-z') option for each input FITS image (in the same order as the input FITS files). If a single value is provided, it will be used for all channels. Run with '--help' for more information on how to run.
EOF
        exit 1
else
    rzero=$(echo "$zeropoint" | awk '{print $1}')
    gzero=$(echo "$zeropoint" | awk '{print $2}')
    bzero=$(echo "$zeropoint" | awk '{print $3}')
    kzero=$(echo "$zeropoint" | awk '{print $4}')
fi


# Weight. If the does not provide a value for weight, set it to 1.0. If
# user provides a single value for --weight, use it for all channels.
# Otherwise, check that the number of weights matches
# with the number of inputs.
nweight=$(echo "$weight" | awk '{print NF}')
if [ x$nweight = x0 ]; then
    rweight=1.0
    gweight=1.0
    bweight=1.0
elif [ x$nweight = x1 ]; then
    rweight=$weight
    gweight=$weight
    bweight=$weight
elif [ $nweight -gt 1 ] && [ x"$nweight" != x"$ninputs" ]; then
        cat <<EOF
$scriptname: number of inputs ($ninputs) and weight values ($nweight) does not match. Every input FITS image needs a weight value. You can use multiple calls to the '--weight' ('-w') option for each input FITS image (in the same order as the input FITS files). If a single value is provided, it will be used for all channels. Run with '--help' for more information on how to run.
EOF
        exit 1
else
    rweight=$(echo "$weight" | awk '{print $1}')
    gweight=$(echo "$weight" | awk '{print $2}')
    bweight=$(echo "$weight" | awk '{print $3}')
fi


# Bright. If the user provides --qbright, make sure it is not equal to zero
# (with 8 decimals), that would crash the asinh transformation.
if [ x$qbright != x ]; then
    qbright_check=$(echo "$qbright" | awk 'BEGIN{FS=","} {printf "%.8f", $1}')
    if [ x$qbright_check = x0.00000000 ]; then
        echo "$scriptname: --qbright value ($qbright) cannot be zero (8 decimals)."
        exit 1
    fi
else
    qbright=$qbright_default
fi


# Stretch. If the user provides --stretch, make sure it is not equal to
# zero (with 8 decimals), that would crash the asinh transformation.
if [ x$stretch != x ]; then
    stretch_check=$(echo "$stretch" | awk 'BEGIN{FS=","} {printf "%.8f", $1}')
    if [ x$stretch_check = x0.00000000 ]; then
        echo "$scriptname: --stretch value ($stretch) cannot be zero (8 decimals)."
        exit 1
    fi
else
    stretch=$stretch_default
fi






# Define the temporary directory
# -----------------------------
#
# Construct the temporary directory. If the user does not specify any
# directory, then a default one with will be constructed.  If the user
# set the directory, then make it. This directory will be deleted at
# the end of the script if the user does not want to keep it (with the
# `--keeptmp' option).
defaulttmpdir="color-faint-gray-tmp"
if [ x$tmpdir = x ]; then tmpdir=$defaulttmpdir; fi
if [ -d $tmpdir ]; then junk=1; else mkdir $tmpdir; fi





# Clip pixel values below the specified limit
# -------------------------------------------
#
# If the user specify a given value below which the pixel values are
# wanted to be zero, then put all of those pixels to zero. By default,
# no clipping will be done (a symbolic link will be put to the input).
rclipped="$tmpdir/R_clipped.fits"
gclipped="$tmpdir/G_clipped.fits"
bclipped="$tmpdir/B_clipped.fits"
kclipped="$tmpdir/K_clipped.fits"

if [ x"$rmin" = x ]; then
    cp $rimage $rclipped
else
    astarithmetic $rimage --hdu=$rhdu set-i $quiet \
                  i i $rmin lt 0 where --output=$rclipped
    rhdu=1
fi

if [ x"$gmin" = x ]; then
    cp $gimage $gclipped
else
    astarithmetic $gimage --hdu=$ghdu set-i $quiet \
                  i i $gmin lt 0 where --output=$gclipped
    ghdu=1
fi

if [ x"$bmin" = x ]; then
    cp $bimage $bclipped
else
    astarithmetic $bimage --hdu=$bhdu set-i $quiet \
                  i i $bmin lt 0 where --output=$bclipped
    bhdu=1
fi

# kclipped is constructed only if a fourth image has been given.
if [ x"$kmin" = x ] && [ x$kimage != x ]; then
    cp $kimage $kclipped
elif [ x$kimage != x ]; then
    astarithmetic $kimage --hdu=$khdu set-i $quiet \
                  i i $kmin lt 0 where --output=$kclipped
    khdu=1
fi





# Scale the images: weighting and converting to micro Janskys
# -----------------------------------------------------------
#
# All input images are scaled if different weights of 1 are provided. This
# step allow to easily weight the different bands differently. The
# weighting is done considering the specified weight values and setting the
# maximum value to 1. That means that the image with the highest weighting
# value will be not modified, while the other bands will be scaled
# appropiately. In addition to this, if zero points are provided, the
# images will be transformed to have micro Jansky values, this helps makes
# a more physically meaningful comparison between the filters.
rscaled="$tmpdir/R_scaled.fits"
gscaled="$tmpdir/G_scaled.fits"
bscaled="$tmpdir/B_scaled.fits"

# Compute normalized weights
weight_sum=$(astarithmetic $rweight f32 $gweight f32 $bweight f32 3 sum --type=f32 -q)
rweight_sum=$(astarithmetic $rweight f32 $weight_sum / -q | awk '{printf "%.4f", $1}')
gweight_sum=$(astarithmetic $gweight f32 $weight_sum / -q | awk '{printf "%.4f", $1}')
bweight_sum=$(astarithmetic $bweight f32 $weight_sum / -q | awk '{printf "%.4f", $1}')

wmax=$(astarithmetic $rweight_sum $gweight_sum $bweight_sum 3 max --type=f32 -q)

rweight_norm=$(astarithmetic $rweight_sum $wmax / -q | awk '{printf "%.4f", $1}')
gweight_norm=$(astarithmetic $gweight_sum $wmax / -q | awk '{printf "%.4f", $1}')
bweight_norm=$(astarithmetic $bweight_sum $wmax / -q | awk '{printf "%.4f", $1}')


# If all weights are 1.0000 and no zeropoints are provided, then symbolic link.
if [ x$rweight = x"1.0000" ] &&
   [ x$gweight = x"1.0000" ] &&
   [ x$bweight = x"1.0000" ] &&
   [ x"$rzero" = x ]; then
    ln -sf $rclipped $rscaled
    ln -sf $gclipped $gscaled
    ln -sf $bclipped $bscaled

# If diferent weights but same zeropoints, then scale the images.
elif [ x$rweight != x"1.0000" ] ||
     [ x$gweight != x"1.0000" ] ||
     [ x$bweight != x"1.0000" ] ||
     [ x"$rzero" = x ]; then
    # Just weight the images appropiately
    astarithmetic $rclipped -h$rhdu $rweight_norm x -o$rscaled $quiet
    astarithmetic $gclipped -h$ghdu $gweight_norm x -o$gscaled $quiet
    astarithmetic $bclipped -h$bhdu $bweight_norm x -o$bscaled $quiet
    rhdu=1; ghdu=1; bhdu=1

# Else, use weights and zeropoints to scale and transform to micro Jansky.
else
    # Consider zeropoints as float (8 decimals) numbers to homogeneize
    rzerof=$(echo $rzero | awk '{printf "%.8f", $1}')
    gzerof=$(echo $gzero | awk '{printf "%.8f", $1}')
    bzerof=$(echo $bzero | awk '{printf "%.8f", $1}')

    # Transform to pixel unit to micro Jansky
    astarithmetic $rclipped -h$rhdu $rzerof counts-to-jy 1e6 x \
                  $rweight_norm x --output=$rscaled $quiet
    astarithmetic $gclipped -h$ghdu $gzerof counts-to-jy 1e6 x \
                  $gweight_norm x --output=$gscaled $quiet
    astarithmetic $bclipped -h$bhdu $bzerof counts-to-jy 1e6 x \
                  $bweight_norm x --output=$bscaled $quiet
    rhdu=1; ghdu=1; bhdu=1
fi





# Stacked image: i_rgb = (R+G+B)/3
# --------------------------------
#
# The asinh transformation is done on the mean of RGB images. Here, this
# image is obtained by stacking them using the 'mean' operator. If the
# stacked image has exact zero pixel values, change them to nan values.
i_rgb_stack="$tmpdir/rgb_mean.fits"
astarithmetic $rscaled --hdu=$rhdu \
              $gscaled --hdu=$ghdu \
              $bscaled --hdu=$bhdu 3 mean \
               --output=$i_rgb_stack $quiet





# ASINH transformation
# --------------------
#
# Once the necessary parameters have been obtained, here the asinh
# transformation is done over the mean of RGB images. After the
# transformation is done, it is normalized by 'i_rgb'. Finally, the range
# of pixel values is linear transformed to [minvalrange - maxvalrange].
i_rgb_asinh="$tmpdir/rgb_mean-asinh.fits"
astarithmetic $i_rgb_stack -h1 set-i_rgb \
              i_rgb $qbright x $stretch x set-i \
              i asinh $qbright / --output=$i_rgb_asinh $quiet
i_rgb_asinh_norm="$tmpdir/rgb_mean-asinh-norm.fits"
astarithmetic $i_rgb_stack -h1 set-stack \
              $i_rgb_asinh -h1 set-asinh \
              asinh stack / --output=$i_rgb_asinh_norm $quiet





# Compute the r, g, b images
# --------------------------
#
# Each input image is multiplied by the transformed and normalized averaged
# image (i_rgb_asinh_norm).
i_r="$tmpdir/r.fits"
i_g="$tmpdir/g.fits"
i_b="$tmpdir/b.fits"
astarithmetic $rscaled --hdu=$rhdu $i_rgb_asinh_norm x $quiet --output=$i_r
astarithmetic $gscaled --hdu=$ghdu $i_rgb_asinh_norm x $quiet --output=$i_g
astarithmetic $bscaled --hdu=$bhdu $i_rgb_asinh_norm x $quiet --output=$i_b





# Normalize by the maximum value of R, G, B
# -----------------------------------------
#
# Compute the maximum of the R, G, B images and divide each image by that
# value. This step is done in order to represent the true color.
i_r_norm="$tmpdir/r_norm.fits"
i_g_norm="$tmpdir/g_norm.fits"
i_b_norm="$tmpdir/b_norm.fits"
maxr=$(aststatistics $i_r --maximum -q)
maxg=$(aststatistics $i_g --maximum -q)
maxb=$(aststatistics $i_b --maximum -q)
maxrgb=$(astarithmetic $maxr float64 $maxg float64 $maxb \
                       float64 3 max -q)
astarithmetic $i_r $maxrgb / $maxvalrange x float32 -o$i_r_norm $quiet
astarithmetic $i_g $maxrgb / $maxvalrange x float32 -o$i_g_norm $quiet
astarithmetic $i_b $maxrgb / $maxvalrange x float32 -o$i_b_norm $quiet





# Transformations to enhance the image
# ------------------------------------
#
# Modify the images that are going to be used for obtaining the COLOR
# image. There are two possible options: linear and NON-linear
# transformation. For the linear transformation, two parameters are used,
# contrast and bias:
#
#  OUTPUT = contrast * INPUT + bias
#
# In the NON-linear transformation only one parameter is used, gamma:
#
#  OUTPUT = INPUT**gamma
#
# After this transformation is done, all values are clipped by the
# maxvalrange value. So, all pixels above maxrangevalue become equal to
# maxrangevalue.
i_r_enhanced="$tmpdir/r_enhanced.fits"
i_g_enhanced="$tmpdir/g_enhanced.fits"
i_b_enhanced="$tmpdir/b_enhanced.fits"
if [ x"$gamma" != x"1.0" ]; then
  astarithmetic $i_r_norm $maxvalrange / $gamma pow $maxvalrange x set-t \
                t t $maxvalrange gt $maxvalrange where \
                --output=$i_r_enhanced $quiet
  astarithmetic $i_g_norm $maxvalrange / $gamma pow $maxvalrange x set-t \
                t t $maxvalrange gt $maxvalrange where \
                --output=$i_g_enhanced $quiet
  astarithmetic $i_b_norm $maxvalrange / $gamma pow $maxvalrange x set-t \
                t t $maxvalrange gt $maxvalrange where \
                --output=$i_b_enhanced $quiet

elif [ x"$contrast" != x"1.0" ] || [ x$bias != x"0.0" ]; then
  astarithmetic $i_r_norm $contrast x $bias + set-t \
                t t $maxvalrange gt $maxvalrange where \
                --output=$i_r_enhanced $quiet
  astarithmetic $i_g_norm $contrast x $bias + set-t \
                t t $maxvalrange gt $maxvalrange where \
                --output=$i_g_enhanced $quiet
  astarithmetic $i_b_norm $contrast x $bias + set-t \
                t t $maxvalrange gt $maxvalrange where \
                --output=$i_b_enhanced $quiet

else
    cp $i_r_norm $i_r_enhanced    # Not using 'ln -s' because there will
    cp $i_g_norm $i_g_enhanced    # be too many links and 'realpath' is
    cp $i_b_norm $i_b_enhanced    # not portable on non-GNU OSs.
fi





# If the user only wants colored pixels (with black background).
if [ x$coloronly = x1 ]; then

    # Make the color figure
    # ---------------------
    #
    # Once all the previous treatment has been done for each image, then combine
    # all with Convert program to obtain the colored image. Limit fluxes used
    # here correspond to the gray-computed ones in order to obtain exactly the
    # same color as the gray-background image (for those pixels that are not
    # background).
    astconvertt $i_r_enhanced -h1 \
                $i_g_enhanced -h1 \
                $i_b_enhanced -h1 \
                $markoptions      \
                --output=$output $quiet

    # Remove images
    if [ $keeptmp = 0 ]; then
      rm $rscaled $gscaled $bscaled \
         $rclipped $gclipped $bclipped
    fi


# If user wants the gray background image
else

    # In this case, the color map is the following:
    #   Bright pixels are shown in color.
    #   Fainter pixels are shown in black.
    #   Background pixels are shown in gray.
    # As a consequence, two different thresholds need to be defined:
    # colorval and grayval. They are defined from the threshold image.

    # Regions:
    # Threshold image:
    # Final channel images:
    # ----------------------------------------------
    # bright-color    | faint-black  |  back-gray
    # 0   20   40     |    60  70    |  80  100
    # R: 90 80 10 ... |    0 0 0     | ... 10 50 100
    # G: 80 70 20 ... |    0 0 0     | ... 10 50 100
    # B: 90 60 30 ... |    0 0 0     | ... 10 50 100

    # In words. The bright pixels are formed from the three channels, each
    # with different values for the same pixel.

    # The faint region (in black) can be shown in color but dark because
    # the pixels have lower values in the three channels. These pixels go
    # close to zero in the channels.

    # Then, the background is shown in gray. To do it, the same pixel value
    # are set for the different channels. In this case, lower but equal
    # pixel values mean dark gray, while high and same pixel values mean
    # white.

    # In the situation above there is no pure black regions because the
    # transition from color to gray is smooth ('colorval'='grayval'). But
    # it is possible to define a region of pure black pixels. They are set
    # from the --colorval and --grayval thresholds. The pixels between
    # these values in the threshold image are set to zero in the three
    # channels.


    # Background image
    # ----------------
    #
    # If three images are provided, we use the stacked and
    # asinh-transformed image for the background. Otherwise, if four images
    # are provided, we use the fourth image with no modifications for the
    # background.
    i_colorgray_threshold="$tmpdir/colorgray_threshold.fits"
    if [ x$ninputs = x3 ]; then
        i_back=$i_rgb_asinh
        khdu=1

        # Convolve the background image
        # -----------------------------
        #
        # If the user wants to convolve the background image.
        i_back_convolved="$tmpdir/back_convolved.fits"
        if [ $graykernelfwhm = 0 ]; then
          cp $i_back $i_back_convolved
        else
          i_back_kernel="$tmpdir/back_kernel.fits"
          astmkprof --kernel=gaussian,$graykernelfwhm,3 \
                    --oversample=1 --output=$i_gray_kernel $quiet
          astconvolve $i_back --hdu=$khdu --kernel=$i_back_kernel \
                      --domain=spatial --output=$i_back_convolved $quiet
        fi

        # Change pixel values to the wanted range
        astarithmetic $i_back_convolved -h1 set-image \
                      image minvalue set-oldmin \
                      image maxvalue set-oldmax \
                      $minvalrange set-newmin \
                      $maxvalrange set-newmax \
                      oldmax oldmin - set-oldrange \
                      newmax newmin - set-newrange \
                      image oldmin - newrange x oldrange / newmin + \
                      float32 --output=$i_colorgray_threshold $quiet

    else
        i_back_convolved=$kclipped
        ln -sf $kclipped $i_colorgray_threshold
    fi





    # Find the thresholds
    # -------------------
    #
    # The color image with the gray background is constructed by separating the
    # original image into two regimes: COLOR and GRAY. Here, the pixel value
    # used for separate these two regions is computed. It is obtained from the
    # asinh-transformed image. If the user does not provide a value then use
    # ghe computed one (guessed). If the user provide a value, then use it
    # directly. Note that the guessed value is computed in any case.
    colorval_estimated=$(aststatistics $i_colorgray_threshold \
                                       --median --quiet)
    if [ x$colorval = x"" ]; then
      colorval=$colorval_estimated
    fi

    grayval_estimated=$(aststatistics $i_colorgray_threshold \
                                      --median --quiet)
    if [ x$grayval = x"" ]; then
      grayval=$grayval_estimated
    fi





    # Regions labeled image
    # ---------------------
    #
    # The TOTAL_MASK consists of a labeled image whose pixel values
    # correspond to the three regions. Labels are defined as follows:
    #   2 will be shown in color
    #   1 will be shown in pure black
    #   0 will be shown in gray
    # This image is computed by default from the colorval and grayval
    # parameters. Alternatively, it can be provided by the user.
    if [ x$regions = x ]; then
        total_mask="$tmpdir/total_mask-2color-1black-0gray.fits"
        astarithmetic $i_colorgray_threshold                     set-i \
                      i $colorval gt                   2 uint8 x set-c \
                      i $colorval lt i $grayval gt and 1 uint8 x set-b \
                      i $colorval lt                   0 uint8 x set-g \
                      c b g 3 sum uint8 --output $total_mask
        rhdu=1
   else
       grayval="$regions"
       colorval="$regions"
       total_mask=$regions
   fi





    # Gray background image
    # ---------------------
    #
    # We use the specified gray background image after some modifications to
    # obtain the GRAY part. For futher highlight the interesting diffuse flux,
    # we need to invert it the brigher pixels are darker. Here, the color
    # region is masked (pixels=2 in TOTAL_MASK)
    #
    # Here is the logic of the steps we take:
    #   1. We call the convolved image 'values'.
    #   2. We call the mask image 'mask'.
    #   3. Pixels equal to 1 in 'mask' are set to nan in 'values': 'masked'.
    #   4. The min value of this image is 'oldmin'.
    #   5. The max value of this image is 'oldmax'.
    #   6. The new min value of the final image is 'newmin'.
    #   7. The new max value of the final image is 'newmax'.
    #   (Note that they are equal but inverted of the general range values).
    #   7. The old range (oldmax - oldmin) is 'oldrange'.
    #   8. The new range (newmax - newmin) is 'newrange'.
    #   9. The 'masked' image is transformed to have the new range of values.
    #   (Note that 'grayscale' will transform the pixel value in case of any
    #   function is specified. E.g., log, sqrt, asinh, etc.)
    grayscale=""
    i_gray_colormasked="$tmpdir/gray_colormasked.fits"
    echo here1
    astarithmetic $i_back_convolved -h1          set-values \
                  $total_mask -h$rhdu 2 uint8 eq set-mask \
                  values mask nan where $grayscale set-masked \
                  masked minvalue set-oldmin \
                  masked maxvalue set-oldmax \
                  $maxvalrange set-newmin \
                  $minvalrange set-newmax \
                  oldmax oldmin - set-oldrange \
                  newmax newmin - set-newrange \
                  masked oldmin - newrange x oldrange / newmin + \
                  set-transformed \
                  transformed float32 --output=$i_gray_colormasked $quiet





    # Set the pure black region to zero pixel values
    # ----------------------------------------------
    #
    # Put black pixels (pixels=1 in TOTAL_MASK) equal to zero. By doing
    # this, those pixels will be set to pure black color.
    i_gray_colormasked_zeroblack="$tmpdir/gray_colormasked_zeroblack.fits"
    astarithmetic $i_gray_colormasked -h1 set-i \
                  $total_mask -h$rhdu 1 uint8 eq -h1 set-b \
                  i b 0.0 where float32 \
                  --output=$i_gray_colormasked_zeroblack





    # Use color for high S/N and gray for low S/N
    # -------------------------------------------
    #
    # Once the gray-mask has been obtained, use it for replace all pixels less
    # than the gray-threshold with the pixels of the reference image. This is
    # done for each input image (band).  Output images end with -gray.fits,
    # generated into the for loop.
    i_r_black_gray="$tmpdir/r_black_gray.fits"
    i_g_black_gray="$tmpdir/g_black_gray.fits"
    i_b_black_gray="$tmpdir/b_black_gray.fits"
    for f in $i_r_enhanced $i_g_enhanced $i_b_enhanced; do
      outputname=$(echo "$f" | sed -e's/_enhanced.fits/_black_gray.fits/')
      astarithmetic $f \
                    $i_gray_colormasked_zeroblack isblank not \
                    $i_gray_colormasked_zeroblack \
                    where -g1 --output=$outputname $quiet;
    done





    # Make the color-gray image
    # -------------------------
    #
    # Once all the previous treatment has been done for each image, then
    # combine all with Convert program to obtain the colored image (with the
    # low S/N regions in gray).
    astconvertt $i_r_black_gray -h1 \
                $i_g_black_gray -h1 \
                $i_b_black_gray -h1 \
                $markoptions      \
                --output=$output $quiet
fi





# Information to help and warn the user
# -------------------------------------
#
# If the quiet mode is not activated, then print valuable information to
# help the user in guessing appropiate parameters for making the color
# image. In the same way, if the images come with astrometric information
# (WCS) the relative variations in pixel scales and alignment of them are
# provided. This is useful to know if color-gradients on the color image
# could be due to a bad alignment of the images or different pixel scales.

if [ ! x$quiet = x"--quiet" ]; then

  # Print on the command line the statistics of the image and the parameters
  # that are going to be used. This will helps to guess appropiate parameters
  if [ x$checkparams = x1 ]; then
  echo "                   "
  echo "For asinh-transformation ('--strech' and '--qbright' parameters)."
  aststatistics $i_rgb_stack

  echo "                   "
  echo "For color and gray thresholds."
  echo "separation between color and black regions (--colorval)"
  echo "separation between black and gray regions (--grayval)"
  aststatistics $i_colorgray_threshold

  fi

  cat <<EOF

TIPS:
  # First, use the default options to estimate the parameters.
  # Select a good background value of the images:
      A minimum value of zero could be a good option: '--minimum=0.0'
  # Focus on the bright regions and tweak '--qbright' and '--stretch':
      First, try low values of '--qbright' to show the bright parts.
      Then, adjust '--stretch' to show the fainter regions around bright parts.
      Overall, play with these two parameters to show the color regions appropriately.
  # Change '--colorval' to separate the color and black regions:
      This is the lowest value of the threshold image that is shown in color.
  # Change '--grayval' to separate the black and gray regions:
      This is highest value of the threshold image that is shown in gray.
  # Use '--checkparams' to check the pixel value distributions.
  # Use '--keeptmp' to not remove the threshold image and check it:
      '$i_colorgray_threshold'
EOF
  echo
  printf "%-17s %-15s %-15s\n"   Option       Default           Used
  printf "  %-15s %-15g %-15g\n" "--qbright"  $qbright_default  $qbright
  printf "  %-15s %-15g %-15g\n" "--stretch"  $stretch_default  $stretch
  if [ x$regions = x ]; then
      printf "  %-15s %-15g %-15g\n" "--colorval" $colorval_default $colorval
      printf "  %-15s %-15g %-15g\n" "--grayval"  $grayval_default  $grayval
  fi
  echo; echo "Output written to '$output'."
fi





# Remove temporary files
# ---------------------
#
# If the user does not specify to keep the temporary files with the option
# `--keeptmp', then remove the whole directory.
if [ $keeptmp = 0 ]; then
    rm -rf $tmpdir
fi





# The script has finished, reset the original language to the system's
# default language.
export LANG=$system_lang