#!/bin/bash
#
# Developed by Fred Weinhaus 9/20/2007 .......... revised 4/25/2015
#
# ------------------------------------------------------------------------------
#
# Licensing:
#
# Copyright © Fred Weinhaus
#
# My scripts are available free of charge for non-commercial use, ONLY.
#
# For use of my scripts in commercial (for-profit) environments or
# non-free applications, please contact me (Fred Weinhaus) for
# licensing arrangements. My email address is fmw at alink dot net.
#
# If you: 1) redistribute, 2) incorporate any of these scripts into other
# free applications or 3) reprogram them in another scripting language,
# then you must contact me for permission, especially if the result might
# be used in a commercial or for-profit environment.
#
# My scripts are also subject, in a subordinate manner, to the ImageMagick
# license, which can be found at: http://www.imagemagick.org/script/license.php
#
# ------------------------------------------------------------------------------
#
####
#
# USAGE: laplacian [-f filter] [t thresh] [-m mix] infile outfile
# USAGE: laplacian [-h or -help]
#
# OPTIONS:
#
# -f filter filter=1 to 5 (in order of increasing
# edge strength); default=1
# -t thresh threshold percent for binarization;
# thresh=integer 0 to 100; default="" (none)
# -m mix mixing percent with original image;
# mix=integer 0 to 100; default=100
# -h get help information
# -help get help information
#
###
#
# NAME: LAPLACIAN
#
# PURPOSE: To apply a Laplacian filter to an image.
#
# DESCRIPTION: LAPLACIAN generates an output image which is a user defined mix
# or blend of the original image and a Laplacian convolution filtered version
# of the image. This is achieved by forming a single convolution kernel whose
# weights depend upon a mixing of the Laplacian coefficients and the identity
# kernel. The script applies one of three different 3x3 Laplacian filters to
# an image to extract or enhance the edges in the image. It is a type of high
# pass filter which is constructed from the sum of the x and y second derivatives.
#
# The basic blended high pass filtering formula is F = (1-m)*I + m*L, where I
# is the original image, L is the Laplacian high pass filtered image and m =
# mix/100. When m=0, we get only the original image and when m=1, we get only
# the high pass Laplacian filtered image. For intermediate value of m, we
# get a blend of the image and the Laplacian high pass filtered image. Now,
# we can consider both I and L as a convolution of some kernel with the
# original image, namely I = i x I and L = l x I, where x means convolution.
# Note that a convolution is simply a weighted average of all the pixels in
# some neighborhood of a give pixel. Usually an odd sized neighborhood, such
# as 3x3, etc is used to prevent having the resulting image be shifted a
# fractional pixel. The convolution kernel values are simply the weights for
# the average. So here, i is the identity kernel, which is all zeroes, except
# the center of the kernel which has a value of 1. Similarly, l is one of the
# three Laplacian kernels. They are different forms of a high pass filter.
# Thus we can consider the final filtered image, F = f x I, where f = (1-m)*i
# + m*l. Consequently, we only have to do one convolution using the
# convolution kernel, f. Note, that all pure high pass filter convolution
# kernels will have weights that sum to 0. Also note, that the filters are
# actually the negatives of the true laplacian filters so that when mixed
# with the image they produce sharpening rather than blurring. For example,
# applying Laplacian 3 is equivalent the identity kernel minus the 3x3 average
# convolution kernel.
#
# OPTIONS:
#
# -f filter is the form of the filter. Three different 3x3 filters, one 5x5
# filter and one 7x7 filters are provided in order of the increasing edge
# strength they produce. Values for filter are 1 to 5. The default is filter=1.
#
# Laplacian 1
# 0 -1 0
# -1 4 -1
# 0 -1 0
#
# Laplacian 2
# -2 1 -2
# 1 4 1
# -2 1 -2
#
# Laplacian 3
# -1 -1 -1
# -1 8 -1
# -1 -1 -1
#
# Laplacian 4
# -4 -1 0 -1 -4
# -1 2 3 2 -1
# 0 3 4 3 0
# -1 2 3 2 -1
# -4 -1 0 -1 -4
#
# Laplacian 5
# -10 -5 -2 -1 -2 -5 -10
# -5 0 3 4 3 0 -5
# -2 3 6 7 6 3 -2
# -1 4 7 8 7 4 -1
# -2 3 6 7 6 3 -2
# -5 0 3 4 3 0 -5
# -10 -5 -2 -1 -2 -5 -10
#
# -t thresh is the thresholding percentage used to create a binary Laplacian
# edge image. Values range from 0 to 100. A higher value will result in
# fewer edges in the resulting image.
#
# -m mix is the percentage mixing factor used to blend the Laplacian with
# the original image. A value of mix=0, results in the original image. A
# value of mix=100 results in a pure Laplacian filtered image.
#
# CAVEAT: No guarantee that this script will work on all platforms,
# nor that trapping of inconsistent parameters is complete and
# foolproof. Use At Your Own Risk.
#
######
#
#
# set default params
filter=1
mix=100
thresh=""
# set directory for temporary files
dir="." # suggestions are dir="." or dir="/tmp"
# define Laplacian filters
# Laplacian 1 (4-neighbor)
# 0 -1 0
# -1 4 -1
# 0 -1 0
L1="0,-1,0,-1,4,-1,0,-1,0"
# Laplacian 2 (d2x + d2y)
# -2 1 -2
# 1 4 1
# -2 1 -2
L2="-2,1,-2,1,4,1,-2,1,-2"
# Laplacian 3 (8-neighbor)
# -1 -1 -1
# -1 8 -1
# -1 -1 -1
L3="-1,-1,-1,-1,8,-1,-1,-1,-1"
# Laplacian 4 (d2x + d2y)
# -4 -1 0 -1 -4
# -1 2 3 2 -1
# 0 3 4 3 0
# -1 2 3 2 -1
# -4 -1 0 -1 -4
L4="-4,-1,0,-1,-4,-1,2,3,2,-1,0,3,4,3,0,-1,2,3,2,-1,-4,-1,0,-1,-4"
# Laplacian 5 (d2x + d2y)
# -10 -5 -2 -1 -2 -5 -10
# -5 0 3 4 3 0 -5
# -2 3 6 7 6 3 -2
# -1 4 7 8 7 4 -1
# -2 3 6 7 6 3 -2
# -5 0 3 4 3 0 -5
# -10 -5 -2 -1 -2 -5 -10
L5="-10,-5,-2,-1,-2,-5,-10,-5,0,3,4,3,0,-5,-2,3,6,7,6,3,-2,-1,4,7,8,7,4,-1,-2,3,6,7,6,3,-2,-5,0,3,4,3,0,-5,-10,-5,-2,-1,-2,-5,-10"
# set up functions to report Usage and Usage with Description
PROGNAME=`type $0 | awk '{print $3}'` # search for executable on path
PROGDIR=`dirname $PROGNAME` # extract directory of program
PROGNAME=`basename $PROGNAME` # base name of program
usage1()
{
echo >&2 ""
echo >&2 "$PROGNAME:" "$@"
sed >&2 -e '1,/^####/d; /^###/g; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
}
usage2()
{
echo >&2 ""
echo >&2 "$PROGNAME:" "$@"
sed >&2 -e '1,/^####/d; /^######/g; /^#/!q; s/^#*//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME"
}
# function to report error messages
errMsg()
{
echo ""
echo $1
echo ""
usage1
exit 1
}
# function to test for minus at start of value of second part of option 1 or 2
checkMinus()
{
test=`echo "$1" | grep -c '^-.*$'` # returns 1 if match; 0 otherwise
[ $test -eq 1 ] && errMsg "$errorMsg"
}
# test for correct number of arguments and get values
if [ $# -eq 0 ]
then
# help information
echo ""
usage2
exit 0
elif [ $# -eq 3 -o $# -eq 5 -o $# -eq 7 -o $# -gt 8 ]
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
;;
-f) # get filter
shift # to get the next parameter - filter
# test if parameter starts with minus sign
errorMsg="--- INVALID LAPLACIAN FILTER SPECIFIED ---"
checkMinus "$1"
filter="$1"
# test filter values
[ $filter -lt 1 -o $filter -gt 5 ] && errMsg "--- FILTER=$filter IS NOT A VALID VALUE ---"
;;
-t) # get thresh
shift # to get the next parameter - thresh
# test if parameter starts with minus sign
errorMsg="--- INVALID THRESHOLD SPECIFICATION ---"
checkMinus "$1"
# test thresh values
thresh=`expr "$1" : '\([0-9]*\)'`
[ "$thresh" = "" ] && errMsg "--- THRESH=$thresh MUST BE AN INTEGER ---"
threshtestA=`echo "$mix < 0" | bc`
threshtestB=`echo "$mix > 100" | bc`
[ $threshtestA -eq 1 -o $threshtestB -eq 1 ] && errMsg "--- THRESH=$thresh MUST BE AN INTEGER BETWEEN 0 AND 100 ---"
;;
-m) # get mix
shift # to get the next parameter - mix
# test if parameter starts with minus sign
errorMsg="--- INVALID MIX SPECIFICATION ---"
checkMinus "$1"
# test mix values
mix=`expr "$1" : '\([0-9]*\)'`
[ "$mix" = "" ] && errMsg "--- MIX=$mix MUST BE AN INTEGER ---"
mixtestA=`echo "$mix < 0" | bc`
mixtestB=`echo "$mix > 100" | bc`
[ $mixtestA -eq 1 -o $mixtestB -eq 1 ] && errMsg "--- MIX=$mix MUST BE AN INTEGER BETWEEN 0 AND 100 ---"
;;
-) # STDIN and end of arguments
break
;;
-*) # any other - argument
errMsg "--- UNKNOWN OPTION ---"
;;
*) # end of arguments
break
;;
esac
shift # next option
done
#
# get infile and outfile
infile="$1"
outfile="$2"
fi
# test that infile provided
[ "$infile" = "" ] && errMsg "--- NO INPUT FILE SPECIFIED ---"
# test that outfile provided
[ "$outfile" = "" ] && errMsg "--- NO OUTPUT FILE SPECIFIED ---"
# test if image an ordinary, readable and non-zero size
if [ -f $infile -a -r $infile -a -s $infile ]
then
: 'do nothing - proceed'
else
errMsg "--- FILE $infile DOES NOT EXIST OR IS NOT AN ORDINARY FILE, NOT READABLE OR HAS ZERO SIZE ---"
exit 1
fi
# setup temporary images and auto delete upon exit
tmp0="$dir/laplacian_0_$$.miff"
trap "rm -f $tmp0;" 0
trap "rm -f $tmp0; exit 1" 1 2 3 15
trap "rm -f $tmp0; exit 1" ERR
# 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`
# specify filter
# get filter
[ $filter -eq 1 ] && lap=$L1
[ $filter -eq 2 ] && lap=$L2
[ $filter -eq 3 ] && lap=$L3
[ $filter -eq 4 ] && lap=$L4
[ $filter -eq 5 ] && lap=$L5
# convert 1D laplacian to array for later use
kern=`echo $lap | sed 's/,/ /g'`
kernArr=($kern)
num=${#kernArr[*]}
width=`echo "scale=0; sqrt($num)" | bc -l`
# print 2D laplacian
k=0
for ((i=0; i