# HG changeset patch # User greg # Date 1563971437 14400 # Node ID 0729a4b20e67120c695962e5891de2cad91b642d Uploaded diff -r 000000000000 -r 0729a4b20e67 .shed.yml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.shed.yml Wed Jul 24 08:30:37 2019 -0400 @@ -0,0 +1,19 @@ +name: imagej2_adjust_threshold_binary +owner: greg +homepage_url: http://fiji.sc +long_description: | + ImageJ2 is a new version of ImageJ for the next generation of multidimensional + image data, with a focus on scientific imaging. Its central goal is to broaden + the paradigm of ImageJ beyond the limitations of ImageJ 1.x, to support the next + generation of multidimensional scientific imaging. + + ImageJ2 is also a collection of reusable software libraries built on the SciJava + software stack, using a powerful plugin framework to facilitate rapid development + and painless user customization. + + The Fiji distribution of ImageJ has shipped with beta versions of ImageJ2 for + quite some time. +remote_repository_url: https://github.com/bgruening/galaxytools/tree/master/tools/image_processing/imagej2 +type: unrestricted +categories: + - Imaging diff -r 000000000000 -r 0729a4b20e67 imagej2_adjust_threshold_binary.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imagej2_adjust_threshold_binary.py Wed Jul 24 08:30:37 2019 -0400 @@ -0,0 +1,63 @@ +#!/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( '--threshold_min', dest='threshold_min', type=float, help='Minimum threshold value' ) +parser.add_argument( '--threshold_max', dest='threshold_max', type=float, help='Maximum threshold value' ) +parser.add_argument( '--method', dest='method', help='Threshold method' ) +parser.add_argument( '--display', dest='display', help='Display mode' ) +parser.add_argument( '--black_background', dest='black_background', help='Black background' ) +parser.add_argument( '--stack_histogram', dest='stack_histogram', help='Stack histogram' ) +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 += ' %.3f' % args.threshold_min +cmd += ' %.3f' % args.threshold_max +cmd += ' %s' % args.method +cmd += ' %s' % args.display +cmd += ' %s' % args.black_background +cmd += ' %s' % args.stack_histogram +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 ) diff -r 000000000000 -r 0729a4b20e67 imagej2_adjust_threshold_binary.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imagej2_adjust_threshold_binary.xml Wed Jul 24 08:30:37 2019 -0400 @@ -0,0 +1,115 @@ + + + of binary image + + imagej2_macros.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +@requires_binary_input@ + +**What it does** + + + + + + diff -r 000000000000 -r 0729a4b20e67 imagej2_adjust_threshold_binary_jython_script.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imagej2_adjust_threshold_binary_jython_script.py Wed Jul 24 08:30:37 2019 -0400 @@ -0,0 +1,49 @@ +import jython_utils +import sys +from ij import IJ + +# Fiji Jython interpreter implements Python 2.5 which does not +# provide support for argparse. +error_log = sys.argv[ -10 ] +input = sys.argv[ -9 ] +threshold_min = float( sys.argv[ -8 ] ) +threshold_max = float( sys.argv[ -7 ] ) +method = sys.argv[ -6 ] +display = sys.argv[ -5 ] +black_background = jython_utils.asbool( sys.argv[ -4 ] ) +# TODO: this is not being used. +stack_histogram = 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() + +try: + # Convert image to binary if necessary. + if not image_processor_copy.isBinary(): + # Convert the image to binary grayscale. + IJ.run( input_image_plus_copy, "Make Binary","iterations=1 count=1 edm=Overwrite do=Nothing" ) + # Set the options. + if black_background: + method_str = "%s dark" % method + else: + method_str = method + IJ.setAutoThreshold( input_image_plus_copy, method_str ) + if display == "red": + display_mode = "Red" + elif display == "bw": + display_mode = "Black & White" + elif display == "over_under": + display_mode = "Over/Under" + IJ.setThreshold( input_image_plus_copy, threshold_min, threshold_max, display_mode ) + # Run the command. + IJ.run( input_image_plus_copy, "threshold", "" ) + # Save the ImagePlus object as a new image. + IJ.saveAs( input_image_plus_copy, output_datatype, tmp_output_path ) +except Exception, e: + jython_utils.handle_error( error_log, str( e ) ) diff -r 000000000000 -r 0729a4b20e67 imagej2_base_utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imagej2_base_utils.py Wed Jul 24 08:30:37 2019 -0400 @@ -0,0 +1,169 @@ +import os +import shutil +import sys +import tempfile + +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 jvm_memory in [None, 'None']: + jvm_memory_str = '' + else: + jvm_memory_str = '-Xmx%s' % jvm_memory + # The following bunwarpj_base_cmd string will look something like this: + # "java %s -cp $JAR_DIR/ij-1.49k.jar:$PLUGINS_DIR/bUnwarpJ_-2.6.1.jar \ + # bunwarpj.bUnwarpJ_" % (jvm_memory_str) + # See the bunwarpj.sh script for the fiji 20151222 + # bioconda recipe in github. + bunwarpj_base_cmd = "bunwarpj %s" % jvm_memory_str + return bunwarpj_base_cmd + + +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, but our bioconda recipe allows us to do this. + """ + return 'ImageJ' + + +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) diff -r 000000000000 -r 0729a4b20e67 imagej2_base_utils.pyc Binary file imagej2_base_utils.pyc has changed diff -r 000000000000 -r 0729a4b20e67 imagej2_macros.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imagej2_macros.xml Wed Jul 24 08:30:37 2019 -0400 @@ -0,0 +1,106 @@ + + + 20170530 + + + fiji + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + --iterations $iterations + --count $count + --black_background $black_background + --pad_edges_when_eroding $pad_edges_when_eroding + + +.. 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. + + + + + + + + + + + + @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") + + 10.1038/nmeth.2019 + + + + + 10.1038/nmeth.2102 + + + diff -r 000000000000 -r 0729a4b20e67 jython_utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jython_utils.py Wed Jul 24 08:30:37 2019 -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() diff -r 000000000000 -r 0729a4b20e67 test-data/blobs.gif Binary file test-data/blobs.gif has changed diff -r 000000000000 -r 0729a4b20e67 test-data/blobs_threshold_default.gif Binary file test-data/blobs_threshold_default.gif has changed diff -r 000000000000 -r 0729a4b20e67 test-data/blobs_threshold_huang_dark.gif Binary file test-data/blobs_threshold_huang_dark.gif has changed diff -r 000000000000 -r 0729a4b20e67 test-data/blobs_threshold_ijiso.gif Binary file test-data/blobs_threshold_ijiso.gif has changed