#! /bin/bash
# Last edited on 2023-09-25 12:53:05 by stolfi

# Converts a raw image of the Phaistos disk to a standard size and
# resolution, with cropping and perspective rectification, given
# coordinates of four reference points (key features) on the input image
# and their desired coordinate on the outout image. The image is
# converted to grayscale.
#
# Parameters:
#
#   {IMG_IN}      Filename of input image to be transformed.
#   {IPTS_REF_IN} File with coords of key features on that image.
#   {OPTS_REF_IN} File with coords of those features on output image.
#   {OUNIT}       Output pixels per user output coordinate unit.
#   {IMG_OUT}     Filename of output (rectified) image. 
#   {OPTS_UMP_IN} File with output coords to be mapped back to input coords.
#   {PTS_UMP_OUT} Filename for input and output coords of those mapped points.
#   {NHOOD_PREF}  Prefix for filenames of neighborhoods of key features.
#
# Assumes that each of the files {IPTS_REF_IN}, {OPTS_REF_IN}, and
# {OPTS_UMP_IN} contains the coordinates user input (IPTS) or user
# output output (OPTS) of selected features on the input or output
# image. Each data line (excluding '#'-comments and blank lines) should
# have a feature tag ("P{NN}" or "Q{NN}", where {NN} is an integer)
# followed by the user X and Y coordinates of the feature on the
# respective image.
# 
# The projective map from input coordinates to output coordinates is
# determined by the points from {IPTS_REF_IN} and {OPTS_REF_IN} with
# matching tags "P{NUM}" or "Q{NUM}", where {NUM} is a single digit.
# Ignores features that are listed in only one file, or whose tags do
# not have that form. There must be at least 4 such matching features.
# If there are more than 4, only the first 4 in sorted order of tag will
# be used.
#
# The script will also compute the input user and pixel coordinates of
# the point of {IMG_IN} that corresponds to the output user coordinates
# of each point listed in {OPTS_UMP_IN}. This information will be written to
# file {PTS_UMP_OUT} (or to standard error if {PTS_UMP_OUT} is "-").
# Each line of this file has fields
#
#   {TAG} {XI} {YI} {XIP} {YIP} {XOP} {IOP} {XO} {YO}
#
# where {(XI,YI)} and {(XO,YO)} are user coordinates, respectively input and output;
# and {(XIP,YIP)} and {(XOP,YOP)} are pixel coordinates, respectively input and output.
#
# If {IMG_OUT} is "NONE", this script will not compute or write the
# output image or its ref point neighborhood images
# "{NHOOD_PREF}-{TAG}.png". However, it will still read {IMG_IN},
# {IPTS_REF_IN}, {OPTS_REF_IN}, and {OPTS_UMP_IN}, compute the
# perpective projection map, and write the ref point mapping file
# {PTS_UMP_OUT}.
#
# Since image mapping is slow, when {IMG_OUT} is not "NONE" this script
# will still skip computing and writing {IMG_OUT} if the file exists and
# it is newer than {IMG_IN}, {IPTS_REF_IN}, and {OPTS_REF_IN}. In this case it
# will NOT generate the point mapping file {PTS_UMP_OUT}. CAUTION: this test
# will not notice if only the {OUNIT} or other mapping parameters have
# changed. In this case the user must explicitly delete {IMG_OUT} before
# calling this script.
#
# In any case, if {IMG_OUT} is not "NONE", for each point listed in
# {OPTS_REF_IN} and {OPTS_UMP_IN} this script will extract from the
# output image {IMG_OUT} a sub-image of fixed size (in pixels) centered
# at that point, and write that ref point neighborhood image to file
# "{NHOOD_PREF}-{TAG}.png", where {TAG} is the tag of that point in
# {OPTS_REF_IN}.
#

echo "${0##*/} args = $*" 1>&2

IMG_IN="$1"; shift
IPTS_REF_IN="$1"; shift
OPTS_REF_IN="$1"; shift
OUNIT="$1"; shift
IMG_OUT="$1"; shift
OPTS_UMP_IN="$1"; shift
PTS_UMP_OUT="$1"; shift
NHOOD_PREF="$1"; shift

tmp="/tmp/$$"

if [[ "/${IMG_OUT}" == "/NONE" ]]; then
  # Do not write the output file but do all computation:
  redoProjMap=1
  noImgOpt=( "-noImage" )
else
  # Redo computation and recreate {IMG_OUT} if source files changed:
  redoProjMap=`older_than.sh ${IMG_OUT} ${IMG_IN} ${IPTS_REF_IN} ${OPTS_REF_IN}`
  noImgOpt=( )
fi

if [[ ${redoProjMap} -ne 0 ]]; then
  
  echo "obtaining the coords of shared features ..." 1>&2
  itpfile="${tmp}-in.ipts"
  cat ${IPTS_REF_IN} | sed -e 's:^ *::g' | egrep -e '^[PQRS][A-Z0-9a-z_]*' | sort > ${itpfile}
  rtpfile="${tmp}-ref.pts"
  cat ${OPTS_REF_IN} | sed -e 's:^ *::g' | egrep -e '^[PQRS][A-Z0-9a-z_]*' | sort > ${rtpfile}
  jpfile="${tmp}-join.jpts"
  join -j 1 -o 0,1.2,1.3,2.2,2.3 ${itpfile} ${rtpfile} | head -n 4 > ${jpfile}
  cat ${jpfile} 1>&2
  
  # Get the ref points that are to be mapped back to the full image:
  unmapOpts=( \
    ` egrep -e '^[A-Z]' ${OPTS_UMP_IN} \
        | gawk '//{ print $1, $2, $3; }' \
        | sed -e 's:^:-unmap :g' \
    ` \
  )
  rm -f  ${PTS_UMP_OUT}
  
  timgfile="${tmp}-out.ppm"
  rm -f ${timgfile}
  echo "computing the projective map and mapping points from ${OPTS_UMP_IN} ..." 1>&2
  convert ${IMG_IN} PPM:- \
    | pnmprojmap \
        -xAxis right \
        -yAxis down \
        -iOrg 0 0 \
        -points \
          `cat ${jpfile} | gawk '// { print $2, $3; }'` \
          `cat ${jpfile} | gawk '// { print $4, $5; }'` \
          -oSize 3184 2960 \
          -oOrg 0 0 \
          -oUnit ${OUNIT} \
          -mapFile ${PTS_UMP_OUT} \
          ${unmapOpts[@]} \
          -undef 0.5 \
          -maxval 65535 \
          -isMask F \
          ${noImgOpt[@]} \
          -verbose \
    > ${timgfile}
    
  cat ${PTS_UMP_OUT} 1>&2
  
  if [[ "/${IMG_OUT}" == "/NONE" ]]; then
    echo "!! output image file and point map/unmap file are not generated" 1>&2
  elif [[ ! ( -s ${timgfile} ) ]]; then
    echo "** {pnmprojmap} failed to write the output image" 1>&2; exit 1 
  else
    echo "recreating mapped image ${IMG_OUT} ..." 1>&2
    rm -f ${IMG_OUT}
    cat ${timgfile} \
      | ppmtopgm \
      | pnmtopng \
      > ${IMG_OUT}
  fi
   
  rm ${itpfile} ${rtpfile} ${jpfile} ${timgfile}
  
else
  echo "!! file ${IMG_OUT} seems to be up-to-date, not recreated" 1>&2
fi

if [[ "/${IMG_OUT}" != "/NONE" ]]; then
  if [[ ! ( "/${IMG_OUT}" =~ *mask* ) ]]; then 
    echo "recreating the ${NHOOD_PREF}-{TAG}.png files ..." 1>&2
    mkdir -pv "${NHOOD_PREF%/*}"
    UWINRAD=72   # Window radius in user units.
    rm -f ${NHOOD_PREF}-*.png
    nhptfile="${tmp}-ump.opts"
    cat ${OPTS_REF_IN} ${OPTS_UMP_IN} \
      | sed -e 's:[ ][ ][ ]*: :g' -e 's:[ ][ ]*$::g' \
      | egrep -e '^[A-Z]' \
      | sort | uniq \
      > ${nhptfile}
    extract_pt_nhoods.sh ${IMG_OUT} ${nhptfile} ${UWINRAD} ${OUNIT} ${NHOOD_PREF}
    ls -l ${NHOOD_PREF}-*.png 1>&2
    rm ${nhptfile} 
  else
    echo "files ${NHOOD_PREF}-{TAG}.png suppressed for mask images" 1>&2
  fi
else
 echo "!! feature neighborhhod image clips were not re-created." 1>&2
fi
