changeset 0:802c21d935e7 draft

planemo upload commit 378c3ecfbaed0330faf197811d379431b0f9e92c-dirty
author iuc
date Sun, 11 Oct 2015 13:29:38 -0400
parents
children f00fd3aa6472
files imagej2_base_utils.py imagej2_find_maxima.py imagej2_find_maxima.xml imagej2_macros.xml jython_script.py jython_utils.py test-data/blobs.gif test-data/blobs_count.tabular test-data/blobs_list.tabular test-data/blobs_segmented.gif test-data/blobs_single_points.gif test-data/blobs_tolerance.gif tool_dependencies.xml
diffstat 13 files changed, 742 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imagej2_base_utils.py	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,164 @@
+import os
+import shutil
+import sys
+import tempfile
+
+FIJI_JAR_DIR = os.environ.get( 'FIJI_JAR_DIR', None )
+FIJI_OSX_JAVA3D_DIR = os.environ.get( 'FIJI_OSX_JAVA3D_DIR', None )
+FIJI_PLUGIN_DIR = os.environ.get( 'FIJI_PLUGIN_DIR', None )
+FIJI_ROOT_DIR = os.environ.get( 'FIJI_ROOT_DIR', None )
+
+BUFF_SIZE = 1048576
+
+def cleanup_before_exit( tmp_dir ):
+    """
+    Remove temporary files and directories prior to tool exit.
+    """
+    if tmp_dir and os.path.exists( tmp_dir ):
+        shutil.rmtree( tmp_dir )
+
+def get_base_cmd_bunwarpj( jvm_memory ):
+    if FIJI_JAR_DIR is not None and FIJI_PLUGIN_DIR is not None:
+        if jvm_memory in [ None, 'None' ]:
+            jvm_memory_str = ''
+        else:
+            jvm_memory_str = '-Xmx%s' % jvm_memory
+        bunwarpj_base_cmd = "java %s -cp %s/ij-1.49k.jar:%s/bUnwarpJ_-2.6.1.jar bunwarpj.bUnwarpJ_" % \
+            ( jvm_memory_str, FIJI_JAR_DIR, FIJI_PLUGIN_DIR )
+        return bunwarpj_base_cmd
+    return None
+
+def get_base_command_imagej2( memory_size=None, macro=None, jython_script=None ):
+    imagej2_executable = get_imagej2_executable()
+    if imagej2_executable is None:
+        return None
+    cmd = '%s --ij2 --headless --debug' % imagej2_executable
+    if memory_size is not None:
+        memory_size_cmd = ' -DXms=%s -DXmx=%s' % ( memory_size, memory_size )
+        cmd += memory_size_cmd
+    if macro is not None:
+        cmd += ' --macro %s' % os.path.abspath( macro )
+    if jython_script is not None:
+        cmd += ' --jython %s' % os.path.abspath( jython_script )
+    return cmd
+
+def get_file_extension( image_format ):
+    """
+    Return a valid bioformats file extension based on the received
+    value of image_format( e.g., "gif" is returned as ".gif".
+    """
+    return '.%s' % image_format
+
+def get_file_name_without_extension( file_path ):
+    """
+    Eliminate the .ext from the received file name, assuming that
+    the file name consists of only a single '.'.
+    """
+    if os.path.exists( file_path ):
+        path, name = os.path.split( file_path )
+        name_items = name.split( '.' )
+        return name_items[ 0 ]
+    return None
+
+def get_imagej2_executable():
+    """
+    Fiji names the ImageJ executable different names for different
+    architectures, so figure out which name we need.
+    """
+    platform_dict = get_platform_info_dict()
+    if platform_dict.get( 'architecture', None ) in [ 'x86_64' ]:
+        if platform_dict.get( 'os', None ) in [ 'darwin' ]:
+            return 'ImageJ-macosx'
+        if platform_dict.get( 'os', None ) in [ 'linux' ]:
+            return 'ImageJ-linux64'
+    return None
+    
+def get_input_image_path( tmp_dir, input_file, image_format ):
+    """
+    Bioformats uses file extensions (e.g., .job, .gif, etc)
+    when reading and writing image files, so the Galaxy dataset
+    naming convention of setting all file extensions as .dat
+    must be handled.
+    """
+    image_path = get_temporary_image_path( tmp_dir, image_format )
+    # Remove the file so we can create a symlink.
+    os.remove( image_path )
+    os.symlink( input_file, image_path )
+    return image_path
+
+def get_platform_info_dict():
+    '''Return a dict with information about the current platform.'''
+    platform_dict = {}
+    sysname, nodename, release, version, machine = os.uname()
+    platform_dict[ 'os' ] = sysname.lower()
+    platform_dict[ 'architecture' ] = machine.lower()
+    return platform_dict
+
+def get_stderr_exception( tmp_err, tmp_stderr, tmp_out, tmp_stdout, include_stdout=False ):
+    tmp_stderr.close()
+    """
+    Return a stderr string of reasonable size.
+    """
+    # Get stderr, allowing for case where it's very large.
+    tmp_stderr = open( tmp_err, 'rb' )
+    stderr_str = ''
+    buffsize = BUFF_SIZE
+    try:
+        while True:
+            stderr_str += tmp_stderr.read( buffsize )
+            if not stderr_str or len( stderr_str ) % buffsize != 0:
+                break
+    except OverflowError:
+        pass
+    tmp_stderr.close()
+    if include_stdout:
+        tmp_stdout = open( tmp_out, 'rb' )
+        stdout_str = ''
+        buffsize = BUFF_SIZE
+        try:
+            while True:
+                stdout_str += tmp_stdout.read( buffsize )
+                if not stdout_str or len( stdout_str ) % buffsize != 0:
+                    break
+        except OverflowError:
+            pass
+    tmp_stdout.close()
+    if include_stdout:
+        return 'STDOUT\n%s\n\nSTDERR\n%s\n' % ( stdout_str, stderr_str )
+    return stderr_str
+
+def get_temp_dir( prefix='tmp-imagej-', dir=None ):
+    """
+    Return a temporary directory.
+    """
+    return tempfile.mkdtemp( prefix=prefix, dir=dir )
+
+def get_tempfilename( dir=None, suffix=None ):
+    """
+    Return a temporary file name.
+    """
+    fd, name = tempfile.mkstemp( suffix=suffix, dir=dir )
+    os.close( fd )
+    return name
+
+def get_temporary_image_path( tmp_dir, image_format ):
+    """
+    Return the path to a temporary file with a valid image format
+    file extension that can be used with bioformats.
+    """
+    file_extension = get_file_extension( image_format )
+    return get_tempfilename( tmp_dir, file_extension )
+
+def handle_none_type( val, val_type='float' ):
+    if val is None:
+        return ' None'
+    else:
+        if val_type == 'float':
+            return ' %.3f' % val
+        elif val_type == 'int':
+            return ' %d' % val
+    return ' %s' % val
+
+def stop_err( msg ):
+    sys.stderr.write( msg )
+    sys.exit( 1 )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imagej2_find_maxima.py	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+import argparse
+import os
+import shutil
+import subprocess
+import tempfile
+import imagej2_base_utils
+
+parser = argparse.ArgumentParser()
+parser.add_argument( '--input', dest='input', help='Path to the input file' )
+parser.add_argument( '--input_datatype', dest='input_datatype', help='Datatype of the input image' )
+parser.add_argument( '--scale_when_converting', dest='scale_when_converting', help='Scale when converting RGB image' )
+parser.add_argument( '--weighted_rgb_conversions', dest='weighted_rgb_conversions', help='Weighted RGB conversions for RGB image' )
+parser.add_argument( '--noise_tolerance', dest='noise_tolerance', type=int, help='Noise tolerance' )
+parser.add_argument( '--output_type', dest='output_type', help='Output type' )
+parser.add_argument( '--exclude_edge_maxima', dest='exclude_edge_maxima', help='Exclude edge maxima' )
+parser.add_argument( '--light_background', dest='light_background', help='Light background' )
+parser.add_argument( '--jython_script', dest='jython_script', help='Path to the Jython script' )
+parser.add_argument( '--output', dest='output', help='Path to the output file' )
+parser.add_argument( '--output_datatype', dest='output_datatype', help='Datatype of the output image' )
+args = parser.parse_args()
+
+tmp_dir = imagej2_base_utils.get_temp_dir()
+# ImageJ expects valid image file extensions, so the Galaxy .dat extension does not
+# work for some features.  The following creates a symlink with an appropriate file
+# extension that points to the Galaxy dataset.  This symlink is used by ImageJ.
+tmp_input_path = imagej2_base_utils.get_input_image_path( tmp_dir, args.input, args.input_datatype )
+tmp_output_path = imagej2_base_utils.get_temporary_image_path( tmp_dir, args.output_datatype )
+
+# Define command response buffers.
+tmp_out = tempfile.NamedTemporaryFile().name
+tmp_stdout = open( tmp_out, 'wb' )
+tmp_err = tempfile.NamedTemporaryFile().name
+tmp_stderr = open( tmp_err, 'wb' )
+# Java writes a lot of stuff to stderr, so we'll specify a file for handling actual errors.
+error_log = tempfile.NamedTemporaryFile( delete=False ).name
+
+# Build the command line.
+cmd = imagej2_base_utils.get_base_command_imagej2( None, jython_script=args.jython_script )
+if cmd is None:
+    imagej2_base_utils.stop_err( "ImageJ not found!" )
+cmd += ' %s' % error_log
+cmd += ' %s' % tmp_input_path
+cmd += ' %s' % args.scale_when_converting
+cmd += ' %s' % args.weighted_rgb_conversions
+cmd += ' %d' % args.noise_tolerance
+cmd += ' %s' % args.output_type
+cmd += ' %s' % args.exclude_edge_maxima
+cmd += ' %s' % args.light_background
+cmd += ' %s' % tmp_output_path
+cmd += ' %s' % args.output_datatype
+
+# Run the command.
+proc = subprocess.Popen( args=cmd, stderr=tmp_stderr, stdout=tmp_stdout, shell=True )
+rc = proc.wait()
+
+# Handle execution errors.
+if rc != 0:
+    error_message = imagej2_base_utils.get_stderr_exception( tmp_err, tmp_stderr, tmp_out, tmp_stdout )
+    imagej2_base_utils.stop_err( error_message )
+
+# Handle processing errors.
+if os.path.getsize( error_log ) > 0:
+    error_message = open( error_log, 'r' ).read()
+    imagej2_base_utils.stop_err( error_message )
+
+# Save the output image.
+shutil.move( tmp_output_path, args.output )
+imagej2_base_utils.cleanup_before_exit( tmp_dir )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imagej2_find_maxima.xml	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,125 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<tool id="imagej2_find_maxima" name="Find maxima" version="@WRAPPER_VERSION@.0">
+    <description></description>
+    <macros>
+        <import>imagej2_macros.xml</import>
+    </macros>
+    <expand macro="fiji_requirements" />
+    <command>
+<![CDATA[
+    python $__tool_directory__/imagej2_find_maxima.py
+    --input "$input"
+    --input_datatype $input.ext
+    --scale_when_converting $scale_when_converting
+    --weighted_rgb_conversions $weighted_rgb_conversions
+    --noise_tolerance $noise_tolerance
+    --output_type $output_type
+    --exclude_edge_maxima $exclude_edge_maxima
+    --light_background $light_background
+    --jython_script $__tool_directory__/jython_script.py
+    #if str( $output_type ) == "List" or str( $output_type ) == "Count":
+        --output "$results"
+    #else:
+        --output "$output"
+    #end if
+    --output_datatype $output.ext
+]]>
+    </command>
+    <inputs>
+        <param format="bmp,eps,gif,jpg,pcx,pgm,png,psd,tiff" name="input" type="data" label="Select image"/>
+        <param name="noise_tolerance" type="integer" value="10" min="0" label="Noise tolerance" help="Maxima are ignored if they do not stand out from the surroundings by more than this value."/>
+        <param name="output_type" type="select" label="Output type">
+            <option value="Single_Points" selected="True">Single Points</option>
+            <option value="Maxima_Within_Tolerance">Maxima Within Tolerance</option>
+            <option value="Segmented_Particles">Segmented Particles</option>
+            <option value="List">List</option>
+            <option value="Count">Count</option>
+        </param>
+        <param name="exclude_edge_maxima" type="select" label="Exclude edge maxima?">
+            <option value="no" selected="True">No</option>
+            <option value="yes">Yes</option>
+        </param>
+        <param name="light_background" type="select" label="Light background?">
+            <option value="no" selected="True">No</option>
+            <option value="yes">Yes</option>
+        </param>
+        <param name="scale_when_converting" type="select" label="Scale when converting?" help="Applies only to 24-bit (RGB) images, ignored for other types.">
+            <option value="yes" selected="True">Yes</option>
+            <option value="no">No</option>
+        </param>
+        <param name="weighted_rgb_conversions" type="select" label="Weighted RGB conversions?" help="Applies only to 24-bit (RGB) images, ignored for other types.">
+            <option value="no" selected="True">No</option>
+            <option value="yes">Yes</option>
+        </param>
+    </inputs>
+    <outputs>
+        <data name="output" format_source="input" label="${tool.name} on ${on_string}: ${output_type}">
+            <filter>output_type != "List" and output_type != "Count"</filter>
+        </data>
+        <data name="results" format="tabular" label="${tool.name} on ${on_string}: ${output_type}">
+            <filter>output_type == "List" or output_type == "Count"</filter>
+        </data>
+    </outputs>
+    <tests>
+        <test>
+            <param name="input" value="blobs.gif" />
+            <output name="output" file="blobs_single_points.gif" compare="sim_size" />
+        </test>
+        <test>
+            <param name="input" value="blobs.gif" />
+            <param name="noise_tolerance" value="13" />
+            <param name="output_type" value="Maxima_Within_Tolerance" />
+            <output name="output" file="blobs_tolerance.gif" compare="sim_size" />
+        </test>
+        <test>
+            <param name="input" value="blobs.gif" />
+            <param name="noise_tolerance" value="16" />
+            <param name="output_type" value="Segmented_Particles" />
+            <param name="exclude_edge_maxima" value="yes" />
+            <output name="output" file="blobs_segmented.gif" compare="sim_size" />
+        </test>
+        <test>
+            <param name="input" value="blobs.gif" />
+            <param name="output_type" value="List" />
+            <output name="output" file="blobs_list.tabular" />
+        </test>
+        <test>
+            <param name="input" value="blobs.gif" />
+            <param name="output_type" value="Count" />
+            <output name="output" file="blobs_count.tabular" />
+        </test>
+    </tests>
+    <help>
+
+**What it does**
+
+Determines the local maxima in an image and creates a binary (mask-like) image of the same size with the
+maxima (or one segmented particle per maximum) marked.  For RGB images, maxima of luminance are selected
+with the luminance defined as the weighted or unweighted average of the colors depending on the **Scale
+when converting** and **Weighted RGB conversions** settings.
+
+- **Scale when converting** (RGB images only) - If "yes", the tool will scale from min--max to 0--255 when converting from 16-bit or 32-bit to 8-bit or to scale from min--max to 0--65535 when converting from 32-bit to 16-bit.
+
+- **Weighted RGB conversions** (RGB images only) - If "yes", the formula gray = 0.299 × red + 0.587 × green + 0.114 × blue is used to convert RGB images to grayscale.  If "no", the formula gray = (red + green + blue) ⁄ 3 is used.
+
+- **Noise Tolerance** - Maxima are ignored if they do not stand out from the surroundings by more than this value (calibrated units for calibrated images). In other words, a threshold is set at the maximum value minus noise tolerance and the contiguous area around the maximum above the threshold is analyzed. For accepting a maximum, this area must not contain any point with a value higher than the maximum. Only one maximum within this area is accepted.
+
+- **Output Type** can be:
+
+ - **Single Points** - Produces an image with one single point per maximum.
+
+ - **Maxima Within Tolerance** - Produces an image with all points within the "Noise Tolerance" for each maximum.
+
+ - **Segmented Particles** - Assumes that each maximum belongs to a particle and segments the image by a watershed algorithm applied to the values of the image.
+
+ - **List** - Produces the list x, y of maxima in the image.
+
+ - **Count** - Produces the number of maxima in the image.
+
+- **Exclude Edge Maxima** - Excludes maxima if the area within the noise tolerance surrounding a maximum touches the edge of the image (edge of the selection does not matter).
+
+- **Light Background** - Select "yes" if the image background is brighter than the objects you want to find.
+
+    </help>
+    <expand macro="fiji_headless_citations" />
+</tool>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/imagej2_macros.xml	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,121 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<macros>
+    <token name="@WRAPPER_VERSION@">1.0</token>
+    <xml name="fiji_requirements">
+        <requirements>
+            <requirement type="package" version="20141125">fiji</requirement>
+        </requirements>
+    </xml>
+    <xml name="python_bioformats_requirements">
+        <requirements>
+            <requirement type="package" version="20141125">fiji</requirement>
+            <requirement type="package" version="1.0.11">javabridge</requirement>
+            <requirement type="package" version="1.9">numpy</requirement>
+            <requirement type="package" version="1.0.4">python_bioformats</requirement>
+        </requirements>
+    </xml>
+    <xml name="stdio">
+        <stdio>
+            <exit_code range="1:"/>
+            <exit_code range=":-1"/>
+            <regex match="Error:"/>
+            <regex match="Exception:"/>
+        </stdio>
+    </xml>
+    <xml name="image_type">
+        <param name="image_type" type="select" label="Image type">
+            <option value="8-bit_white" selected="True">8-bit white</option>
+            <option value="8-bit_black">8-bit black</option>
+            <option value="8-bit_random">8-bit random</option>
+            <option value="8-bit_ramp">8-bit ramp</option>
+            <option value="16-bit_white">16-bit white</option>
+            <option value="16-bit_black">16-bit black</option>
+            <option value="16-bit_random">16-bit random</option>
+            <option value="16-bit_ramp">16-bit ramp</option>
+            <option value="32-bit_white">32-bit white</option>
+            <option value="32-bit_black">32-bit black</option>
+            <option value="32-bit_random">32-bit random</option>
+            <option value="32-bit_ramp">32-bit ramp</option>
+            <option value="RGB_white">RGB white</option>
+            <option value="RGB_black">RGB black</option>
+            <option value="RGB_random">RGB random</option>
+            <option value="RGB_ramp">RGB ramp</option>
+        </param>
+    </xml>
+    <xml name="make_binary_params">
+        <param name="iterations" type="integer" value="1" min="1" max="100" label="Iterations" help="The number of times (1-100) erosion, dilation, opening, and closing are performed."/>
+        <param name="count" type="integer" value="1" min="1" max="8" label="Count" help="The number of adjacent background pixels necessary (1-8) for erosion or dilation."/>
+        <param name="black_background" type="select" label="Black background" help="If Yes, the background is black and the foreground is white (no implies the opposite).">
+            <option value="no" selected="True">No</option>
+            <option value="yes">Yes</option>
+        </param>
+        <param name="pad_edges_when_eroding" type="select" label="Pad edges when eroding" help="If Yes, eroding does not erode from the edges of the image.">
+            <option value="no" selected="True">No</option>
+            <option value="yes">Yes</option>
+        </param>
+    </xml>
+    <xml name="black_background_param">
+        <param name="black_background" type="select" label="Black background" help="If Yes, the background is black and the foreground is white (no implies the opposite).">
+            <option value="no" selected="True">No</option>
+            <option value="yes">Yes</option>
+        </param>
+    </xml>
+    <token name="@make_binary_args@">
+        --iterations $iterations
+        --count $count
+        --black_background $black_background
+        --pad_edges_when_eroding $pad_edges_when_eroding
+    </token>
+    <token name="@requires_binary_input@">
+.. class:: warningmark
+
+This tool works on binary images, so other image types will automatically be converted to binary
+before they are analyzed.  This step is performed using the ImageJ2 **Make Binary** command with
+the following settings: **Iterations:** 1, **Count:** 1, **Pad edges when eroding:** No.  The tool
+allows you to choose the **Black background** setting.  If these settings are not appropriate,
+first manually convert the image to binary using the **Convert to binary (black and white)**
+tool, which allows you to change them.
+    </token>
+    <xml name="image_datatypes">
+        <option value="bmp">bmp</option>
+        <option value="gif">gif</option>
+        <option value="jpg">jpg</option>
+        <option value="png" selected="true">png</option>
+        <option value="tiff">tiff</option>
+    </xml>
+    <xml name="bunwarpj_citations">
+        <citations>
+            <citation type="bibtex">
+                @InProceedings(Arganda-Carreras2006,
+                    author =     "Ignacio Arganda-Carreras and
+                                        Carlos Oscar S{\'a}nchez Sorzano and
+                                        Roberto Marabini and
+                                        Jos{\'e} Mar\'{\i}a Carazo and
+                                        Carlos Ortiz-de-Solorzano and
+                                        Jan Kybic",
+                    title =          "Consistent and Elastic Registration of Histological Sections Using Vector-Spline Regularization",    
+                    publisher =  "Springer Berlin / Heidelberg",    
+                    booktitle =   "Computer Vision Approaches to Medical Image Analysis",
+                    series =       "Lecture Notes in Computer Science",
+                    year =          "2006",
+                    volume =      "4241",
+                    pages =       "85-95",
+                    month =       "May",
+                    city =            "Graz, Austria")
+            </citation>
+            <citation type="doi">10.1038/nmeth.2019</citation>
+        </citations>
+    </xml>
+    <xml name="fiji_headless_citations">
+        <citations>
+            <citation type="doi">10.1038/nmeth.2102</citation>
+        </citations>
+    </xml>
+    <xml name="bioformats_fiji_javabridge_citations">
+        <citations>
+            <citation type="doi">10.1038/nmeth.2102</citation>
+            <citation type="doi">10.1038/nmeth.2019</citation>
+            <citation type="doi">10.1083/jcb.201004104</citation>
+        </citations>
+    </xml>
+</macros>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jython_script.py	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,94 @@
+import sys
+import jython_utils
+from ij import ImagePlus, IJ
+from ij.plugin.filter import Analyzer, MaximumFinder
+from ij.process import ImageProcessor
+from jarray import array
+
+# Fiji Jython interpreter implements Python 2.5 which does not
+# provide support for argparse.
+error_log = sys.argv[ -10 ]
+input = sys.argv[ -9 ]
+scale_when_converting = jython_utils.asbool( sys.argv[ -8 ] )
+weighted_rgb_conversions = jython_utils.asbool( sys.argv[ -7 ] )
+noise_tolerance = int( sys.argv[ -6 ] )
+output_type = sys.argv[ -5 ]
+exclude_edge_maxima = jython_utils.asbool( sys.argv[ -4 ] )
+light_background = jython_utils.asbool( sys.argv[ -3 ] )
+tmp_output_path = sys.argv[ -2 ]
+output_datatype = sys.argv[ -1 ]
+
+# Open the input image file.
+input_image_plus = IJ.openImage( input )
+
+# Create a copy of the image.
+input_image_plus_copy = input_image_plus.duplicate()
+image_processor_copy = input_image_plus_copy.getProcessor()
+bit_depth = image_processor_copy.getBitDepth()
+analyzer = Analyzer( input_image_plus_copy )
+
+try:
+    # Set the conversion options.
+    options = []
+    # The following 2 options are applicable only to RGB images.
+    if bit_depth == 24:
+        if scale_when_converting:
+            option.append( "scale" )
+        if weighted_rgb_conversions:
+            options.append( "weighted" )
+    # Perform conversion - must happen even if no options are set.
+    IJ.run( input_image_plus_copy, "Conversions...", "%s" % " ".join( options ) )
+    if output_type in [ 'List', 'Count' ]:
+        # W're  generating a tabular file for the output.
+        # Set the Find Maxima options.
+        options = [ 'noise=%d' % noise_tolerance ]
+        if output_type.find( '_' ) > 0:
+            output_type_str = 'output=[%s]' % output_type.replace( '_', ' ' )
+        else:
+            output_type_str = 'output=%s' % output_type
+        options.append( output_type_str )
+        if exclude_edge_maxima:
+            options.append( 'exclude' )
+        if light_background:
+            options.append( 'light' )
+        # Run the command.
+        IJ.run( input_image_plus_copy, "Find Maxima...", "%s" % " ".join( options ) )
+        results_table = analyzer.getResultsTable()
+        results_table.saveAs( tmp_output_path )
+    else:
+        # Find the maxima of an image (does not find minima).
+        # LIMITATIONS: With output_type=Segmented_Particles
+        # (watershed segmentation), some segmentation lines
+        # may be improperly placed if local maxima are suppressed
+        # by the tolerance.
+        mf = MaximumFinder()
+        if output_type == 'Single_Points':
+            output_type_param = mf.SINGLE_POINTS
+        elif output_type == 'Maxima_Within_Tolerance':
+            output_type_param = mf.IN_TOLERANCE
+        elif output_type == 'Segmented_Particles':
+            output_type_param = mf.SEGMENTED
+        elif output_type == 'List':
+            output_type_param = mf.LIST
+        elif output_type == 'Count':
+            output_type_param = mf.COUNT
+        # Get a new byteProcessor with a normal (uninverted) LUT where
+        # the marked points are set to 255 (Background 0). Pixels outside
+        # of the roi of the input image_processor_copy are not set. No
+        # output image is created for output types POINT_SELECTION, LIST
+        # and COUNT.  In these cases findMaxima returns null.
+        byte_processor = mf.findMaxima( image_processor_copy,
+                                        noise_tolerance,
+                                        ImageProcessor.NO_THRESHOLD,
+                                        output_type_param,
+                                        exclude_edge_maxima,
+                                        False )
+        # Invert the image or ROI.
+        byte_processor.invert()
+        if output_type == 'Segmented_Particles' and not light_background:
+            # Invert the values in this image's LUT (indexed color model).
+            byte_processor.invertLut()
+        image_plus = ImagePlus( "output", byte_processor )
+        IJ.saveAs( image_plus, output_datatype, tmp_output_path )
+except Exception, e:
+    jython_utils.handle_error( error_log, str( e ) )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jython_utils.py	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,48 @@
+import imagej2_base_utils
+from ij import IJ
+
+IMAGE_PLUS_IMAGE_TYPE_FIELD_VALUES = { '0':'GRAY8', '1':'GRAY16', '2':'GRAY32',
+                                       '3':'COLOR_256', '4':'COLOR_RGB' }
+
+def asbool( val ):
+    return str( val ).lower() in [ 'yes', 'true' ]
+
+def convert_before_saving_as_tiff( image_plus ):
+    # The bUnwarpJ plug-in produces TIFF image stacks consisting of 3
+    # slices which can be viewed in ImageJ.  The 3 slices are: 1) the
+    # registered image, 2) the target image and 3) the black/white warp
+    # image.  When running bUnwarpJ from the command line (as these
+    # Galaxy wrappers do) the initial call to IJ.openImage() (to open the
+    # registered source and target images produced by bUnwarpJ) in the
+    # tool's jython_script.py returns an ImagePlus object with a single
+    # slice which is the "generally undesired" slice 3 discussed above.
+    # However, a call to IJ.saveAs() will convert the single-slice TIFF
+    # into a 3-slice TIFF image stack (as described above) if the selected
+    # format for saving is TIFF.  Galaxy supports only single-layered
+    # images, so to work around this behavior, we have to convert the
+    # image to something other than TIFF so that slices are eliminated.
+    # We can then convert back to TIFF for saving.  There might be a way
+    # to do this without converting twice, but I spent a lot of time looking
+    # and I have yet to discover it.
+    tmp_dir = imagej2_base_utils.get_temp_dir()
+    tmp_out_png_path = imagej2_base_utils.get_temporary_image_path( tmp_dir, 'png' )
+    IJ.saveAs( image_plus, 'png', tmp_out_png_path )
+    return IJ.openImage( tmp_out_png_path )
+
+def get_binary_options( black_background, iterations=1, count=1, pad_edges_when_eroding='no' ):
+    options = [ 'edm=Overwrite', 'iterations=%d' % iterations, 'count=%d' % count ]
+    if asbool( pad_edges_when_eroding ):
+        options.append( 'pad' )
+    if asbool( black_background ):
+        options.append( "black" )
+    return " ".join( options )
+
+def get_display_image_type( image_type ):
+    return IMAGE_PLUS_IMAGE_TYPE_FIELD_VALUES.get( str( image_type ), None )
+
+def handle_error( error_log, msg ):
+    # Java writes a lot of stuff to stderr, so the received error_log 
+    # will log actual errors.
+    elh = open( error_log, 'wb' )
+    elh.write( msg )
+    elh.close()
Binary file test-data/blobs.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/blobs_count.tabular	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,2 @@
+ 	Count
+1	112
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/blobs_list.tabular	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,113 @@
+ 	X	Y
+1	95	8
+2	107	24
+3	10	25
+4	34	16
+5	2	20
+6	118	11
+7	19	10
+8	132	0
+9	124	143
+10	130	139
+11	142	126
+12	140	108
+13	125	99
+14	133	80
+15	30	65
+16	46	52
+17	42	41
+18	116	34
+19	24	36
+20	136	33
+21	50	29
+22	86	29
+23	125	23
+24	143	23
+25	71	10
+26	39	8
+27	105	5
+28	5	3
+29	23	0
+30	2	0
+31	114	141
+32	31	140
+33	112	136
+34	20	133
+35	125	122
+36	28	116
+37	110	109
+38	54	105
+39	15	101
+40	142	95
+41	96	93
+42	4	88
+43	112	91
+44	86	91
+45	58	90
+46	42	90
+47	76	77
+48	102	84
+49	44	81
+50	29	75
+51	41	73
+52	57	73
+53	0	72
+54	118	66
+55	44	68
+56	16	60
+57	67	64
+58	125	63
+59	85	63
+60	108	62
+61	88	49
+62	122	47
+63	97	48
+64	64	43
+65	143	47
+66	28	44
+67	85	46
+68	1	44
+69	14	42
+70	127	40
+71	63	36
+72	93	28
+73	60	28
+74	23	26
+75	73	23
+76	62	24
+77	142	18
+78	49	15
+79	77	3
+80	101	1
+81	95	1
+82	95	140
+83	83	138
+84	69	139
+85	68	126
+86	6	133
+87	70	135
+88	52	135
+89	90	124
+90	88	116
+91	1	114
+92	51	112
+93	8	113
+94	83	112
+95	62	109
+96	31	105
+97	81	99
+98	33	99
+99	31	92
+100	59	85
+101	51	70
+102	79	57
+103	109	54
+104	112	50
+105	104	48
+106	12	48
+107	94	64
+108	43	24
+109	98	22
+110	67	78
+111	143	7
+112	143	0
Binary file test-data/blobs_segmented.gif has changed
Binary file test-data/blobs_single_points.gif has changed
Binary file test-data/blobs_tolerance.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tool_dependencies.xml	Sun Oct 11 13:29:38 2015 -0400
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<tool_dependency>
+    <package name="fiji" version="20141125">
+        <repository changeset_revision="9dbe89105a25" name="package_fiji_20141125" owner="iuc" toolshed="https://testtoolshed.g2.bx.psu.edu" />
+    </package>
+</tool_dependency>