/////////////////////////////////////////////////////////////////////////////////////////////////// // Facade to ImageJ functionality /////////////////////////////////////////////////////////////////////////////////////////////////// //More information on: // Image: https://imagej.nih.gov/ij/docs/guide/146-28.htmltoc-Section-28 // Process: https://imagej.nih.gov/ij/docs/guide/146-29.html#toc-Section-29 // Analyze: https://imagej.nih.gov/ij/docs/guide/146-30.html#toc-Section-30 Utils = Java.type('ch.psi.pshell.imaging.Utils') Pair = Java.type('org.apache.commons.math3.util.Pair') IJ = Java.type('ij.IJ') ImageJ = Java.type('ij.ImageJ') WindowManager = Java.type('ij.WindowManager') ImagePlus = Java.type('ij.ImagePlus') Prefs = Java.type('ij.Prefs') FileSaver = Java.type('ij.io.FileSaver') ImageProcessor = Java.type('ij.process.ImageProcessor') ByteProcessor = Java.type('ij.process.ByteProcessor') ShortProcessor = Java.type('ij.process.ShortProcessor') ColorProcessor = Java.type('ij.process.ColorProcessor') FloatProcessor = Java.type('ij.process.FloatProcessor') ImageConverter = Java.type('ij.process.ImageConverter') AutoThresholder = Java.type('ij.process.AutoThresholder') LUT = Java.type('ij.process.LUT') Measurements = Java.type('ij.measure.Measurements') ResultsTable = Java.type('ij.measure.ResultsTable') Analyzer = Java.type('ij.plugin.filter.Analyzer') GaussianBlur = Java.type('ij.plugin.filter.GaussianBlur') Filters = Java.type('ij.plugin.filter.Filters') FFTFilter = Java.type('ij.plugin.filter.FFTFilter') BackgroundSubtracter = Java.type('ij.plugin.filter.BackgroundSubtracter') EDM = Java.type('ij.plugin.filter.EDM') Shadows = Java.type('ij.plugin.filter.Shadows') UnsharpMask = Java.type('ij.plugin.filter.UnsharpMask') MaximumFinder = Java.type('ij.plugin.filter.MaximumFinder') EDM = Java.type('ij.plugin.filter.EDM') Shadows = Java.type('ij.plugin.filter.Shadows') UnsharpMask = Java.type('ij.plugin.filter.UnsharpMask') RankFilters = Java.type('ij.plugin.filter.RankFilters') Convolver = Java.type('ij.plugin.filter.Convolver') ParticleAnalyzer = Java.type('ij.plugin.filter.ParticleAnalyzer') ContrastEnhancer = Java.type('ij.plugin.ContrastEnhancer') Thresholder = Java.type('ij.plugin.Thresholder') ImageCalculator = Java.type('ij.plugin.ImageCalculator') FFT = Java.type('ij.plugin.FFT') Concatenator = Java.type('ij.plugin.Concatenator') //ImageJ customizations FFTMath = Java.type('ch.psi.pshell.imaging.ij.FFTMath') FFTFilter = Java.type('ch.psi.pshell.imaging.ij.FFTFilter') Binary = Java.type('ch.psi.pshell.imaging.ij.Binary') Slicer = Java.type('ch.psi.pshell.imaging.ij.Slicer') //This eliminates the error messages due to the bug on ij.gui.ImageWindow row 555 (ij is null) try{ _image_j } catch(ex) { _image_j = new ImageJ(null, ImageJ.NO_SHOW) } /////////////////////////////////////////////////////////////////////////////////////////////////// //Image creation, copying & saving /////////////////////////////////////////////////////////////////////////////////////////////////// function load_image(image, title){ /* image: file name or BufferedImage */ if (!is_defined(title)) title ="img" if (typeof image == 'string') try{ file = get_context().setup.expandPath(image) } catch(ex) { } try{ image = Utils.newImage(file) } catch(ex) { //try loading from assembly image = get_context().setup.getAssemblyImage(image) } return new ImagePlus(title, image) } function load_array(array, width, height, title){ /* array: 1d array if width and height defined , or else 2d array to be flattened. */ if (!is_defined(width)) width = null if (!is_defined(height)) height = null if (!is_defined(title)) title ="img" //2D if ((width==null) && (height==null)){ if (array.typecode == '[B') proc = new ByteProcessor(array[0].length, array.length, Convert.flatten(array)) else if (array.typecode == '[S') proc = new ShortProcessor(array[0].length, array.length, Convert.flatten(array), null) else if (array.typecode == '[I') proc = new FloatProcessor(array[0].length, array.length, Convert.flatten(array)) else if (array.typecode == '[F') proc = new FloatProcessor(array[0].length, array.length, Convert.flatten(array)) else if (array.typecode == '[D') proc = new FloatProcessor(array[0].length, array.length, Convert.flatten(array)) else throw "Invalid array type" //1D }else{ if (array.length > width*height) array = array.slice[0, width*height] if (array.typecode == 'b') proc = new yteProcessor(width, height, array) else if (array.typecode == 'h') proc = new ShortProcessor(width, height, array, null) else if (array.typecode == 'i') proc = new FloatProcessor(width, height, array) else if (array.typecode == 'f') proc = new FloatProcessor(width, height, array) else if (array.typecode == 'd') proc = new FloatProcessor(width, height, array) else throw "Invalid array type" } return new ImagePlus(title, proc) } function save_image(ip, path, format){ /* Saves image or stack If parameters omitted, saves image again in same location, with same format. */ if (!is_defined(path)) path = null if (!is_defined(format)) format = null var fs = new FileSaver(ip) if (path == null)fs.save() else{ try{ path = get_context().setup.expandPath(path) } catch(ex) { } if (format == "bmp") fs.saveAsBmp(path) else if (format == "fits") fs.saveAsFits(path) else if (format == "gif") fs.saveAsGif(path) else if (format == "jpeg") fs.saveAsJpeg(path) else if (format == "lut") fs.saveAsLut(path) else if (format == "pgm") fs.saveAsPgm(path) else if (format == "png") fs.saveAsPng(path) else if (format == "raw" && (ip.getImageStackSize()>1)) fs.saveAsRawStack(path) else if (format == "raw") fs.saveAsRaw(path) else if (format == "txt") fs.saveAsText(path) else if (format == "tiff" && (ip.getImageStackSize()>1)) fs.saveAsTiffStack(path) else if (format == "tiff") fs.saveAsTiff(path) else if (format == "zip") fs.saveAsZip(path) } } function new_image(width, height, image_type, title, fill_color){ /* type = "byte", "short", "color" or "float" */ if (!is_defined(image_type)) image_type = "byte" if (!is_defined(title)) title = "img" if (!is_defined(fill_color)) fill_color = null if (image_type == "byte") p= new ByteProcessor(width, height) else if (image_type == "short") p= new ShortProcessor(width, height) else if (image_type == "color") p= new ColorProcessor(width, height) else if (image_type == "float") p= new FloatProcessor(width, height) else throw ("Invalid image type " + str(image_type)) ret = new ImagePlus(title, p) if (fill_color != null){ p.setColor(fill_color) p.resetRoi() p.fill() } return ret } function sub_image(ip, x, y, width, height){ /* Returns new ImagePlus */ ip.setRoi(x, y, width, height) p=ip.getProcessor().crop() return new ImagePlus(ip.getTitle() + " subimage", p) } function copy_image(ip){ return ip.duplicate() } function copy_image_to(ip_source, ip_dest, x, y){ ip_source.deleteRoi() ip_source.copy() ip_dest.setRoi(x, y, ip_source.getWidth(), ip_source.getHeight()) ip_dest.paste() ip_dest.changes = false ip_dest.deleteRoi() } function pad_image(ip, left, right, top, bottom, fill_color){ if (!is_defined(left)) left = 0 if (!is_defined(right)) right = 0 if (!is_defined(top)) top = 0 if (!is_defined(bottom)) bottom = 0 if (!is_defined(fill_color)) fill_color = null p=ip.getProcessor() width = p.getWidth() + left + right height = p.getHeight() + top + bottom image_type = get_image_type(ip) ret = new_image(width, height, image_type, ip.getTitle() + " padded", fill_color) ip.deleteRoi() ip.copy() ret.setRoi(left, top, p.getWidth(), p.getHeight()) ret.paste() ret.changes = false ret.deleteRoi() return ret } function get_image_type(ip){ /* Returns: "byte", "short", "color" or "float" */ p=ip.getProcessor() if (p instanceof ShortProcessor) return "short" else if (p instanceof ColorProcessor) return "color" else if (p instanceof FloatProcessor) return "float" return "byte" } /////////////////////////////////////////////////////////////////////////////////////////////////// //Image type conversion /////////////////////////////////////////////////////////////////////////////////////////////////// function grayscale(ip, in_place){ if (!is_defined(in_place)) in_place = true ip = (in_place==true) ? ip : ip.duplicate() ic = new ImageConverter(ip) ic.convertToGray8() return ip } function get_channel(ip, channel){ /* Return a channel from a color image as a new ImagePlus. channel: "red", "green","blue", "alpha", "brightness", */ proc = ip.getProcessor() if (channel == "red") ret = proc.getChannel(1, null) else if (channel == "green") ret = proc.getChannel(2, null) else if (channel == "blue") ret = proc.getChannel(3, null) else if (channel == "alpha") ret = proc.getChannel(4, null) else if (channel == "brightness") ret = proc.getBrightness() else throw ("Invalid channel " + channel) return new ImagePlus(ip.getTitle() + " channel: " + channel, ret) } /////////////////////////////////////////////////////////////////////////////////////////////////// //Thresholder /////////////////////////////////////////////////////////////////////////////////////////////////// function threshold(ip, min_threshold, max_threshold, in_place){ if (!is_defined(in_place)) in_place = true ip = in_place ? ip : ip.duplicate() ip.getProcessor().setThreshold(min_threshold, max_threshold, ImageProcessor.NO_LUT_UPDATE) WindowManager.setTempCurrentImage(ip) new Thresholder().run("mask") return ip } function auto_threshold(ip, dark_background, method, in_place){ if (!is_defined(dark_background)) dark_background = false if (!is_defined(method)) method = AutoThresholder.getMethods()[0] if (!is_defined(in_place)) in_place = true ip = in_place ? ip : ip.duplicate() ip.getProcessor().setAutoThreshold(method, dark_background , ImageProcessor.NO_LUT_UPDATE) WindowManager.setTempCurrentImage(ip) thresholder=new Thresholder().run("mask") return ip } /////////////////////////////////////////////////////////////////////////////////////////////////// //Binary functions /////////////////////////////////////////////////////////////////////////////////////////////////// function binary_op(ip, op, dark_background, iterations, count, in_place){ /* op = "erode","dilate", "open","close", "outline", "fill holes", "skeletonize" */ if (!is_defined(dark_background)) dark_background = false if (!is_defined(iterations)) iterations = 1 if (!is_defined(count)) count = 1 if (!is_defined(in_place)) in_place = true ip = in_place ? ip : ip.duplicate() binary = new Binary() Binary.count = count Binary.iterations = iterations Prefs.blackBackground=dark_background binary.setup(op, ip) binary.run(ip.getProcessor()) return ip } function binary_erode(ip, dark_background, iterations, count, in_place){ return binary_op(ip, "erode", dark_background, iterations, count, in_place) } function binary_dilate(ip, dark_background, iterations, count, in_place){ return binary_op(ip, "dilate", dark_background, iterations, count, in_place) } function binary_open(ip, dark_background, iterations, count, in_place){ return binary_op(ip, "open", dark_background, iterations, count, in_place) } function binary_close(ip, dark_background, iterations, count, in_place){ return binary_op(ip, "close", dark_background, iterations, count, in_place) } function binary_outline(ip, dark_background, in_place){ return binary_op(ip, "outline", dark_background, 1, 1, in_place) } function binary_fill_holes(ip, dark_background, in_place){ return binary_op(ip, "fill holes", dark_background, 1, 1, in_place) } function binary_skeletonize(ip, dark_background, in_place){ return binary_op(ip, "skeletonize", dark_background, 1, 1, in_place) } function analyse_particles(ip, min_size, max_size, fill_holes, exclude_edges, extra_measurements,print_table, output_image, minCirc, maxCirc){ /* Returns: tuple (ResultsTable results_table, ImagePlus output_image) output_image = "outlines", "overlay_outlines", "masks", "overlay_masks", "roi_masks" or null extra_measurements = mask with Measurements.CENTROID, PERIMETER, RECT, MIN_MAX, ELLIPSE, CIRCULARITY, AREA_FRACTION, INTEGRATED_DENSITY, INVERT_Y, FERET, KURTOSIS, MEDIAN, MODE, SKEWNESS, STD_DEV Measurements is a mask of flags: https://imagej.nih.gov/ij/developer/api/ij/measure/Measurements.html. Returned ResultsTable hold public fields: https://imagej.nih.gov/ij/developer/api/ij/measure/ResultsTable.html */ if (!is_defined(fill_holes)) fill_holes = true if (!is_defined(exclude_edges)) exclude_edges = true if (!is_defined(extra_measurements)) extra_measurements = 0 if (!is_defined(print_table)) print_table = false if (!is_defined(output_image)) output_image = "outlines" if (!is_defined(minCirc)) minCirc = 0.0 if (!is_defined(maxCirc)) maxCirc = 1.0 rt = new ResultsTable() show_summary = false var options = ParticleAnalyzer.SHOW_RESULTS | ParticleAnalyzer.CLEAR_WORKSHEET /* ParticleAnalyzer.SHOW_ROI_MASKS | \ //ParticleAnalyzer.RECORD_STARTS | \ //ParticleAnalyzer.ADD_TO_MANAGER | \ //ParticleAnalyzer.FOUR_CONNECTED | \ //ParticleAnalyzer.IN_SITU_SHOW | \ //ParticleAnalyzer.SHOW_NONE | \ */ if (show_summary) options = options | ParticleAnalyzer.DISPLAY_SUMMARY if (output_image == "outlines") options = options | ParticleAnalyzer.SHOW_OUTLINES else if (output_image == "overlay_outlines") options = options | ParticleAnalyzer.SHOW_OVERLAY_OUTLINES else if (output_image == "masks") options = options | ParticleAnalyzer.SHOW_MASKS else if (output_image == "overlay_masks") options = options | ParticleAnalyzer.SHOW_OVERLAY_MASKS else if (output_image == "roi_masks") options = options | ParticleAnalyzer.SHOW_ROI_MASKS //ParticleAnalyzer.SHOW_ROI_MASKS if (exclude_edges) options = options | ParticleAnalyzer.EXCLUDE_EDGE_PARTICLES if (fill_holes) options = options | ParticleAnalyzer.INCLUDE_HOLES measurements = Measurements.AREA | Measurements.MEAN | Measurements.CENTER_OF_MASS | Measurements.RECT pa = new ParticleAnalyzer(options, measurements, rt, min_size, max_size, minCirc, maxCirc) pa.setHideOutputImage(true) ParticleAnalyzer.setResultsTable(rt) if (pa.analyze(ip)){ if (print_table){ print (rt.getColumnHeadings()) for (var row= 0; row