diff --git a/2c_fibertyping.py b/2c_fibertyping.py index cc43e624329573dd4b31fc13c2b918dbea98718a..d57919d9450c73bd818db8cbb742b7c074dac5bc 100644 --- a/2c_fibertyping.py +++ b/2c_fibertyping.py @@ -1,24 +1,6 @@ -# this is a python rewrite of the original ijm published at -# https://github.com/Hyojung-Choo/Myosoft/blob/Myosoft-hub/Scripts/central%20nuclei%20counter.ijm - -# IJ imports -# TODO: are the imports RoiManager and ResultsTable needed when using the services? -from ij import IJ, WindowManager as wm -from ij.plugin import Duplicator, RoiEnlarger, RoiScaler -from trainableSegmentation import WekaSegmentation -from de.biovoxxel.toolbox import Extended_Particle_Analyzer - -# Bio-formats imports -from loci.plugins import BF -from loci.plugins.in import ImporterOptions - -# python imports -import time -import os - -#@ String (visibility=MESSAGE, value="<html><b> Welcome to Myosoft! </b></html>") msg1 -#@ File (label="Select fiber-ROIs zip-file", style="file") roi_zip -#@ File (label="Select image file", description="select your image") path_to_image +# @ String (visibility=MESSAGE, value="<html><b> Welcome to Myosoft! </b></html>") msg1 +# @ File (label="Select fiber-ROIs zip-file", style="file") roi_zip +# @ File (label="Select image file", description="select your image") path_to_image #@ File (label="Select directory for output", style="directory") output_dir #@ Boolean (label="close image after processing", description="tick this box when using batch mode", value=False) close_raw #@ String (visibility=MESSAGE, value="<html><b> channel positions in the hyperstack </b></html>") msg5 @@ -28,134 +10,26 @@ import os #@ Integer (label="minimum fiber intensity (0=auto)", description="0 = automatic threshold detection", value=0) min_fiber_intensity_1 #@ Integer (label="minimum fiber intensity (0=auto)", description="0 = automatic threshold detection", value=0) min_fiber_intensity_2 #@ Integer (label="minimum fiber intensity (0=auto)", description="0 = automatic threshold detection", value=0) min_fiber_intensity_3 -#@ ResultsTable rt -#@ RoiManager rm - - -def fix_ij_options(): - """Configure ImageJ (IJ) to a predefined state. - - This function sets various options in ImageJ to ensure a consistent - environment for image processing tasks. It includes settings for appearance, - color management, binary image options, and file output format. - """ - # Disable inverting LUT (Look-Up Table) for images - IJ.run("Appearance...", " menu=0 16-bit=Automatic") - - # Set foreground color to be white and background color to be black, with red for selections - IJ.run("Colors...", "foreground=white background=black selection=red") - - # Enable black background for binary images and pad edges when eroding - IJ.run("Options...", "black pad") - - # Set saving format to .txt files, saving both columns and rows - IJ.run("Input/Output...", "file=.txt save_column save_row") +# @ ResultsTable rt +# @ RoiManager rm - # Set "Black Background" option for binary operations - IJ.run("Options...", "black") - - # Enable scaling when converting images between types - IJ.run("Conversions...", "scale") - - -def fix_ij_dirs(path): - """Replace backslashes with forward slashes in directory paths. - - This function takes a directory path obtained from a dialogue or script - parameter and returns a more robust path with forward slashes as separators. - - Parameters - ---------- - path : str - A directory path - - Returns - ------- - str - A directory path with forward slashes as separators - """ - fixed_path = str(path).replace("\\", "/") - # fixed_path = fixed_path + "/" +# ─── Requirements ───────────────────────────────────────────────────────────── - return fixed_path +# List of update sites needed for the code +# * IMCF +# ─── Imports ────────────────────────────────────────────────────────────────── -def open_image_with_BF(path_to_file): - """Open the first image from a file using Bio-Formats. - This function utilizes Bio-Formats to open an image file and returns - the first image contained within the file. The image is opened in - grayscale mode with autoscaling enabled. - - Parameters - ---------- - path_to_file : str - The file path to the image file to be opened. - - Returns - ------- - ImagePlus - The first image contained in the specified file. - """ - # Create an ImporterOptions object for configuring the import process - options = ImporterOptions() - # Set the color mode to grayscale - options.setColorMode(ImporterOptions.COLOR_MODE_COMPOSITE ) - # Enable autoscaling for the image - options.setAutoscale(True) - # Set the file path for the image to be opened - options.setId(path_to_file) - # Open the image(s) using Bio-Formats and store them in an array - imps = BF.openImagePlus(options) - - # Return the first image in the array - return imps[0] - - -def fix_BF_czi_imagetitle(imp): - """ - Fix the title of an image read using the bio-formats importer. - - The title is modified to remove the ".czi" extension and replace - spaces with underscores. - - Parameters - ---------- - imp : ij.ImagePlus - The image to be processed. +import time - Returns - ------- - str - The modified title of the image. - """ - # Get the short title of the image (without path) - image_title = os.path.basename(imp.getShortTitle()) - # Remove the ".czi" extension - image_title = image_title.replace(".czi", "") - # Replace spaces with underscores - image_title = image_title.replace(" ", "_") - # Remove any double underscores - image_title = image_title.replace("_-_", "") - # Remove any double underscores - image_title = image_title.replace("__", "_") - # Remove any "#" characters - image_title = image_title.replace("#", "Series") - - return image_title - - -def clear_ij_roi_manager(rm): - """ - Clear all ROIs from the RoiManager. +from ij import IJ +from ij import WindowManager as wm +from imcflibs import pathtools +from imcflibs.imagej import bioformats as bf +from imcflibs.imagej import misc, prefs, resultstable, roimanager - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - a reference of the IJ-RoiManager - """ - # Run the "reset" command in the RoiManager to clear all ROIs - rm.runCommand('reset') +# ─── Functions ──────────────────────────────────────────────────────────────── def get_threshold_from_method(imp, channel, method): @@ -190,136 +64,47 @@ def get_threshold_from_method(imp, channel, method): return [lower_thr, upper_thr] -def measure_in_all_rois( imp, channel, rm ): - """Measures in all ROIs on a given channel of imp all parameters that are set in IJ "Set Measurements". - - This function takes an ImagePlus (imp), a channel (integer, starts at 1), and a RoiManager (rm) as - parameters. It then sets the channel of the imp to the desired channel, deselects all ROIs in the - RoiManager, and measures in all ROIs of the RoiManager on the channel of the imp. The parameters - that are measured are the ones that are set in IJ "Set Measurements". +def enhance_contrast(imp): + """Use "Auto" Contrast & Brightness settings in each channel of imp. Parameters ---------- imp : ij.ImagePlus - the imp to measure on - channel : int - the channel to measure in. starts at 1. - rm : ij.plugin.frame.RoiManager - a reference of the IJ-RoiManager - """ - # Set the channel of the imp to the desired channel - imp.setC(channel) - # Deselect all ROIs in the RoiManager - rm.runCommand(imp,"Deselect") - # Measure in all ROIs of the RoiManager on the channel of the imp - rm.runCommand(imp,"Measure") - - -def change_all_roi_color(rm, color): - """ - Change the color of all ROIs in the RoiManager. - - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - A reference to the IJ-RoiManager. - color : str - The desired color, e.g., "green", "red", "yellow", "magenta". - - Returns - ------- - None - """ - # Select all ROIs in the RoiManager - #rm.setSelectedIndexes(range(rm.getCount())) - rm.runCommand("Deselect") - # Change the color of all ROIs to the desired color - rm.runCommand("Set Color", color) - # # Deselect all ROIs again to finalize changes - # rm.runCommand("Deselect") - - -def change_subset_roi_color(rm, selected_rois, color): - """ - Change the color of selected ROIs in the RoiManager. - - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - A reference to the IJ-RoiManager. - selected_rois : list of int - Indices of ROIs in the RoiManager to change. - color : str - The desired color, e.g., "green", "red", "yellow", "magenta", etc. - """ - - # Deselect all currently selected ROIs in the RoiManager - rm.runCommand("Deselect") - # Select the specified ROIs by their indices - rm.setSelectedIndexes(selected_rois) - # Change the color of the selected ROIs to the specified color - rm.runCommand("Set Color", color) - # Deselect all ROIs again to finalize changes - rm.runCommand("Deselect") - - -def show_all_rois_on_image(rm, imp): + The imp on which to change C&B. """ - Display all ROIs on the given image using the ROI Manager. + for channel in range(imp.getDimensions()[2]): + imp.setC(channel + 1) # IJ channels start at 1 + IJ.run(imp, "Enhance Contrast", "saturated=0.35") - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - A reference to the IJ-RoiManager. - imp : ij.ImagePlus - The image on which to display the ROIs. - """ - # Show the image in the ImageJ window - imp.show() - # Use the ROI Manager to show all ROIs on the image - rm.runCommand(imp, "Show All") +def setup_defined_ij(rm, rt): + """Set up a clean and defined Fiji user environment. -def save_all_rois(rm, target): - """Save all ROIs in the RoiManager as a zip file to the given target path. + This function configures the ImageJ environment to a predefined state by + resetting the RoiManager and ResultsTable, and clearing the IJ log. Parameters ---------- rm : ij.plugin.frame.RoiManager A reference to the IJ-RoiManager. - target : str - The file path in which to save the ROIs, e.g., /my-images/resulting_rois.zip + rt : ij.measure.ResultsTable + A reference to the IJ-ResultsTable. """ - # Save all ROIs in the RoiManager to the given target path - rm.runCommand("Save", target) + # Configure IJ options to a predefined state + prefs.set_default_ij_options() + # Reset the RoiManager to remove all existing ROIs + rm.runCommand("reset") -def save_selected_rois(rm, selected_rois, target): - """ - Save selected ROIs in the RoiManager as a zip file to the given target path. + # Reset the ResultsTable to clear all previous results + rt.reset() - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - A reference to the IJ-RoiManager. - selected_rois : list of int - Indices of ROIs in the RoiManager to save. - target : str - The file path in which to save the ROIs, e.g., /my-images/resulting_rois_subset.zip - """ - # Deselect all currently selected ROIs in the RoiManager to ensure a clean start - rm.runCommand("Deselect") - # Select the specified ROIs by their indices - rm.setSelectedIndexes(selected_rois) - # Save the selected ROIs to the given target path as a zip file - rm.runCommand("save selected", target) - # Deselect all ROIs again to finalize changes and maintain a clean state - rm.runCommand("Deselect") + # Clear the IJ log to ensure a fresh output window + IJ.log("\\Clear") -def select_positive_fibers( imp, channel, rm, min_intensity ): - """For all ROIs in the RoiManager, select ROIs based on intensity measurement in given channel of imp. - See https://imagej.nih.gov/ij/developer/api/ij/process/ImageStatistics.html +def measure_mean_of_rois(imp, channel, rm): + """Measure the mean intensity of all ROIs in the given channel of imp. Parameters ---------- @@ -339,149 +124,35 @@ def select_positive_fibers( imp, channel, rm, min_intensity ): """ imp.setC(channel) all_rois = rm.getRoisAsArray() - selected_rois = [] for i, roi in enumerate(all_rois): imp.setRoi(roi) stats = imp.getStatistics() - if stats.mean > min_intensity: - selected_rois.append(i) - - return selected_rois - - -def open_rois_from_zip(rm, path): - """ - Open ROIs from a zip file and add them to the RoiManager. - - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - A reference to the IJ-RoiManager. - path : str - The file path to the ROI zip file. - """ - rm.runCommand("Open", path) - - -def preset_results_column( rt, column, value): - """Pre-set all rows in given column of the IJ-ResultsTable with desired value. - - Parameters - ---------- - rt : ij.measure.ResultsTable - A reference of the IJ-ResultsTable - column : str - The desired column. Will be created if it does not yet exist - value : str or float or int - The value to be set - """ - for i in range( rt.size() ): - rt.setValue(column, i, value) - - rt.show("Results") - - -def add_results(rt, column, row, value): - """Add a value in specified rows of a given column. - - Parameters - ---------- - rt : ij.measure.ResultsTable - A reference to the IJ-ResultsTable. - column : str - The column in which to add the values. - row : list of int - The row numbers in which to add the values. - value : str, float, or int - The value to be set. - """ - # Iterate over each row index in the row list - for i in range(len(row)): - # Set the specified value in the given column and row - rt.setValue(column, row[i], value) - - # Display the updated ResultsTable - rt.show("Results") - -def enhance_contrast(imp): - """Use "Auto" Contrast & Brightness settings in each channel of imp. - - Parameters - ---------- - imp : ij.ImagePlus - The imp on which to change C&B. - """ - for channel in range(imp.getDimensions()[2]): - imp.setC(channel + 1) # IJ channels start at 1 - IJ.run(imp, "Enhance Contrast", "saturated=0.35") - - -def renumber_rois(rm): - """Rename all ROIs in the RoiManager according to their number. - - The RoiManager uses 0-based indexing, so the first ROI is at index 0. - This function renames each ROI with its index (starting from 1). - - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - A reference to the IJ-RoiManager. - """ - number_of_rois = rm.getCount() - for roi in range( number_of_rois ): - rm.rename( roi, str(roi + 1) ) - - -def setup_defined_ij(rm, rt): - """Set up a clean and defined Fiji user environment. - - This function configures the ImageJ environment to a predefined state by - resetting the RoiManager and ResultsTable, and clearing the IJ log. - - Parameters - ---------- - rm : ij.plugin.frame.RoiManager - A reference to the IJ-RoiManager. - rt : ij.measure.ResultsTable - A reference to the IJ-ResultsTable. - """ - # Configure IJ options to a predefined state - fix_ij_options() - # Reset the RoiManager to remove all existing ROIs - rm.runCommand('reset') - - # Reset the ResultsTable to clear all previous results - rt.reset() - - # Clear the IJ log to ensure a fresh output window - IJ.log("\\Clear") execution_start_time = time.time() -setup_defined_ij(rm, rt) + misc.timed_log("Script starting") + setup_defined_ij(rm, rt) + image_file_info = pathtools.parse_path(path_to_image) # open image using Bio-Formats -path_to_image = fix_ij_dirs(path_to_image) -raw = open_image_with_BF(path_to_image) -raw.hide() + raw = bf.import_image(image_file_info["full"]) + raw.hide() -# get image info -raw_image_calibration = raw.getCalibration() -raw_image_title = fix_BF_czi_imagetitle(raw) + # get image info + raw_image_calibration = raw.getCalibration() + raw_image_title = misc.sanitize_image_title(raw) -# take care of paths and directories -input_rois_path = fix_ij_dirs( roi_zip ) -output_dir = fix_ij_dirs(output_dir) + "/2c_fibertyping" + roi_file_info = pathtools.parse_path(roi_zip) -if not os.path.exists( str(output_dir) ): - os.makedirs( str(output_dir) ) + # take care of paths and directories + input_rois_path = roi_file_info["full"] + output_dir = pathtools.join2(output_dir, "2c_fibertyping") + pathtools.create_directory(output_dir) -# open ROIS and show on image -open_rois_from_zip( rm, str(input_rois_path) ) -change_all_roi_color(rm, "blue") -# show_all_rois_on_image( rm, raw ) + roimanager.load_rois_from_zip(rm, input_rois_path) + roimanager.change_roi_color(rm, "blue") # update the log for the user IJ.log( "Now working on " + str(raw_image_title) ) @@ -498,6 +169,7 @@ IJ.log( " -- settings used -- ") IJ.run("Set Measurements...", "area perimeter shape feret's redirect=None decimal=4") IJ.run("Clear Results", "") measure_in_all_rois( raw, fiber_channel_1, rm ) + roimanager.measure_in_all_rois(raw, fiber_channel_1, rm) # loop through the fiber channels, check if positive, add info to results table all_fiber_channels = [fiber_channel_1, fiber_channel_2, fiber_channel_3] @@ -505,18 +177,65 @@ all_min_fiber_intensities = [min_fiber_intensity_1, min_fiber_intensity_2, min_f roi_colors = ["green", "orange", "red"] all_fiber_subsets =[ [], [], [] ] -for index, fiber_channel in enumerate(all_fiber_channels): - if fiber_channel > 0: - preset_results_column( rt, "channel " + str(fiber_channel) + " positive (" + roi_colors[index] + ")", "NO" ) - if all_min_fiber_intensities[index] == 0: - all_min_fiber_intensities[index] = get_threshold_from_method(raw, fiber_channel, "Mean")[0] - IJ.log( "fiber channel " + str(fiber_channel) + " intensity threshold: " + str(all_min_fiber_intensities[index]) ) - positive_fibers = select_positive_fibers( raw, fiber_channel, rm, all_min_fiber_intensities[index] ) - all_fiber_subsets[index] = positive_fibers - if len(positive_fibers) > 0: - change_subset_roi_color(rm, positive_fibers, roi_colors[index]) - save_selected_rois( rm, positive_fibers, output_dir + "/" + raw_image_title + "_positive_fiber_rois_c" + str( fiber_channel ) + ".zip") - add_results( rt, "channel " + str(fiber_channel) + " positive (" + roi_colors[index] + ")", positive_fibers, "YES") + for index, fiber_channel in enumerate(all_fiber_channels): + if fiber_channel > 0: + resultstable.preset_results_column( + rt, "channel %s mean intensity" % fiber_channel, 0 + ) + mean_intensities = measure_mean_of_rois(raw, fiber_channel, rm) + resultstable.add_results_to_resultstable( + rt, + "channel %s mean intensity" % fiber_channel, + range(rm.getCount()), + mean_intensities, + ) + + resultstable.preset_results_column( + rt, + "channel " + + str(fiber_channel) + + " positive (" + + roi_colors[index] + + ")", + "NO", + ) + if all_min_fiber_intensities[index] == 0: + all_min_fiber_intensities[index] = get_threshold_from_method( + raw, fiber_channel, "Mean" + )[0] + IJ.log( + "fiber channel " + + str(fiber_channel) + + " intensity threshold: " + + str(all_min_fiber_intensities[index]) + ) + positive_fibers = roimanager.select_rois_above_min_intensity( + raw, fiber_channel, rm, all_min_fiber_intensities[index] + ) + all_fiber_subsets[index] = positive_fibers + if len(positive_fibers) > 0: + roimanager.change_roi_color(rm, roi_colors[index], positive_fibers) + roimanager.save_rois_to_zip( + rm, + pathtools.join2( + output_dir, + raw_image_title + + "_positive_fiber_rois_c" + + str(fiber_channel) + + ".zip", + ), + positive_fibers, + ) + resultstable.add_results_to_resultstable( + rt, + "channel " + + str(fiber_channel) + + " positive (" + + roi_colors[index] + + ")", + positive_fibers, + "YES", + ) # single positive positive_c1 = all_fiber_subsets[0] @@ -537,22 +256,34 @@ channels = [ (positive_c1_c2_c3, [fiber_channel_1, fiber_channel_2, fiber_channel_3], "white") ] -for positives, ch_nums, color in channels: - if positives: - ch_str = ",".join(map(str, ch_nums)) - color_label = "channel %s positive (%s)" % (ch_str, color) - preset_results_column(rt, color_label.replace(',', '-'), "NO") - change_subset_roi_color(rm, positives, color) - save_selected_rois(rm, positives, "%s/%s_positive_fiber_rois_c%s.zip" % (output_dir, raw_image_title, '_c'.join(map(str, ch_nums)))) - add_results(rt, color_label.replace(',', '-'), positives, "YES") - -# save all results together -save_all_rois( rm, output_dir + "/" + raw_image_title + "_all_fiber_type_rois_color-coded.zip" ) -rt.save(output_dir + "/" + raw_image_title + "_fibertyping_results.csv") - -# dress up the original image, save a overlay-png, present original to the user -raw.show() -show_all_rois_on_image( rm, raw ) + for positives, ch_nums, color in channels: + if positives: + ch_str = ",".join(map(str, ch_nums)) + color_label = "channel %s positive (%s)" % (ch_str, color) + resultstable.preset_results_column(rt, color_label.replace(",", "-"), "NO") + roimanager.change_roi_color(rm, color, positives) + roimanager.save_rois_to_zip( + rm, + "%s/%s_positive_fiber_rois_c%s.zip" + % (output_dir, raw_image_title, "_c".join(map(str, ch_nums))), + positives, + ) + resultstable.add_results_to_resultstable( + rt, color_label.replace(",", "-"), positives, "YES" + ) + + # save all results together + roimanager.save_rois_to_zip( + rm, + pathtools.join2( + output_dir, raw_image_title + "_all_fiber_type_rois_color-coded.zip" + ), + ) + rt.save(pathtools.join2(output_dir, raw_image_title + "_fibertyping_results.csv")) + + # dress up the original image, save a overlay-png, present original to the user + raw.show() + roimanager.show_all_rois_on_image(rm, raw) # raw.setDisplayMode(IJ.COMPOSITE) enhance_contrast( raw ) IJ.run("From ROI Manager", "") # ROIs -> overlays so they show up in the saved png @@ -560,13 +291,13 @@ qc_duplicate = raw.duplicate() IJ.saveAs(qc_duplicate, "PNG", output_dir + "/" + raw_image_title + "_fibertyping") qc_duplicate.close() wm.toFront( raw.getWindow() ) -# IJ.run("Remove Overlay", "") -# raw.setDisplayMode(IJ.GRAYSCALE) -# show_all_rois_on_image( rm, raw ) -total_execution_time_min = (time.time() - execution_start_time) / 60.0 -IJ.log("total time in minutes: " + str(total_execution_time_min)) -IJ.log( "~~ all done ~~" ) -IJ.selectWindow("Log") -IJ.saveAs("Text", str(output_dir + "/" + raw_image_title + "_fibertyping_Log")) -if close_raw == True: - raw.close() \ No newline at end of file + + IJ.log( + "Script finished. Total runtime [HH:MM:SS.ss]: " + + misc.elapsed_time_since(execution_start_time) + ) + IJ.log("~~ all done ~~") + IJ.selectWindow("Log") + IJ.saveAs("Text", str(output_dir + "/" + raw_image_title + "_fibertyping_Log")) + if close_raw: + raw.close()