Wrapper script for ImageJ

Description

A command line wrapper script to launch ImageJ on Linux / Mac OS / Cygwin platforms, with the following features

  • Open images from the command line
  • Launch macros / execute commands
  • Send images / commands to running ImageJ windows
  • Set available memory

Author

Jonathan Jackson (jjackson a.t familyjackson dot net)

Change List

  • 2008-12-04: First posted on the ImageJ Wiki
  • 2009-08-21: Now works with MacOS X (tested on 10.5). Fixed bug with assignment of lock files to open ports.

Limitations

  • Can't open files in existing ImageJ panel on MacOS X
  • Not tested on Cygwin

Usage

#!/bin/bash 
# A wrapper script to launch imagej from the UNIX command line
# Images given as arguments will be opened, macros may also be given as arguments
# Looks for macros in the imagej macro directory
#
# This program is free software, but comes with no warrenty or guarantee
# send bug reports or feedback to jjackson at familyjackson dot net
# Author: Jon Jackson
# Last modified date: $Date: 2008-12-04 18:38:47 +0000 (Thu, 04 Dec 2008) $
# $Revision: 64 $
#
# INSTALLATION INSTRUCTIONS
#
### WARNING ###########################################################
# This file won't run if edited in 'Notepad' or similar programs
# that don't support unix new line characters
#######################################################################

# Source location: http://rsb.info.nih.gov/ij/download/linux/unix-script.txt
# 1) Save this script in the ImageJ directory as 'imagej'
# 2) Modify path variables according to your system 
# 3) Give the new file execute permission
# 4) Be sure the 'imagej' wrapper is in the 'PATH' 

### DEPENDENCIES ######################################################
# readlink: This script works better with GNU readlink, which is not 
#           installed by default on MacOS (or Solaris 8)
# MacOS X:  readlink can be installed with 'MacPorts': 
#             Install MacPorts and Apple xCode, then run
#             'sudo port install coreutils' and set the PATH variable appropriately

# ALL:      If no version of readlink is avaliable, hard code the ij_path and ij_jar_paths
#           sending images to a running ImageJ panel may not work (around line 357)
#######################################################################

# setup environment
set +u # don't give error for unset variables (matters for environment variables)
shopt -s extglob # allow extended pattern matching
shopt -s expand_aliases

# Detect readlink version 
# if GNU readlink is known to be installed, this code can be replaced by "alias ReadLink='readlink -f'"
alias ReadLink='readlink' # Default
if [[ $(uname) == Darwin ]] && which greadlink &>/dev/null ; then  
	# Using GNU readlink on MacOS X
	alias ReadLink='greadlink -f'
elif [[ -f $(which readlink) ]] && readlink --version | grep coreutils &>/dev/null ; then
	alias ReadLink='readlink -f'  # use GNU readlink
else
	alias ReadLink='echo'
	echo "Please install GNU readlink. Symbolic links may not be resolved properly" >&2
fi

############ SITE SPECIFIC VARIABLES #########################
# Trailing / is not required for path variables
# IMAGEJ PATH - production installation
#ij_path='/local/ImageJ'
ij_path="$(dirname $(ReadLink $0))"
# IMAGEJ PATH - development installation (optional)
ij_path_dev='/Users/jjackson/ImageJ'
# JAVA PATH
# assumes executable is ${java_home}/bin/java
# set java_home variables ='' to use JAVA_HOME environment variable
# Get a sensible default
if which java > /dev/null ; then
	java_home="$(dirname $(ReadLink $(which java)))"
fi
# The following allow platform specific defaults for the java path
# these take preference over the default if set
if [[ -d /usr/java/jdk1.6_x64 ]] ; then
    java_home='/usr/java/jdk1.5'
    java_home_Linux_x86_64='/usr/java/jdk1.5'
    java_home_dev='/usr/java/jdk1.6_x64'
else
    # Optionally specify java path for all available OS / architecture combinations
    java_home_Linux="${ij_path}/jre"
    java_home_Linux_x86_64="${ij_path}/jre64"
    java_home_SunOS="${ij_path}/jre64"
    java_home_Darwin=""
    # 
fi
ijadmin='j.jackson AT ion.ucl.ac.uk'
# DOCUMENTATION URL
#doc_url='http://rsb.info.nih.gov/ij/'
doc_url='http://saturn/~jjackson/imagej'
# TEMP FOLDER
ij_tmp='/tmp/imagej'
# LOG FILE
ij_log="${ij_tmp}/log.txt"
# default behaviour when an ImageJ window is already open
newwindow='true'
#newwindow='false'
# macro argument delimiter character
separator=':'
# a ' ' may work provided no arguments would contain spaces
# use macro functions:  args=getArgument(); argArray=split(args, ':'); 
# to recover macro arguments

############ DEFAULT MEMORY SETTINGS  #########################

declare -i default_mem=900
declare -i min_mem=32
declare -i max_32bit=1800 # empirical
declare -i max_64bit=17000000000 # conservative

############ SITE SPECIFIC MODULES #########################

# JAR libraries for additional modules may be placed in ${ij_path}/lib
# jmf.jar jai_codec.jar jai_core.jar mlibwrapper_jai.jar

# Native libraries may be placed in ${ij_path}/lib/$OS
# where OS matches the output of the 'uname' command

############ END SITE SPECIFIC VARIABLES #########################

OS=$(uname)
processor=$(uname -m) # -p doesn't work on ubuntu
declare -i mem
declare -i max_mem
declare -i free_mem

java_home="${java_home:-$JAVA_HOME}"

if [[ "$OS" == 'SunOS' ]] ; then
    java_arch='-d64'
    JAVA_HOME="${java_home_SunOS:-$java_home}"    
    max_mem=`vmstat | awk 'BEGIN{maxMem='$max_64bit'} NR == 3 {fmem=int($5 / 1024); if (fmem < maxMem) {print fmem} else {print maxMem}}'`
    free_mem="max_mem"
    mem=${free_mem}/2
elif [[ "$OS" == 'Linux' ]] ; then
    if [[ "$processor" == 'x86_64' ]] ; then
        java_arch='-d64'
        JAVA_HOME="${java_home_Linux_x86_64:-$java_home}"
        max_mem=`free | awk -v maxMem=$max_64bit 'NR == 2 {fmem=int($2 / 1024); if (fmem < maxMem) {print fmem} else {print maxMem}}'`
        free_mem=`free | awk -v maxMem=$max_64bit 'NR == 3 {fmem=int($4 / 1024); if (fmem < maxMem) {print fmem} else {print maxMem}}'`
    else
        java_arch='-d32'
        JAVA_HOME="${java_home_Linux:-$java_home}"
        max_mem=`free | awk -v maxMem=$max_32bit 'NR == 2 {fmem=int($2 / 1024); if (fmem < maxMem) {print fmem} else {print maxMem}}'`
        free_mem=`free | awk -v maxMem=$max_32bit 'NR == 3 {fmem=int($4 / 1024); if (fmem < maxMem) {print fmem} else {print maxMem}}'`
    fi
elif [[ "$OS" == Darwin ]] ; then
    java_arch='-d64'
    JAVA_HOME="${java_home_Darwin:-$java_home}"
	max_mem=`vm_stat | awk 'NR == 2 {free=$3} NR == 3 {active=$3} NR == 4 {inactive=$3} END{print int((free+active+inactive)/256)}'`
    free_mem=`vm_stat | awk 'BEGIN{maxMem='$max_64bit'} NR == 2 {fmem=int($3 / 256)} NR == 4 {imem=int($3 / 256)} END{amem=fmem+imem ; if (amem < maxMem) {print amem} else {print maxMem}}'`
fi
mem=${free_mem}/3*2
(( $mem > $default_mem || $mem < $min_mem )) && mem=$default_mem 

if [[ -f ${JAVA_HOME}/bin/java ]] ; then
	JAVA=${JAVA_HOME}/bin/java
else
	JAVA=${JAVA_HOME}/java
	unset JAVA_HOME # confuses Mac java
fi

# if tools.jar is not in ${ij_path}/jre/lib/ext/ edit the 'tools=' line 
# to point to tools.jar. The -compile switch will load tools.jar into the 
# classpath and enable plugins to be compiled in imagej

if [[ -f "${ij_path}/tools.jar" ]] ; then
    tools="${ij_path}/tools.jar"
else
    tools=''
fi

# End Site specific variables ---------------------------------------------------------

# other variables 
dir=`pwd`
user=`whoami`
host=`hostname`  # `hostname -s` is better but not solaris 8 compat.
if [[ -z "$DISPLAY" ]] ; then
    echo 'Display variable not set'
    echo 'If ImageJ fails to load, try '
    echo '% setenv DISPLAY yourcomputer:0'
    echo 'if you use the "csh" or for "bash" try'
    echo '% export DISPLAY=yourcomputer:0'
    display='default' 
else
    display="${DISPLAY##*/}" 
    # display variable on Darwin can look like this: '/tmp/launch-si9S06/:0'
fi

declare -i port=0
declare -i verbosity=0
declare -i max_attempts=10
images=''
macrocmd=''
macroargs=''

function usage {
    echo 
    echo 'Image display and analysis program. Opens formats including:'
    echo 'UNC, Analyze, Dicom, NIFTI, Tiff, Jpeg, Gif, PNG ...'  
    echo
    echo 'imagej [options] image [ image2 ... image3 ]'
    echo '    -h        print help and more options'
    echo '    -o        open images in an open ImageJ panel'
    echo '    -p <N>    open images in ImageJ panel number <N>' 
    echo "    -x <MB>   set available memory (default=${mem} max=${max_mem})"     
    echo 
}

function fullusage {
    echo 
    echo 'Image display and analysis program. Opens formats including:'
    echo 'UNC, Analyze, Dicom, NIFTI, Tiff, Jpeg, Gif, PNG ...'  
    echo 
    echo 'imagej [options] image [ image2 ... image3 ] -> open images'
    echo
    echo 'basic options:'
    echo '  -h        print help and more options'
    echo '  -o        open images in existing ImageJ panel if one exists'
    echo '  -p <N>    open images in existing ImageJ panel number <N>' 
    echo "  -x <MB>   set available memory (default=${mem} max=${max_mem})"
    echo
    echo 'advanced options:'
    echo '  -c        enable plugin compilation within imagej'
    echo '  -d        use development version'
    echo "  -j        use development java version ($java_home_dev)"
    echo '  -v        be verbose (vv or vvv increases verbosity)'  
    echo
    echo 'options for batch processing:'
    echo "  -e 'Macro Code'            execute macro code"
    echo "  -r 'Menu Command'          run menu command"
    echo "Quotation marks '' are required around commands including spaces"
    echo 'Commands can be sent to open ImageJ panels with the -p option'
    echo 
    echo 'options for macros:'
    echo 'imagej [-i image] [-b|-m] [arg1 ... argN] '
    echo '  -b macro    run macro without graphics window' 
    echo '  -m macro    run macro' 
    echo '"image" will be opened before macro is run'
    echo 'all following arguments are passed to macro'        
    echo 
    echo "Documentation - $doc_url "
    echo "Report problems with this software to $ijadmin"
    echo
}

function macroCmdError {
    fullusage 
    echo 'Only one command option (-b -e -m OR -r) may be specified' 1>&2
    echo "Macro commands can't be used with the '-o' option"  1>&2
    exit 1
}

# parse input arguments
while getopts b:cde:hi:jm:nop:r:vx: options
do
    case $options in
        b)    [[ -n "$macrocmd" ]] && macroCmdError # exits
            macrocmd="-batch ${OPTARG}"
            display='batch'
            newwindow='false'
            ;;
        c)    modules="${modules:-}${modules+:}${tools}"
            ;;
        d)    ij_path="$ij_path_dev"
            ;;
        e)  [[ -n "$macrocmd" ]] && macroCmdError # exits
            macrocmd='-eval'
            macroargs="'${OPTARG}'"
            ;;
        h)  fullusage
            exit 0
            ;;
        i)  images="${images}'${OPTARG}' "
            ;;
        j)  JAVA_HOME="$java_home_dev"
            ;;
        m)    [[ -n "$macrocmd" ]] && macroCmdError # exits
            macrocmd="-macro ${OPTARG}"
            ;;
        n)  newwindow='true'
            ;;
        o)  [[ -n "$macrocmd" ]] && macroCmdError # exits
            newwindow='false'
            ;;
        p)    newwindow='false'
            port="${OPTARG}"
            if (( "$port" < 1 || "$port" > 99 )) ; then
                echo "${OPTARG} is not a permissible value for port number (-p)" 1>&2
                exit 1
            fi
            ;;
        r)    [[ -n "$macrocmd" ]] && macroCmdError # exits
            macrocmd='-run'
            macroargs="'${OPTARG}'"
            ;;
        v)    verbosity=verbosity+1
            if (( $verbosity == 2 )) ; then set -x ; fi
            if (( $verbosity == 3 )) ; then set -v ; fi
            ;;
        x)    mem="${OPTARG}"
            newwindow='true'
            if (( $mem < $min_mem || $mem > $max_mem )) ; then
                echo "${OPTARG} is not a permissible value for memory (-x)" 1>&2
                echo "min=${min_mem}, max=${max_mem}" 1>&2
                exit 1                
            fi
            ;;
        \?) usage
            exit 1 
            ;;
    esac
done
            
declare -i i=1
while (( i < $OPTIND )) ; do
    shift
    i=i+1
done

if [[ "$#" == 0 && -z "$macrocmd" ]] ; then
    usage
fi    

# The best way to install .jar libraries required by plugins is to copy them 
# to the imagej plugins/jars directory
# Alternatively, either copy them to ${ij_path}/jre/lib/ext/ or add the .jar
# filepath to the modules line below. Paths are separated by a colon
# Classpath must follow command line arguments, as ij_path is dependent on the -d option

# Resolving ij.jar path.  If ij.jar is a symbolic link to ij_<version>.jar
# this allows updating ij.jar without crashing running sessions
ij_jar_path=$(ReadLink  ${ij_path}/ij.jar)    
                                         
for mod_jar in ${ij_path}/lib/*jar  ; do
    modules="${modules:-}${modules+:}$mod_jar"
done
modules="-cp ${ij_jar_path}:${modules+:}${modules:-}"
#${ij_path}/plugins/jars/dcmie.jar

export LD_LIBRARY_PATH="${ij_path}/lib/${OS}_$processor"
if (( $verbosity > 0 )) ; then
    echo "LD_LIBRARY_PATH=$LD_LIBRARY_PATH"
fi

# -b and -m options only:
# remaining command line arguments are passed as macro arguments 
# separated by $separator
if [[ -n "$macrocmd" && -z "$macroargs" ]] ; then
    while (( "$#" > 0 )) ; do
        if [[ -z "$macroargs" ]] ; then 
            macroargs="${1}"
        else
            macroargs="${macroargs}${separator}${1}"
        fi
        shift         
    done
    macroargs="'$macroargs'"
    # if user hasn't requested a specific port number
    # and it's not a batch mode macro, set display to 'macro'
    # to prevent other instances accessing this ImageJ window
    if [[ "$display" != "batch" && "$port" == 0 ]] ; then
        display="macro"
        newwindow='true' # should be redundant
    fi
fi

# PROTECT POSSIBLE SPACES IN IMAGE FILENAMES
if (( "$#" > 0 )) ; then
    while (( "$#" > 0 )) ; do
        filearg="${1}"
        if [[ ! -f "$filearg" ]] ; then
            echo "Warning: image '$filearg' may not exist" >&2
        fi
        # full file path required when sending images to running ImageJ panel
        if [[ "${newwindow}" == 'false' && -f "${filearg}" ]]  && ! expr "${filearg}" : '/.*' > /dev/null; then 
            filearg="$(ReadLink ${filearg})"
        fi
        images="${images}'$filearg' "
        shift 
    done
fi    

# CREATE IMAGEJ SOCKET-LOCK DIRECTORY IF NON EXISTANT 
if [[ ! -d "$ij_tmp" ]] ; then
    mkdir "$ij_tmp"
    chmod 777 "$ij_tmp"
fi

# CREATE IMAGEJ LOG FILE IF NON EXISTANT 
if [[ -n "$ij_log" && ! -f "$ij_log" ]] ; then
    touch "$ij_log"
    chmod 666 "$ij_log"
fi

# CREATES A TEMP FILE INDICATING A PORT IS IN USE BY IMAGEJ
cd "$ij_tmp"
declare -i count=1
portopen='false'
lockFileCreated='false'
declare -a locklist=(`ls | grep '[0-9][0-9]-.*'`)

[[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tNew Window = $newwindow" >> "$ij_log" 2> /dev/null
[[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tPort = $port" >> "$ij_log" 2> /dev/null
[[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tlocklist: \n ${locklist[*]}" >> "$ij_log" 2> /dev/null
if (( $verbosity > 0 )) ; then echo -e "locklist: \n ${locklist[*]}" ; fi

# PORT SPECIFIED BY USER
if (( $port > 0 )) ; then
    # look for a lock on the port specified
    for lockname in ${locklist[*]} ; do
        prefix=`printf '%02u' $port`
        if [[ "$lockname" == ${prefix}-${user}-${host}* ]] ; then
            # found lock on the requested port, owned by user on current display
            portopen='true'
            if (( $verbosity > 0 )) ; then echo "Using socket lock: $lockname" ; fi
            count=$port
            break
        elif [[ "$lockname" == ${prefix}-* ]] ; then
            echo "Port $port is in use by some other user or a different host" 1>&2
            if (( $verbosity > 0 )) ; then echo "Port lock: $lockname" ; fi
            exit 1
        fi
    done 
    # specified port not in use 
    count=$port

# IF EXISTING WINDOW IS REQUESTED, LOOK FOR LISTENING PORT
elif [[ "$newwindow" == 'false' && ${#locklist} != 0 ]] ; then
    # look for a lock on the current display for this user
    for lockname in ${locklist[*]} ; do
        if [[ "$lockname" == [0-9][0-9]-${user}-${host}-${display} ]] ; then
            portopen='true'
            if (( $verbosity > 0 )) ; then echo "Found socket lock: $lockname" ; fi
            # if a matching user/display is found, use this one
            count="${lockname%-*-*-*}"
            #count=`echo $lockname | sed  -e 's/^\([0-9][0-9]\).*/\1/' -e 's/^0//'` # csh?
            break
        fi
    done
fi

# IF A NEW PORT IS TO BE USED
declare -i attempts=0
while [[ "$portopen" == 'false' ]] ; do
    # new window requested or no matching port found
    # if port is not specified, look for first free port
    if (( "$port" == 0 )) ; then 
        if (( ${#locklist} == 0 )) ; then
            # no active locks - use first port
            count=1
        else
            # active locks - check each port number so see if it is in use
            # this is not synchronised!!
            count=0
            inuse='true'
            while [[ "$inuse" == 'true' ]] ; do
                count=count+1
                prefix=`printf '%02u' $count`
                inuse='false'
                for lockname in ${locklist[*]} ; do
                    if [[ "$lockname" == ${prefix}-* ]] ; then
                        inuse='true'
                    fi
                done
            done
        fi
    fi
    # CREATING A NEW PORT LOCK
    prefix=`printf '%02u' $count`
    lockname=${prefix}-${user}-${host}-${display}
    
    [[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tCreating lock\t$lockname" >> "$ij_log" 2> /dev/null
    (( $verbosity > 0 )) && echo -n "creating lock $lockname ... " 
    echo $$ > $lockname 2> /dev/null
    if [[ "$(head -n 1 $lockname 2> /dev/null)" == $$ ]] ; then # port lock successful
        trap '\rm -f ${ij_tmp}/$lockname >/dev/null ; [[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tReleasing lock\t$lockname" >> "$ij_log" 2> /dev/null' EXIT TERM KILL 
        # Quitting ImageJ sends EXIT, as does a kill/kill -9 
        # CTRL+C in terminal sends INT + EXIT
        # System shutdown sends TERM (+EXIT??)
        
        portopen='true'
        
        if (( $verbosity > 0 )) ; then  echo 'done' ; fi

        lockFileCreated='true'
        if [[ -z "$macrocmd" ]] ; then 
            echo 'Open other images in this ImageJ panel as follows:'
            echo "  imagej -p $count <image1> [<image2> ... <imageN>]"
        fi
        [[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tSocket lock:\t$lockname" >> "$ij_log" 2> /dev/null
        if (( $verbosity > 0 )) ; then echo "Socket lock: $lockname" ; fi
        echo
    else # port lock unsuccessful (simultaneous instances?)
        [[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tAttempted lock failed\t$lockname" >> "$ij_log" 2> /dev/null
        if (( $attempts > $max_attempts )) ; then
            echo "Failed to secure a port lock for ImageJ after $max_attempts" >&2
            [[ -n "$ij_log" ]] && echo -e "$$\t$(date)\tGiving up\t$lockname" >> "$ij_log" 2> /dev/null
        fi
        # REREAD LOCK LIST
        declare -a locklist=(`ls | grep '[0-9][0-9]-.*'`)
    fi
    attempts=$attempts+1
done

if (( $verbosity > 0 )) ; then
    echo ${JAVA} ${java_arch} -mx${mem}m ${modules} ij.ImageJ -ijpath ${ij_path} -port${count} ${images} ${macrocmd} ${macroargs}
fi

cd "$dir"
eval "${JAVA} ${java_arch} -mx${mem}m ${modules} ij.ImageJ -ijpath ${ij_path} -port${count} ${images} ${macrocmd} ${macroargs} "
exit 0
diverse/commandline/imagej.txt · Last modified: 2010/01/26 11:07 (external edit)
Back to top
CC Attribution-Noncommercial-Share Alike 3.0 Unported
chimeric.de = chi`s home Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0