From c32ebcbe8c9ecc5ebcbf3f980c4694ecaa2c5568 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 16:32:09 +0200 Subject: [PATCH 01/26] Initial commit Mainly copied from spots-in-fibers.py of imcftodo-1089 --- 2d_spots_in_fibers.py | 768 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 768 insertions(+) create mode 100644 2d_spots_in_fibers.py diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py new file mode 100644 index 0000000..3757c8e --- /dev/null +++ b/2d_spots_in_fibers.py @@ -0,0 +1,768 @@ +#@ OpService ops +#@ CommandService command +#@ File (label="select image", description="select your input image", style=file) path_to_image +#@ Integer (label="select image file series", description="leave 1 if not needed", value=1, min=1, max=20, stepSize=1, persist=false, style=slider) series_number +#@ Integer (label="Fiber Segmentation", description="select the Fiber Segmentation RoiSet .zip file") fiber_segmentation_roiset +#@ Integer (label="DAPI channel", description="select the DAPI channel", min=1, max=20, stepSize=1, persist=true, style=slider) dapi_channel_number +#@ Integer (label="DAPI threshold", description="0 = Auto", min=0) threshold +#@ String (label="processing channels", description="comma separated list of channels, e.g. 2,3,6,7", value="1,2,3,4") processing_channels_string +#@ String (label="Spot detection quality threshold", description="comma separated list of values, one per channel", value="80,80,80,80" ) quality_thresholds_string + + +# trackmate imports +from fiji.plugin.trackmate import Settings +from fiji.plugin.trackmate import TrackMate +from fiji.plugin.trackmate.cellpose import CellposeDetectorFactory +from fiji.plugin.trackmate.cellpose.CellposeSettings import PretrainedModel +from fiji.plugin.trackmate.detection import DogDetectorFactory +from fiji.plugin.trackmate.action import LabelImgExporter +# ij imports +from ij import IJ +from ij.plugin import ZProjector, ImageCalculator +from ij.plugin.frame import RoiManager +from ij.measure import ResultsTable +# Bio-Formats imports +from loci.plugins import BF +from loci.common import Region +from loci.plugins.in import ImporterOptions +from loci.formats import ImageReader, MetadataTools, Memoizer +# MorpholibJ imports +from inra.ijpb.binary import BinaryImages +# CLIJ imports +from net.haesleinhuepf.clij2 import CLIJ2 +# imglib2 imports +from net.imglib2.img.display.imagej import ImageJFunctions +from net.imglib2.roi import Regions +from net.imglib2.roi.labeling import LabelRegions +from net.imglib2.algorithm.labeling import ConnectedComponents +# BIOP imports +from ch.epfl.biop.ij2command import Labels2Rois +# python imports +import os + + +def get_parentdir_filename_ext_from_path(path): + """ returns parent directory, filename, and file extension from a given path + + Parameters + ---------- + path : String + the full path to a file + + Returns + ------- + String + the parent directory, the filename without extension and the extension split after the dot + """ + parent_dir = os.path.dirname(path) + base = os.path.basename(path) + (filename, ext) = os.path.splitext(base) + + return (parent_dir, filename, ext) + + +def tile_image(image_width, image_height, tile_width, tile_height, overlap): + """ Calculate tiles of an image of given its dimensions. Calculates tile start coordinates and tile dimensions including overlap, + which can directly be used in Bio-Formats "crop" function. Also calculates tile crop start coordinates and dimensions + to remove the overlap and the crops start coordinates to re-assemble the original size image. Useful to e.g. segment a large + already stitched image tile by tile. + + Parameters + ---------- + image_width : int + the input images width in pixels + image_height : int + the input images height in pixels + tile_width : int + the desired tile width in pixels + tile_height : int + the desired tile height in pixels + overlap : int + the desired tile overlap in pixels + + Returns + ------- + list + all_tile_regions: tile coordinates and dimensions including overlap + all_crop_regions: tile coordinates and dimensions to remove the overlap again + all_tile_start_no_overlap: start coordinates of the tiles without overlap to re-assemble the original size image + """ + # TODO: this function likely needs refactoring to be more clear and readable + number_of_columns = image_width // tile_width # is a floored division + column_remainder_px = image_width % tile_width + number_of_rows = image_height // tile_height + row_remainder_px = image_height % tile_height + all_tile_regions = [] + all_crop_regions = [] + all_tile_start_no_overlap = [] + + for current_row in range(number_of_rows): + if current_row == number_of_rows -1 : + tile_height_boarder_addition = row_remainder_px # increase tile height in the last row + else: + tile_height_boarder_addition = 0 + + for current_column in range (number_of_columns): + if current_column == number_of_columns - 1 : + tile_width_boarder_addition = column_remainder_px # increase tile width in the last column + else: + tile_width_boarder_addition = 0 + + tile_start_x_no_overlap = current_column * tile_width + tile_start_y_no_overlap = current_row * tile_height + + if current_column > 0: + tile_start_x = current_column * tile_width - overlap + crop_start_x = overlap + else: + tile_start_x = 0 + crop_start_x = 0 + + if current_row > 0: + tile_start_y = current_row * tile_height - overlap + crop_start_y = overlap + else: + tile_start_y = 0 + crop_start_y = 0 + + if current_column == 0 or current_column == number_of_columns - 1: + current_tile_width = tile_width + overlap + tile_width_boarder_addition + else: + current_tile_width = tile_width + 2 * overlap + + if current_row == 0 or current_row == number_of_rows - 1: + current_tile_height = tile_height + overlap + tile_height_boarder_addition + else: + current_tile_height = tile_height + 2 * overlap + + tile_region = [tile_start_x, tile_start_y, current_tile_width, current_tile_height] + crop_region = [crop_start_x, crop_start_y, current_tile_width - overlap, current_tile_height - overlap] + tile_start_no_overlap = [tile_start_x_no_overlap, tile_start_y_no_overlap] + + all_tile_regions.append(tile_region) + all_crop_regions.append(crop_region) + all_tile_start_no_overlap.append(tile_start_no_overlap) + + return all_tile_regions, all_crop_regions, all_tile_start_no_overlap + + +def create_empty_image(bit_depth, image_width, image_height): + """creates an empty image of given dimensions. The image is 2D, filled with black pixels of value 0. + + Parameters + ---------- + bit_depth : int + the image bit dept + image_width : int + image width in px + image_height : int + image height in px + + Returns + ------- + ImagePlus + the empty image + """ + + canvas = IJ.createImage("mosaic", str(bit_depth) + "-bit black", image_width, image_height, 1) + + return canvas + + +def write_bf_memoryfile(path_to_file): + """Write a BF memo-file so subsequent access to the same file is faster. + + The Bio-Formats memo-file is written next to the image file (i.e. in the + same folder as the given file). + + Parameters + ---------- + string + The full path to the image file. + """ + reader = Memoizer(ImageReader()) + reader.setId(path_to_file) + reader.close() + + +def get_ome_metadata(path_to_image, series_number): + """Get some metadata fields using BF from an image on disk from a given series. + Gets number of channels, the image width, image height and bit depth. + + Parameters + ---------- + path_to_image : str + full path to the image + series_number : int + the Bio-Formats series number + + Returns + ------- + int + n_channels : the number of channels + int + image_width : the image width in px + int + image_height : the image height in px + int + bit_depth : the image bit depth + """ + series_number -= 1 # python starts counting from 0 + reader = ImageReader() + ome_meta = MetadataTools.createOMEXMLMetadata() + reader.setMetadataStore(ome_meta) + reader.setId(path_to_image) + reader.setSeries(series_number) + n_channels = reader.getSizeC() + image_width = reader.getSizeX() + image_height = reader.getSizeY() + bit_depth = reader.getBitsPerPixel() + + reader.close() + + return n_channels, image_width, image_height, bit_depth + + +def BFopen_image(path_to_image, channel_number, series_number, region=None, z_slice_number=None): + """Use Bio-Formats to open a given image or subset. + + Parameters + ---------- + path_to_image : str + full path to the image file on disk + channel_number : int + the number of the channel to open + series_number : int + the Bio-Formats series number to open + region : list, optional + Bio-Formats crop region, by default None. format = [start_x, start_y, width in px, height in px] + z_slice_number : int, optional + the number of the z-slice to open, by default None + + Returns + ------- + ImagePlus + the requested image + """ + series_number -= 1 # python starts counting from 0 + channel_number -= 1 # python starts counting from 0 + options = ImporterOptions() + options.setId(path_to_image) + options.setColorMode(ImporterOptions.COLOR_MODE_GRAYSCALE) + options.setSplitChannels(False) + options.setSplitFocalPlanes(False) + options.setSplitTimepoints(False) + options.setSeriesOn(series_number, True) + + if region is not None: + options.setCrop(True) + options.setCropRegion(series_number, Region(region[0], region[1], region[2], region[3])) # x,y,w,h + + options.setSpecifyRanges(True) + options.setCBegin(series_number, channel_number) + options.setCEnd(series_number, channel_number) + options.setCStep(series_number, 1) + + if z_slice_number is not None: + z_slice_number -= 1 # python starts counting from 0 + options.setZBegin(series_number, z_slice_number) + options.setZEnd(series_number, z_slice_number) + options.setZStep(series_number, 1) + + imps = BF.openImagePlus(options) + + return imps[0] + + +def run_trackmate_dog_spot_detector(imp, quality_threshold): + """Run TrackMates DoG detector with a given quality threshold + on a target image + + Parameters + ---------- + imp : ImagePlus + the input image + quality_threshold : float or double + the spot detection quality threshold + + Returns + ------- + ImagePlus + a label image of the detected spots. + """ + + cal = imp.getCalibration() + settings = Settings(imp) + + settings.detectorFactory = DogDetectorFactory() + settings.detectorSettings['DO_SUBPIXEL_LOCALIZATION'] = True + settings.detectorSettings['RADIUS'] = 0.15 # type = double + settings.detectorSettings['TARGET_CHANNEL'] = 0 + settings.detectorSettings['THRESHOLD'] = quality_threshold # type = double + settings.detectorSettings['DO_MEDIAN_FILTERING'] = False + + trackmate = TrackMate(settings) + trackmate.computeSpotFeatures(False) + trackmate.computeTrackFeatures(False) + + if not trackmate.checkInput(): + print( str( trackmate.getErrorMessage() ) ) + + if not trackmate.process(): + print( str( trackmate.getErrorMessage() ) ) + + exportSpotsAsDots = True + exportTracksOnly = False + label_imp = LabelImgExporter.createLabelImagePlus(trackmate, exportSpotsAsDots, exportTracksOnly, False) + label_imp.setCalibration(cal) + imp.close() + + return label_imp + + +def run_trackmate_cellpose_detector(imp): + """Run TrackMates CellPose detector on a target image + + Parameters + ---------- + imp : ImagePlus + the input image + + Returns + ------- + ImagePlus + a label image of the detected regions. + """ + + cal = imp.getCalibration() + settings = Settings(imp) + + # TODO: expose more parameters + settings.detectorFactory = CellposeDetectorFactory() + settings.detectorSettings['TARGET_CHANNEL'] = 0 + settings.detectorSettings['OPTIONAL_CHANNEL_2'] = 0 + settings.detectorSettings['CELLPOSE_PYTHON_FILEPATH'] = 'S:/cellpose_env/python.exe' + settings.detectorSettings['CELLPOSE_MODEL_FILEPATH'] = '' + settings.detectorSettings['CELLPOSE_MODEL'] = PretrainedModel.CYTO + settings.detectorSettings['CELL_DIAMETER'] = 30.0 + settings.detectorSettings['USE_GPU'] = True + settings.detectorSettings['SIMPLIFY_CONTOURS'] = True + + trackmate = TrackMate(settings) + trackmate.computeSpotFeatures(False) + trackmate.computeTrackFeatures(False) + + if not trackmate.checkInput(): + print( str( trackmate.getErrorMessage() ) ) + + if not trackmate.process(): + print( str( trackmate.getErrorMessage() ) ) + + exportSpotsAsDots = False + exportTracksOnly = False + label_imp = LabelImgExporter.createLabelImagePlus(trackmate, exportSpotsAsDots, exportTracksOnly, False) + label_imp.setCalibration(cal) + imp.close() + + return label_imp + + +def get_threshold_from_method(imp, method): + """returns the threshold value of chosen IJ AutoThreshold method from a stack (hyperstack?) + + Parameters + ---------- + imp : ImagePlus (maybe also accepts ij2 dataset, img?) + the image from which to get the threshold value + method : string + the AutoThreshold method to use, e.g. "otsu", "yen", ... + + Returns + ------- + int + """ + histogram = ops.run("image.histogram", imp) + threshold_value = ops.run("threshold.%s" % method, histogram) + threshold_value = int(round(threshold_value.get())) + + return threshold_value + + +def convert_to_binary(imp, threshold=1, scale_binary=True): + """Convert grayscale image to a mask / binary image + + Parameters + ---------- + imp : ImagePlus + the input image + threshold : int, optional + the threshold above which pixels are considered foreground, by default 1 + scale_binary : bool, optional + scale the mask image from 0/255 to 0/1, by default True + + Returns + ------- + ImagePlus + the mask / binary image + """ + mask_imp = imp.duplicate() + bitdepth = mask_imp.getBitDepth() + if bitdepth > 16.0: + IJ.run("Conversions...", " ") + IJ.run(mask_imp, "16-bit", "") + IJ.run("Conversions...", "scale") + + IJ.setRawThreshold(mask_imp, threshold, (2**bitdepth)-1, None) + IJ.run(mask_imp, "Convert to Mask", "") + if scale_binary == True: + IJ.run(mask_imp, "Divide...", "value=255") + IJ.run(mask_imp, "Enhance Contrast", "saturated=0.35") + mask_imp.changes = False + + return mask_imp + + +def erode_labelimp_gpu(label_imp, radius, relabel_islands=True): + """Erode a label image on the GPU. + + Parameters + ---------- + label_imp : ImagePlus + the input image + radius : float + radius used for erosion + relabel_islands : bool, optional + if True, split objects will get new labels each , by default True + + Returns + ------- + ImagePlus + the eroded label image + """ + + clij2 = CLIJ2.getInstance() + labels_input = clij2.push(label_imp) + labels_destination = clij2.create(labels_input) + radius = 1.0 + clij2.erodeLabels(labels_input, labels_destination, radius, relabel_islands) + eroded_labelimp = clij2.pull(labels_destination) + clij2.release(labels_input) + clij2.release(labels_destination) + label_imp.close() + + return eroded_labelimp + + +def remove_tileoverlap(imp, crop_region): + """Crop an image to specified dimensions. + The operation is destructive, the original image is lost. + + Parameters + ---------- + imp : ImagePlus + the input image + crop_region : list or array of int + the crop region in the format of [start_x, start_y, crop_width, crop_height] + + Returns + ------- + ImagePlus + the cropped image + """ + + start_x = crop_region[0] + start_y = crop_region[1] + crop_width = crop_region[2] + crop_height = crop_region[3] + imp.setRoi(start_x, start_y, crop_width, crop_height) + cropped_imp = imp.crop() + imp.close() + + return cropped_imp + + +def copy_and_paste_image(source, target, start_coordinates_xy): + """Copy the source image and paste it into the target image + + Parameters + ---------- + source : ImagePlus + the source image + target : ImagePlus + the target image + start_coordinates_xy : list or array of int + the x and y coordinates in the target image where the source image will be pasted + """ + source.copy() + start_x = start_coordinates_xy[0] + start_y = start_coordinates_xy[1] + target.paste(start_x, start_y) + source.close() + + +def merge_touching_labels(label_imp, bit_depth): + """merge touching labels in a label image so they have the same label ID. + + Parameters + ---------- + label_imp : ImagePlus + the label image with touching labels of different label ID + bit_depth : int + the image bit depth + + Returns + ------- + ImagePlus + the label image with merged touching labels + """ + + binary_imp = BinaryImages.binarize(label_imp) + connectivity = 8 + merged_label_imp = BinaryImages.componentsLabeling(binary_imp, connectivity, bit_depth) + label_imp.close() + binary_imp.close() + + return merged_label_imp + + +def save_image_as_IJtif(imp, filename, suffix, target): + """Save the image as ImageJ-Tiff + + Parameters + ---------- + imp : ImagePlus + the input Image to be saved + filename : str + the image filename without extension + suffix : int, float or str + a suffix that will be added to the filename as a string + target : str + the target directory in with to save the image + """ + + savename = filename + "_" + str(suffix) + ".tif" + savepath = os.path.join(target, savename) + IJ.log("now saving: " + str(savepath)) + IJ.saveAs(imp, "Tiff", savepath) + + +def measure_label_size_shape_2d(label_imp): + rm = RoiManager.getInstance() + if not rm: + rm = RoiManager() + rm.reset() + rt = ResultsTable() + + command.run( Labels2Rois , False , 'imp' , label_imp , 'rm', rm).get() + IJ.run("Set Measurements...", "area centroid perimeter shape feret's redirect=None decimal=3") + rm.runCommand(label_imp,"Deselect") + rm.runCommand(label_imp,"Measure") + rm.reset() + IJ.renameResults("Results", "Size and shape features") + results_table = rt.getResultsTable("Size and shape features") + + + return results_table + + +def convert_labelimage_to_imglib2regions(imp): + """Convert a ImagePlus label image to imglib2 regions + + Parameters + ---------- + imp : ImagePlus + the input label image + + Returns + ------- + Imglib2 LabelRegions + the imglib2 label regions + """ + + # fist convert ImagePlus to img as suggested by curtis: + # from https://forum.image.sc/t/how-to-wrap-any-kind-of-imageplus-to-an-imglib2-img-floattype/178/6 + wrapImg = ImageJFunctions.wrap(imp) + # workaround to convert a label image into an imglib2 ImgLabeling (which contains not only an image) + img_labeling = ops.labeling().cca(wrapImg, ConnectedComponents.StructuringElement.EIGHT_CONNECTED) + # img = img_labeling.getIndexImg() # the img label image + # ImageJFunctions.show(img) + regions = LabelRegions(img_labeling) + + return regions + + +def convert_labelimage_to_binary(label_imp, scale_binary=True): + """Convert a label image to a mask / binary image + + Parameters + ---------- + label_imp : ImagePlus + the input label image + scale_binary : bool, optional + scale the mask image from 0/255 to 0/1, by default True + + Returns + ------- + ImagePlus + the mask / binary image + """ + binary_imp = BinaryImages.binarize(label_imp) + if scale_binary == True: + IJ.run(binary_imp, "Divide...", "value=255") + + return binary_imp + + +def measure_intensity_sum(label_imp, target_imp): + """Measure the sum intensity for each label in a label image on another target image. + Uses imglib2 and ops for this and works with both 2D and 3D label images. + + Parameters + ---------- + label_imp : ImagePlus + the input label image + target_imp : ImagePlus + the target image in which to measure + + Returns + ------- + array + label id and corresponding sum intensity [label_id, sum_intensity] + """ + label_id = [] + sum_intensity = [] + target_img = ImageJFunctions.wrap(target_imp) # convert ImagePlus to img to use ops/region measurements on it + regions = convert_labelimage_to_imglib2regions(label_imp) + + for region in regions: + label_id.append(region.getLabel() + 1) # region.getlabel() starts counting from 0. So the label with int 1 has the index 0. + input = Regions.sample(region, target_img) # returns an iterableInterval + sum = ops.stats().sum(input).getRealDouble() + sum_intensity.append(sum) + # other measurements see https://forum.image.sc/t/can-i-get-to-measurements-using-imagej-ops/4448/5 + result = [label_id, sum_intensity] + + return result + + +def add_results_to_resultstable(results_table, column, values): + """Add values to the ResultsTable starting from row 0 of a given column. + + Parameters + ---------- + results_table : ij.measure.ResultsTable + a reference of the IJ-ResultsTable + column : string + the column in which to add the values + values : list(int, double or float) + array with values to be added + """ + for index, value in enumerate(values): + results_table.setValue(column, index, value) + + +def save_results_table(results_table, filename, suffix, target): + """Save an IJ-ResultsTable as txt to a given location + + Parameters + ---------- + results_table : IJ-ResultsTable + an IJ-ResultsTable + filename : str + filename to include in the tables name + suffix : str, int or float + a suffix to be included in the tables name + target : str + the target directory in which to save the table + """ + + savename = filename + "_" + str(suffix) + ".txt" + savepath = os.path.join(target, savename) + results_table.save(savepath) + + +def save_labelimage_as_ijroiset(label_imp, filename, suffix, target): + """Save all labels i a label Images as IJ-ROIset to target location. + Only 2D, requires PTBIOP update site. + + Parameters + ---------- + label_imp : ImagePlus + the input label image + filename : str + the filename to include in the ROIset name + suffix : str, int or float + a suffix to be included in the ROIset name + target : str + the target directory in which to save the table + """ + rm = RoiManager.getInstance() + if not rm: + rm = RoiManager() + rm.reset() + command.run( Labels2Rois , False , 'imp' , label_imp , 'rm', rm).get() + savename = filename + "_" + str(suffix) + ".zip" + savepath = os.path.join(target, savename) + rm.runCommand("Save", savepath) + rm.reset() + + +def close_images(list_of_imps): + """Close given ImagePlus images + + Parameters + ---------- + list_of_imps : list of ImagePlus + list of ImagePlus images to be closed + """ + for imp in list_of_imps: + imp.changes = False + imp.close() + + +path_to_image = str(path_to_image).replace("\\","/") +parent_dir, filename, ext = get_parentdir_filename_ext_from_path(path_to_image) +write_bf_memoryfile(path_to_image) +n_channels, image_width, image_height, bit_depth = get_ome_metadata(path_to_image, series_number) +processing_channels_string = processing_channels_string.replace(" ", "") +processing_channels = processing_channels_string.split(",") +quality_thresholds_string = quality_thresholds_string.replace(" ", "") +quality_thresholds = quality_thresholds_string.split(",") + +# TODO: Get the fiber segmentation and convert to labelimage + +# threshold DAPI channel and convert to binary +dapi_channel = BFopen_image(path_to_image, dapi_channel_number, series_number, z_slice_number=dapi_channel_zslice) +if threshold <= 0: + threshold = get_threshold_from_method(dapi_channel, "otsu") +dapi_binary = convert_to_binary(dapi_channel, threshold) +close_images([dapi_channel]) + +# detect spots and count them per fiber +results_table = ResultsTable() +for index, channel in enumerate(processing_channels): + channel = int(channel) + quality_threshold = float(quality_thresholds[index]) + spots_channel = BFopen_image(path_to_image, channel, series_number) # can be a stack + spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, quality_threshold) # spot detection is 3D + save_image_as_IJtif(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) + save_labelimage_as_ijroiset(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) + spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) # can be a stack + spots_binary_imp_sum_proj = ZProjector.run(spots_binary_imp, "sum") # if 2 (n) spots perfecly overlap in z, the pixel value will be 2 (n) + dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp_sum_proj, dapi_binary, "Multiply create") + dapi_negative_spots_binary = ImageCalculator.run(spots_binary_imp_sum_proj, dapi_positive_spots_binary, "Subtract create") + + n_spots_per_fiber = measure_intensity_sum(fibers_label_imp, spots_binary_imp_sum_proj) + n_dapi_positive_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_positive_spots_binary) + n_dapi_negative_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_negative_spots_binary) + + # TODO: the column fiber label ID needs to be added only one time. Maybe check before if column exists. + add_results_to_resultstable(results_table, "fiber label ID", n_spots_per_fiber[0]) + add_results_to_resultstable(results_table, "n spots channel " + str(channel), n_spots_per_fiber[1]) + add_results_to_resultstable(results_table, "n spots dapi positive channel " + str(channel), n_dapi_positive_spots_per_fiber[1]) + add_results_to_resultstable(results_table, "n spots dapi negative channel " + str(channel), n_dapi_negative_spots_per_fiber[1]) + close_images([spots_channel, spots_label_imp, spots_binary_imp, spots_binary_imp_sum_proj, dapi_binary, dapi_positive_spots_binary, dapi_negative_spots_binary]) + +results_table.show("Spots") +save_results_table(results_table, filename, "Spots", parent_dir) +IJ.log("DONE") \ No newline at end of file -- GitLab From d8310623759b2be3866c2d7b499c5e8d618c9f89 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 17:15:20 +0200 Subject: [PATCH 02/26] Add function to convert ijrois to labelimage --- 2d_spots_in_fibers.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 3757c8e..a5b54fe 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -36,7 +36,7 @@ from net.imglib2.roi import Regions from net.imglib2.roi.labeling import LabelRegions from net.imglib2.algorithm.labeling import ConnectedComponents # BIOP imports -from ch.epfl.biop.ij2command import Labels2Rois +from ch.epfl.biop.ij2command import Labels2Rois, Rois2Labels # python imports import os @@ -707,6 +707,33 @@ def save_labelimage_as_ijroiset(label_imp, filename, suffix, target): rm.reset() +def load_rois_from_zip(path): + """Load ROIs from the given zip file and add them to the RoiManager. + + Parameters + ---------- + path : string + Path to the ROI zip file. + """ + rm = RoiManager.getInstance() + if not rm: + rm = RoiManager() + rm.reset() + + rm.runCommand("Open", path) + + +def convert_rois_to_labelimage(imp): + rm = RoiManager.getInstance() + if not rm: + rm = RoiManager() + + label_imp = command.run( Rois2Labels , False , 'imp' , imp , 'rm', rm).get().getOutput("label_imp") + rm.reset() # TODO: should be optional but can be default + + return label_imp + + def close_images(list_of_imps): """Close given ImagePlus images -- GitLab From 005655c760979b82b0cb73539698e1d6e7996d87 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 17:16:07 +0200 Subject: [PATCH 03/26] Get path to ij roiset from GUI --- 2d_spots_in_fibers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index a5b54fe..08f87b6 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -2,7 +2,7 @@ #@ CommandService command #@ File (label="select image", description="select your input image", style=file) path_to_image #@ Integer (label="select image file series", description="leave 1 if not needed", value=1, min=1, max=20, stepSize=1, persist=false, style=slider) series_number -#@ Integer (label="Fiber Segmentation", description="select the Fiber Segmentation RoiSet .zip file") fiber_segmentation_roiset +#@ File (label="Fiber Segmentation", description="select the Fiber Segmentation RoiSet .zip file", style=file) fiber_segmentation_roiset #@ Integer (label="DAPI channel", description="select the DAPI channel", min=1, max=20, stepSize=1, persist=true, style=slider) dapi_channel_number #@ Integer (label="DAPI threshold", description="0 = Auto", min=0) threshold #@ String (label="processing channels", description="comma separated list of channels, e.g. 2,3,6,7", value="1,2,3,4") processing_channels_string @@ -748,6 +748,7 @@ def close_images(list_of_imps): path_to_image = str(path_to_image).replace("\\","/") +fiber_segmentation_roiset = str(fiber_segmentation_roiset).replace("\\","/") parent_dir, filename, ext = get_parentdir_filename_ext_from_path(path_to_image) write_bf_memoryfile(path_to_image) n_channels, image_width, image_height, bit_depth = get_ome_metadata(path_to_image, series_number) -- GitLab From f6b804c8a44a8a797da9cade12b284bb5635b220 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 17:17:16 +0200 Subject: [PATCH 04/26] Create Labelimage from ij roiset --- 2d_spots_in_fibers.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 08f87b6..53df4a2 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -757,7 +757,12 @@ processing_channels = processing_channels_string.split(",") quality_thresholds_string = quality_thresholds_string.replace(" ", "") quality_thresholds = quality_thresholds_string.split(",") -# TODO: Get the fiber segmentation and convert to labelimage +# Get the fiber segmentation and convert to labelimage +canvas = create_empty_image(32, image_width, image_height) +load_rois_from_zip(fiber_segmentation_roiset) +fiber_label_imp = convert_rois_to_labelimage(canvas) +canvas.close() +fiber_label_imp.show() # threshold DAPI channel and convert to binary dapi_channel = BFopen_image(path_to_image, dapi_channel_number, series_number, z_slice_number=dapi_channel_zslice) -- GitLab From 9a094bcc63e6ef72096c4269550d2c97ae6cc9cf Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 17:18:48 +0200 Subject: [PATCH 05/26] Don't use z-slice option, input is a MIP --- 2d_spots_in_fibers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 53df4a2..41ca9e4 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -765,7 +765,7 @@ canvas.close() fiber_label_imp.show() # threshold DAPI channel and convert to binary -dapi_channel = BFopen_image(path_to_image, dapi_channel_number, series_number, z_slice_number=dapi_channel_zslice) +dapi_channel = BFopen_image(path_to_image, dapi_channel_number, series_number) if threshold <= 0: threshold = get_threshold_from_method(dapi_channel, "otsu") dapi_binary = convert_to_binary(dapi_channel, threshold) -- GitLab From 106d690d5b4c1a92e40ca840596291315984d1ec Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 17:32:17 +0200 Subject: [PATCH 06/26] Remove sum projection as data is a 2D MIP --- 2d_spots_in_fibers.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 41ca9e4..23751d0 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -776,16 +776,15 @@ results_table = ResultsTable() for index, channel in enumerate(processing_channels): channel = int(channel) quality_threshold = float(quality_thresholds[index]) - spots_channel = BFopen_image(path_to_image, channel, series_number) # can be a stack - spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, quality_threshold) # spot detection is 3D + spots_channel = BFopen_image(path_to_image, channel, series_number) + spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, quality_threshold) save_image_as_IJtif(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) save_labelimage_as_ijroiset(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) - spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) # can be a stack - spots_binary_imp_sum_proj = ZProjector.run(spots_binary_imp, "sum") # if 2 (n) spots perfecly overlap in z, the pixel value will be 2 (n) - dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp_sum_proj, dapi_binary, "Multiply create") - dapi_negative_spots_binary = ImageCalculator.run(spots_binary_imp_sum_proj, dapi_positive_spots_binary, "Subtract create") + spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) + dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_binary, "Multiply create") + dapi_negative_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_positive_spots_binary, "Subtract create") - n_spots_per_fiber = measure_intensity_sum(fibers_label_imp, spots_binary_imp_sum_proj) + n_spots_per_fiber = measure_intensity_sum(fibers_label_imp, spots_binary_imp) n_dapi_positive_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_positive_spots_binary) n_dapi_negative_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_negative_spots_binary) @@ -794,7 +793,7 @@ for index, channel in enumerate(processing_channels): add_results_to_resultstable(results_table, "n spots channel " + str(channel), n_spots_per_fiber[1]) add_results_to_resultstable(results_table, "n spots dapi positive channel " + str(channel), n_dapi_positive_spots_per_fiber[1]) add_results_to_resultstable(results_table, "n spots dapi negative channel " + str(channel), n_dapi_negative_spots_per_fiber[1]) - close_images([spots_channel, spots_label_imp, spots_binary_imp, spots_binary_imp_sum_proj, dapi_binary, dapi_positive_spots_binary, dapi_negative_spots_binary]) + close_images([spots_channel, spots_label_imp, spots_binary_imp, spots_binary_imp, dapi_binary, dapi_positive_spots_binary, dapi_negative_spots_binary]) results_table.show("Spots") save_results_table(results_table, filename, "Spots", parent_dir) -- GitLab From db261db207c53f5d2ab718801dce70d818e5f024 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 17:32:40 +0200 Subject: [PATCH 07/26] Do not show intermediate image --- 2d_spots_in_fibers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 23751d0..ae047bd 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -762,7 +762,6 @@ canvas = create_empty_image(32, image_width, image_height) load_rois_from_zip(fiber_segmentation_roiset) fiber_label_imp = convert_rois_to_labelimage(canvas) canvas.close() -fiber_label_imp.show() # threshold DAPI channel and convert to binary dapi_channel = BFopen_image(path_to_image, dapi_channel_number, series_number) -- GitLab From 811cdd7425ba30df15c2bba0efb331f293900063 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Mon, 14 Apr 2025 17:32:51 +0200 Subject: [PATCH 08/26] Precises comment --- 2d_spots_in_fibers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index ae047bd..f16d7c7 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -757,10 +757,10 @@ processing_channels = processing_channels_string.split(",") quality_thresholds_string = quality_thresholds_string.replace(" ", "") quality_thresholds = quality_thresholds_string.split(",") -# Get the fiber segmentation and convert to labelimage +# Get the fiber segmentation from ij roizip and convert to labelimage canvas = create_empty_image(32, image_width, image_height) load_rois_from_zip(fiber_segmentation_roiset) -fiber_label_imp = convert_rois_to_labelimage(canvas) +fibers_label_imp = convert_rois_to_labelimage(canvas) canvas.close() # threshold DAPI channel and convert to binary -- GitLab From d6ed724d72bf72a7bf67d3aefbe941e93c882ffc Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 11:11:11 +0200 Subject: [PATCH 09/26] Adapt to api change in trackmate --- 2d_spots_in_fibers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index f16d7c7..6dffdfc 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -313,7 +313,8 @@ def run_trackmate_dog_spot_detector(imp, quality_threshold): exportSpotsAsDots = True exportTracksOnly = False - label_imp = LabelImgExporter.createLabelImagePlus(trackmate, exportSpotsAsDots, exportTracksOnly, False) + labelIdPainting = LabelImgExporter.LabelIdPainting.LABEL_IS_SPOT_ID + label_imp = LabelImgExporter.createLabelImagePlus(trackmate, exportSpotsAsDots, exportTracksOnly, labelIdPainting) label_imp.setCalibration(cal) imp.close() -- GitLab From 48234c5bd687e0ad1871df95c1071909e31cdb30 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 11:12:34 +0200 Subject: [PATCH 10/26] Add minimun spot diameter as parameter --- 2d_spots_in_fibers.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 6dffdfc..3d169ac 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -7,7 +7,7 @@ #@ Integer (label="DAPI threshold", description="0 = Auto", min=0) threshold #@ String (label="processing channels", description="comma separated list of channels, e.g. 2,3,6,7", value="1,2,3,4") processing_channels_string #@ String (label="Spot detection quality threshold", description="comma separated list of values, one per channel", value="80,80,80,80" ) quality_thresholds_string - +#@ Double (label="Minimum spot diameter", description="smaller spots will be discarded, value=5, min=1, max=200, stepSize=0.5, persist=false, style=slider) spot_diameter # trackmate imports from fiji.plugin.trackmate import Settings @@ -274,7 +274,7 @@ def BFopen_image(path_to_image, channel_number, series_number, region=None, z_sl return imps[0] -def run_trackmate_dog_spot_detector(imp, quality_threshold): +def run_trackmate_dog_spot_detector(imp, spot_diameter, quality_threshold): """Run TrackMates DoG detector with a given quality threshold on a target image @@ -296,7 +296,7 @@ def run_trackmate_dog_spot_detector(imp, quality_threshold): settings.detectorFactory = DogDetectorFactory() settings.detectorSettings['DO_SUBPIXEL_LOCALIZATION'] = True - settings.detectorSettings['RADIUS'] = 0.15 # type = double + settings.detectorSettings['RADIUS'] = spot_diameter / 2 # type = double settings.detectorSettings['TARGET_CHANNEL'] = 0 settings.detectorSettings['THRESHOLD'] = quality_threshold # type = double settings.detectorSettings['DO_MEDIAN_FILTERING'] = False @@ -758,18 +758,16 @@ processing_channels = processing_channels_string.split(",") quality_thresholds_string = quality_thresholds_string.replace(" ", "") quality_thresholds = quality_thresholds_string.split(",") -# Get the fiber segmentation from ij roizip and convert to labelimage -canvas = create_empty_image(32, image_width, image_height) -load_rois_from_zip(fiber_segmentation_roiset) -fibers_label_imp = convert_rois_to_labelimage(canvas) -canvas.close() - # threshold DAPI channel and convert to binary dapi_channel = BFopen_image(path_to_image, dapi_channel_number, series_number) if threshold <= 0: threshold = get_threshold_from_method(dapi_channel, "otsu") dapi_binary = convert_to_binary(dapi_channel, threshold) -close_images([dapi_channel]) + +# Get the fiber segmentation from ij roizip and convert to labelimage +load_rois_from_zip(fiber_segmentation_roiset) +fibers_label_imp = convert_rois_to_labelimage(dapi_channel) +dapi_channel.close() # detect spots and count them per fiber results_table = ResultsTable() -- GitLab From d598275083a8f64f1a0bb84a6223d65abad6b67f Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 11:42:09 +0200 Subject: [PATCH 11/26] Remove unused functions and imports --- 2d_spots_in_fibers.py | 283 +----------------------------------------- 1 file changed, 1 insertion(+), 282 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 3d169ac..0319dd4 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -12,13 +12,11 @@ # trackmate imports from fiji.plugin.trackmate import Settings from fiji.plugin.trackmate import TrackMate -from fiji.plugin.trackmate.cellpose import CellposeDetectorFactory -from fiji.plugin.trackmate.cellpose.CellposeSettings import PretrainedModel from fiji.plugin.trackmate.detection import DogDetectorFactory from fiji.plugin.trackmate.action import LabelImgExporter # ij imports from ij import IJ -from ij.plugin import ZProjector, ImageCalculator +from ij.plugin import ImageCalculator from ij.plugin.frame import RoiManager from ij.measure import ResultsTable # Bio-Formats imports @@ -28,8 +26,6 @@ from loci.plugins.in import ImporterOptions from loci.formats import ImageReader, MetadataTools, Memoizer # MorpholibJ imports from inra.ijpb.binary import BinaryImages -# CLIJ imports -from net.haesleinhuepf.clij2 import CLIJ2 # imglib2 imports from net.imglib2.img.display.imagej import ImageJFunctions from net.imglib2.roi import Regions @@ -61,114 +57,6 @@ def get_parentdir_filename_ext_from_path(path): return (parent_dir, filename, ext) -def tile_image(image_width, image_height, tile_width, tile_height, overlap): - """ Calculate tiles of an image of given its dimensions. Calculates tile start coordinates and tile dimensions including overlap, - which can directly be used in Bio-Formats "crop" function. Also calculates tile crop start coordinates and dimensions - to remove the overlap and the crops start coordinates to re-assemble the original size image. Useful to e.g. segment a large - already stitched image tile by tile. - - Parameters - ---------- - image_width : int - the input images width in pixels - image_height : int - the input images height in pixels - tile_width : int - the desired tile width in pixels - tile_height : int - the desired tile height in pixels - overlap : int - the desired tile overlap in pixels - - Returns - ------- - list - all_tile_regions: tile coordinates and dimensions including overlap - all_crop_regions: tile coordinates and dimensions to remove the overlap again - all_tile_start_no_overlap: start coordinates of the tiles without overlap to re-assemble the original size image - """ - # TODO: this function likely needs refactoring to be more clear and readable - number_of_columns = image_width // tile_width # is a floored division - column_remainder_px = image_width % tile_width - number_of_rows = image_height // tile_height - row_remainder_px = image_height % tile_height - all_tile_regions = [] - all_crop_regions = [] - all_tile_start_no_overlap = [] - - for current_row in range(number_of_rows): - if current_row == number_of_rows -1 : - tile_height_boarder_addition = row_remainder_px # increase tile height in the last row - else: - tile_height_boarder_addition = 0 - - for current_column in range (number_of_columns): - if current_column == number_of_columns - 1 : - tile_width_boarder_addition = column_remainder_px # increase tile width in the last column - else: - tile_width_boarder_addition = 0 - - tile_start_x_no_overlap = current_column * tile_width - tile_start_y_no_overlap = current_row * tile_height - - if current_column > 0: - tile_start_x = current_column * tile_width - overlap - crop_start_x = overlap - else: - tile_start_x = 0 - crop_start_x = 0 - - if current_row > 0: - tile_start_y = current_row * tile_height - overlap - crop_start_y = overlap - else: - tile_start_y = 0 - crop_start_y = 0 - - if current_column == 0 or current_column == number_of_columns - 1: - current_tile_width = tile_width + overlap + tile_width_boarder_addition - else: - current_tile_width = tile_width + 2 * overlap - - if current_row == 0 or current_row == number_of_rows - 1: - current_tile_height = tile_height + overlap + tile_height_boarder_addition - else: - current_tile_height = tile_height + 2 * overlap - - tile_region = [tile_start_x, tile_start_y, current_tile_width, current_tile_height] - crop_region = [crop_start_x, crop_start_y, current_tile_width - overlap, current_tile_height - overlap] - tile_start_no_overlap = [tile_start_x_no_overlap, tile_start_y_no_overlap] - - all_tile_regions.append(tile_region) - all_crop_regions.append(crop_region) - all_tile_start_no_overlap.append(tile_start_no_overlap) - - return all_tile_regions, all_crop_regions, all_tile_start_no_overlap - - -def create_empty_image(bit_depth, image_width, image_height): - """creates an empty image of given dimensions. The image is 2D, filled with black pixels of value 0. - - Parameters - ---------- - bit_depth : int - the image bit dept - image_width : int - image width in px - image_height : int - image height in px - - Returns - ------- - ImagePlus - the empty image - """ - - canvas = IJ.createImage("mosaic", str(bit_depth) + "-bit black", image_width, image_height, 1) - - return canvas - - def write_bf_memoryfile(path_to_file): """Write a BF memo-file so subsequent access to the same file is faster. @@ -321,53 +209,6 @@ def run_trackmate_dog_spot_detector(imp, spot_diameter, quality_threshold): return label_imp -def run_trackmate_cellpose_detector(imp): - """Run TrackMates CellPose detector on a target image - - Parameters - ---------- - imp : ImagePlus - the input image - - Returns - ------- - ImagePlus - a label image of the detected regions. - """ - - cal = imp.getCalibration() - settings = Settings(imp) - - # TODO: expose more parameters - settings.detectorFactory = CellposeDetectorFactory() - settings.detectorSettings['TARGET_CHANNEL'] = 0 - settings.detectorSettings['OPTIONAL_CHANNEL_2'] = 0 - settings.detectorSettings['CELLPOSE_PYTHON_FILEPATH'] = 'S:/cellpose_env/python.exe' - settings.detectorSettings['CELLPOSE_MODEL_FILEPATH'] = '' - settings.detectorSettings['CELLPOSE_MODEL'] = PretrainedModel.CYTO - settings.detectorSettings['CELL_DIAMETER'] = 30.0 - settings.detectorSettings['USE_GPU'] = True - settings.detectorSettings['SIMPLIFY_CONTOURS'] = True - - trackmate = TrackMate(settings) - trackmate.computeSpotFeatures(False) - trackmate.computeTrackFeatures(False) - - if not trackmate.checkInput(): - print( str( trackmate.getErrorMessage() ) ) - - if not trackmate.process(): - print( str( trackmate.getErrorMessage() ) ) - - exportSpotsAsDots = False - exportTracksOnly = False - label_imp = LabelImgExporter.createLabelImagePlus(trackmate, exportSpotsAsDots, exportTracksOnly, False) - label_imp.setCalibration(cal) - imp.close() - - return label_imp - - def get_threshold_from_method(imp, method): """returns the threshold value of chosen IJ AutoThreshold method from a stack (hyperstack?) @@ -423,109 +264,6 @@ def convert_to_binary(imp, threshold=1, scale_binary=True): return mask_imp -def erode_labelimp_gpu(label_imp, radius, relabel_islands=True): - """Erode a label image on the GPU. - - Parameters - ---------- - label_imp : ImagePlus - the input image - radius : float - radius used for erosion - relabel_islands : bool, optional - if True, split objects will get new labels each , by default True - - Returns - ------- - ImagePlus - the eroded label image - """ - - clij2 = CLIJ2.getInstance() - labels_input = clij2.push(label_imp) - labels_destination = clij2.create(labels_input) - radius = 1.0 - clij2.erodeLabels(labels_input, labels_destination, radius, relabel_islands) - eroded_labelimp = clij2.pull(labels_destination) - clij2.release(labels_input) - clij2.release(labels_destination) - label_imp.close() - - return eroded_labelimp - - -def remove_tileoverlap(imp, crop_region): - """Crop an image to specified dimensions. - The operation is destructive, the original image is lost. - - Parameters - ---------- - imp : ImagePlus - the input image - crop_region : list or array of int - the crop region in the format of [start_x, start_y, crop_width, crop_height] - - Returns - ------- - ImagePlus - the cropped image - """ - - start_x = crop_region[0] - start_y = crop_region[1] - crop_width = crop_region[2] - crop_height = crop_region[3] - imp.setRoi(start_x, start_y, crop_width, crop_height) - cropped_imp = imp.crop() - imp.close() - - return cropped_imp - - -def copy_and_paste_image(source, target, start_coordinates_xy): - """Copy the source image and paste it into the target image - - Parameters - ---------- - source : ImagePlus - the source image - target : ImagePlus - the target image - start_coordinates_xy : list or array of int - the x and y coordinates in the target image where the source image will be pasted - """ - source.copy() - start_x = start_coordinates_xy[0] - start_y = start_coordinates_xy[1] - target.paste(start_x, start_y) - source.close() - - -def merge_touching_labels(label_imp, bit_depth): - """merge touching labels in a label image so they have the same label ID. - - Parameters - ---------- - label_imp : ImagePlus - the label image with touching labels of different label ID - bit_depth : int - the image bit depth - - Returns - ------- - ImagePlus - the label image with merged touching labels - """ - - binary_imp = BinaryImages.binarize(label_imp) - connectivity = 8 - merged_label_imp = BinaryImages.componentsLabeling(binary_imp, connectivity, bit_depth) - label_imp.close() - binary_imp.close() - - return merged_label_imp - - def save_image_as_IJtif(imp, filename, suffix, target): """Save the image as ImageJ-Tiff @@ -547,25 +285,6 @@ def save_image_as_IJtif(imp, filename, suffix, target): IJ.saveAs(imp, "Tiff", savepath) -def measure_label_size_shape_2d(label_imp): - rm = RoiManager.getInstance() - if not rm: - rm = RoiManager() - rm.reset() - rt = ResultsTable() - - command.run( Labels2Rois , False , 'imp' , label_imp , 'rm', rm).get() - IJ.run("Set Measurements...", "area centroid perimeter shape feret's redirect=None decimal=3") - rm.runCommand(label_imp,"Deselect") - rm.runCommand(label_imp,"Measure") - rm.reset() - IJ.renameResults("Results", "Size and shape features") - results_table = rt.getResultsTable("Size and shape features") - - - return results_table - - def convert_labelimage_to_imglib2regions(imp): """Convert a ImagePlus label image to imglib2 regions -- GitLab From 80ed315b5c8e43787fbee0c1ac40016871e0a584 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 11:44:04 +0200 Subject: [PATCH 12/26] Fix syntax error --- 2d_spots_in_fibers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 0319dd4..736d494 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -7,7 +7,7 @@ #@ Integer (label="DAPI threshold", description="0 = Auto", min=0) threshold #@ String (label="processing channels", description="comma separated list of channels, e.g. 2,3,6,7", value="1,2,3,4") processing_channels_string #@ String (label="Spot detection quality threshold", description="comma separated list of values, one per channel", value="80,80,80,80" ) quality_thresholds_string -#@ Double (label="Minimum spot diameter", description="smaller spots will be discarded, value=5, min=1, max=200, stepSize=0.5, persist=false, style=slider) spot_diameter +#@ Double (label="Minimum spot diameter", description="smaller spots will be discarded", value=5, min=1, max=200, stepSize=0.5, persist=false) spot_diameter # trackmate imports from fiji.plugin.trackmate import Settings -- GitLab From 34beb9c2f2132d25faa9a84327fa778e90534f2b Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 16:40:02 +0200 Subject: [PATCH 13/26] Change spot label ID painting method --- 2d_spots_in_fibers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 736d494..4f51642 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -201,7 +201,7 @@ def run_trackmate_dog_spot_detector(imp, spot_diameter, quality_threshold): exportSpotsAsDots = True exportTracksOnly = False - labelIdPainting = LabelImgExporter.LabelIdPainting.LABEL_IS_SPOT_ID + labelIdPainting = LabelImgExporter.LabelIdPainting.LABEL_IS_INDEX_MOVIE_UNIQUE label_imp = LabelImgExporter.createLabelImagePlus(trackmate, exportSpotsAsDots, exportTracksOnly, labelIdPainting) label_imp.setCalibration(cal) imp.close() -- GitLab From f1669c5c2bdd42fd74f907b1268396a8099e2be3 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 16:40:32 +0200 Subject: [PATCH 14/26] set default spot diameter to 0.3 --- 2d_spots_in_fibers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 4f51642..0c42fbf 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -7,7 +7,7 @@ #@ Integer (label="DAPI threshold", description="0 = Auto", min=0) threshold #@ String (label="processing channels", description="comma separated list of channels, e.g. 2,3,6,7", value="1,2,3,4") processing_channels_string #@ String (label="Spot detection quality threshold", description="comma separated list of values, one per channel", value="80,80,80,80" ) quality_thresholds_string -#@ Double (label="Minimum spot diameter", description="smaller spots will be discarded", value=5, min=1, max=200, stepSize=0.5, persist=false) spot_diameter +#@ Double (label="Minimum spot diameter", description="smaller spots will be discarded", value=0.3, min=1, max=10, stepSize=0.1) spot_diameter # trackmate imports from fiji.plugin.trackmate import Settings -- GitLab From 352cb1c9ed28a0b93d83f921a2db65d3f7c6f095 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 16:41:11 +0200 Subject: [PATCH 15/26] Add missing spot diameter parameter to function --- 2d_spots_in_fibers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 0c42fbf..2bbd1e5 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -494,7 +494,7 @@ for index, channel in enumerate(processing_channels): channel = int(channel) quality_threshold = float(quality_thresholds[index]) spots_channel = BFopen_image(path_to_image, channel, series_number) - spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, quality_threshold) + spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, spot_diameter, quality_threshold) save_image_as_IJtif(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) save_labelimage_as_ijroiset(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) -- GitLab From f202930ed32d4dc81f2b1b6ef62373cf00a808cb Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Tue, 15 Apr 2025 16:41:26 +0200 Subject: [PATCH 16/26] Run Garbage Collector at the end of the script --- 2d_spots_in_fibers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 2bbd1e5..9fd778b 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -514,4 +514,5 @@ for index, channel in enumerate(processing_channels): results_table.show("Spots") save_results_table(results_table, filename, "Spots", parent_dir) +IJ.run("Collect Garbage", ""); IJ.log("DONE") \ No newline at end of file -- GitLab From f21b7d89b59e0b66d347b96b1b1ceac9ae017e5e Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 10:01:33 +0200 Subject: [PATCH 17/26] Remove unnecessary semicolon --- 2d_spots_in_fibers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 9fd778b..fff2206 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -514,5 +514,5 @@ for index, channel in enumerate(processing_channels): results_table.show("Spots") save_results_table(results_table, filename, "Spots", parent_dir) -IJ.run("Collect Garbage", ""); +IJ.run("Collect Garbage", "") IJ.log("DONE") \ No newline at end of file -- GitLab From fa02df7242365117df2b34aaf98102700388e6bc Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 10:01:52 +0200 Subject: [PATCH 18/26] Add TODO --- 2d_spots_in_fibers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index fff2206..510df1e 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -332,7 +332,7 @@ def convert_labelimage_to_binary(label_imp, scale_binary=True): return binary_imp - +# TODO: test simpler way using IJ roi measure def measure_intensity_sum(label_imp, target_imp): """Measure the sum intensity for each label in a label image on another target image. Uses imglib2 and ops for this and works with both 2D and 3D label images. -- GitLab From e3e5ee7656b30d566039f0d8f0f1737add6f5466 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 10:12:16 +0200 Subject: [PATCH 19/26] Remove unused function and imports --- 2d_spots_in_fibers.py | 58 +++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 510df1e..181b435 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -1,5 +1,6 @@ #@ OpService ops #@ CommandService command +#@ RoiManager rm #@ File (label="select image", description="select your input image", style=file) path_to_image #@ Integer (label="select image file series", description="leave 1 if not needed", value=1, min=1, max=20, stepSize=1, persist=false, style=slider) series_number #@ File (label="Fiber Segmentation", description="select the Fiber Segmentation RoiSet .zip file", style=file) fiber_segmentation_roiset @@ -19,6 +20,8 @@ from ij import IJ from ij.plugin import ImageCalculator from ij.plugin.frame import RoiManager from ij.measure import ResultsTable +from ij.measure import Measurements as M +from ij.plugin.filter import Analyzer # Bio-Formats imports from loci.plugins import BF from loci.common import Region @@ -332,37 +335,50 @@ def convert_labelimage_to_binary(label_imp, scale_binary=True): return binary_imp -# TODO: test simpler way using IJ roi measure -def measure_intensity_sum(label_imp, target_imp): - """Measure the sum intensity for each label in a label image on another target image. - Uses imglib2 and ops for this and works with both 2D and 3D label images. + +def measure_intensity_sum(imp, rm): + """ + Measure theraw integrated intensity for all rois in target imp. Parameters ---------- - label_imp : ImagePlus - the input label image - target_imp : ImagePlus - the target image in which to measure + imp : ImagePlus + The image from which the intensity will be measured. + rm : RoiManager + The ROI Manager containing the regions of interest to be analyzed. Returns ------- - array - label id and corresponding sum intensity [label_id, sum_intensity] + label_id : list of str + A list of labels corresponding to each ROI. + sum_intensity : list of int + A list of summed integrated intensities for each ROI. + + Notes + ----- + The results are stored in a `ij.ResultsTable`, + from which the raw integrated density values are extracted. + + Example + ------- + >>> labels, intensities = measure_intensity_sum(image, roi_manager) """ + + rt_ = ResultsTable() + options = M.INTEGRATED_DENSITY + an = Analyzer (imp, options, rt_) + an.setPrecision(0) + label_id = [] sum_intensity = [] - target_img = ImageJFunctions.wrap(target_imp) # convert ImagePlus to img to use ops/region measurements on it - regions = convert_labelimage_to_imglib2regions(label_imp) - - for region in regions: - label_id.append(region.getLabel() + 1) # region.getlabel() starts counting from 0. So the label with int 1 has the index 0. - input = Regions.sample(region, target_img) # returns an iterableInterval - sum = ops.stats().sum(input).getRealDouble() - sum_intensity.append(sum) - # other measurements see https://forum.image.sc/t/can-i-get-to-measurements-using-imagej-ops/4448/5 - result = [label_id, sum_intensity] + + for index, roi in enumerate(rm.getRoisAsArray()): + imp.setRoi(roi) + an.measure() + label_id.append(roi.getName()) + sum_intensity.append(int(rt_.getColumn("RawIntDen")[index])) - return result + return label_id, sum_intensity def add_results_to_resultstable(results_table, column, values): -- GitLab From b0af87596a1e8f182c347914c8c1e19afeb46d60 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 10:15:16 +0200 Subject: [PATCH 20/26] Remove unused function and imports --- 2d_spots_in_fibers.py | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 181b435..f6ec4ba 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -29,11 +29,6 @@ from loci.plugins.in import ImporterOptions from loci.formats import ImageReader, MetadataTools, Memoizer # MorpholibJ imports from inra.ijpb.binary import BinaryImages -# imglib2 imports -from net.imglib2.img.display.imagej import ImageJFunctions -from net.imglib2.roi import Regions -from net.imglib2.roi.labeling import LabelRegions -from net.imglib2.algorithm.labeling import ConnectedComponents # BIOP imports from ch.epfl.biop.ij2command import Labels2Rois, Rois2Labels # python imports @@ -288,32 +283,6 @@ def save_image_as_IJtif(imp, filename, suffix, target): IJ.saveAs(imp, "Tiff", savepath) -def convert_labelimage_to_imglib2regions(imp): - """Convert a ImagePlus label image to imglib2 regions - - Parameters - ---------- - imp : ImagePlus - the input label image - - Returns - ------- - Imglib2 LabelRegions - the imglib2 label regions - """ - - # fist convert ImagePlus to img as suggested by curtis: - # from https://forum.image.sc/t/how-to-wrap-any-kind-of-imageplus-to-an-imglib2-img-floattype/178/6 - wrapImg = ImageJFunctions.wrap(imp) - # workaround to convert a label image into an imglib2 ImgLabeling (which contains not only an image) - img_labeling = ops.labeling().cca(wrapImg, ConnectedComponents.StructuringElement.EIGHT_CONNECTED) - # img = img_labeling.getIndexImg() # the img label image - # ImageJFunctions.show(img) - regions = LabelRegions(img_labeling) - - return regions - - def convert_labelimage_to_binary(label_imp, scale_binary=True): """Convert a label image to a mask / binary image -- GitLab From 06b45e9a216aaa06f37922d108ed7472d91043eb Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 10:18:03 +0200 Subject: [PATCH 21/26] Adapt to new function --- 2d_spots_in_fibers.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index f6ec4ba..0cca227 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -486,15 +486,15 @@ for index, channel in enumerate(processing_channels): dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_binary, "Multiply create") dapi_negative_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_positive_spots_binary, "Subtract create") - n_spots_per_fiber = measure_intensity_sum(fibers_label_imp, spots_binary_imp) - n_dapi_positive_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_positive_spots_binary) - n_dapi_negative_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_negative_spots_binary) + fiber_label_IDs, n_spots_per_fiber = measure_intensity_sum(fibers_label_imp, spots_binary_imp) + _, n_dapi_positive_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_positive_spots_binary) + _, n_dapi_negative_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_negative_spots_binary) # TODO: the column fiber label ID needs to be added only one time. Maybe check before if column exists. - add_results_to_resultstable(results_table, "fiber label ID", n_spots_per_fiber[0]) - add_results_to_resultstable(results_table, "n spots channel " + str(channel), n_spots_per_fiber[1]) - add_results_to_resultstable(results_table, "n spots dapi positive channel " + str(channel), n_dapi_positive_spots_per_fiber[1]) - add_results_to_resultstable(results_table, "n spots dapi negative channel " + str(channel), n_dapi_negative_spots_per_fiber[1]) + add_results_to_resultstable(results_table, "fiber label ID", fiber_label_IDs) + add_results_to_resultstable(results_table, "n spots channel " + str(channel), n_spots_per_fiber) + add_results_to_resultstable(results_table, "n spots dapi positive channel " + str(channel), n_dapi_positive_spots_per_fiber) + add_results_to_resultstable(results_table, "n spots dapi negative channel " + str(channel), n_dapi_negative_spots_per_fiber) close_images([spots_channel, spots_label_imp, spots_binary_imp, spots_binary_imp, dapi_binary, dapi_positive_spots_binary, dapi_negative_spots_binary]) results_table.show("Spots") -- GitLab From f1bac837022bba94df110cae398bfd95e10678ce Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 10:18:03 +0200 Subject: [PATCH 22/26] Adapt to new function --- 2d_spots_in_fibers.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index f6ec4ba..3924854 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -485,16 +485,17 @@ for index, channel in enumerate(processing_channels): spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_binary, "Multiply create") dapi_negative_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_positive_spots_binary, "Subtract create") - - n_spots_per_fiber = measure_intensity_sum(fibers_label_imp, spots_binary_imp) - n_dapi_positive_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_positive_spots_binary) - n_dapi_negative_spots_per_fiber = measure_intensity_sum(fibers_label_imp, dapi_negative_spots_binary) + + load_rois_from_zip(fiber_segmentation_roiset) + fiber_label_IDs, n_spots_per_fiber = measure_intensity_sum(spots_binary_imp, rm) + _, n_dapi_positive_spots_per_fiber = measure_intensity_sum(dapi_positive_spots_binary, rm) + _, n_dapi_negative_spots_per_fiber = measure_intensity_sum(dapi_negative_spots_binary, rm) # TODO: the column fiber label ID needs to be added only one time. Maybe check before if column exists. - add_results_to_resultstable(results_table, "fiber label ID", n_spots_per_fiber[0]) - add_results_to_resultstable(results_table, "n spots channel " + str(channel), n_spots_per_fiber[1]) - add_results_to_resultstable(results_table, "n spots dapi positive channel " + str(channel), n_dapi_positive_spots_per_fiber[1]) - add_results_to_resultstable(results_table, "n spots dapi negative channel " + str(channel), n_dapi_negative_spots_per_fiber[1]) + add_results_to_resultstable(results_table, "fiber label ID", fiber_label_IDs) + add_results_to_resultstable(results_table, "n spots channel " + str(channel), n_spots_per_fiber) + add_results_to_resultstable(results_table, "n spots dapi positive channel " + str(channel), n_dapi_positive_spots_per_fiber) + add_results_to_resultstable(results_table, "n spots dapi negative channel " + str(channel), n_dapi_negative_spots_per_fiber) close_images([spots_channel, spots_label_imp, spots_binary_imp, spots_binary_imp, dapi_binary, dapi_positive_spots_binary, dapi_negative_spots_binary]) results_table.show("Spots") -- GitLab From d34dc6c34cd1314d43d0d965045cba0d5844e6cc Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 11:54:35 +0200 Subject: [PATCH 23/26] Remove unused function, its execution and associated import --- 2d_spots_in_fibers.py | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 3924854..22b3a45 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -30,7 +30,7 @@ from loci.formats import ImageReader, MetadataTools, Memoizer # MorpholibJ imports from inra.ijpb.binary import BinaryImages # BIOP imports -from ch.epfl.biop.ij2command import Labels2Rois, Rois2Labels +from ch.epfl.biop.ij2command import Labels2Rois # python imports import os @@ -330,7 +330,7 @@ def measure_intensity_sum(imp, rm): Example ------- - >>> labels, intensities = measure_intensity_sum(image, roi_manager) + >>> labels, intensities = measure_intensity_sum(imp, roi_manager) """ rt_ = ResultsTable() @@ -428,17 +428,6 @@ def load_rois_from_zip(path): rm.runCommand("Open", path) -def convert_rois_to_labelimage(imp): - rm = RoiManager.getInstance() - if not rm: - rm = RoiManager() - - label_imp = command.run( Rois2Labels , False , 'imp' , imp , 'rm', rm).get().getOutput("label_imp") - rm.reset() # TODO: should be optional but can be default - - return label_imp - - def close_images(list_of_imps): """Close given ImagePlus images @@ -468,11 +457,6 @@ if threshold <= 0: threshold = get_threshold_from_method(dapi_channel, "otsu") dapi_binary = convert_to_binary(dapi_channel, threshold) -# Get the fiber segmentation from ij roizip and convert to labelimage -load_rois_from_zip(fiber_segmentation_roiset) -fibers_label_imp = convert_rois_to_labelimage(dapi_channel) -dapi_channel.close() - # detect spots and count them per fiber results_table = ResultsTable() for index, channel in enumerate(processing_channels): @@ -480,7 +464,7 @@ for index, channel in enumerate(processing_channels): quality_threshold = float(quality_thresholds[index]) spots_channel = BFopen_image(path_to_image, channel, series_number) spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, spot_diameter, quality_threshold) - save_image_as_IJtif(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) + # save_image_as_IJtif(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) save_labelimage_as_ijroiset(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_binary, "Multiply create") -- GitLab From 2c6afe8e3b650fa6903587504ae828841bc3b99b Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 13:32:55 +0200 Subject: [PATCH 24/26] Remove RoiManager import, get from script parameter instead. --- 2d_spots_in_fibers.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 22b3a45..acf308f 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -18,7 +18,6 @@ from fiji.plugin.trackmate.action import LabelImgExporter # ij imports from ij import IJ from ij.plugin import ImageCalculator -from ij.plugin.frame import RoiManager from ij.measure import ResultsTable from ij.measure import Measurements as M from ij.plugin.filter import Analyzer @@ -386,7 +385,7 @@ def save_results_table(results_table, filename, suffix, target): results_table.save(savepath) -def save_labelimage_as_ijroiset(label_imp, filename, suffix, target): +def save_labelimage_as_ijroiset(label_imp, rm, filename, suffix, target): """Save all labels i a label Images as IJ-ROIset to target location. Only 2D, requires PTBIOP update site. @@ -401,10 +400,6 @@ def save_labelimage_as_ijroiset(label_imp, filename, suffix, target): target : str the target directory in which to save the table """ - rm = RoiManager.getInstance() - if not rm: - rm = RoiManager() - rm.reset() command.run( Labels2Rois , False , 'imp' , label_imp , 'rm', rm).get() savename = filename + "_" + str(suffix) + ".zip" savepath = os.path.join(target, savename) @@ -412,19 +407,16 @@ def save_labelimage_as_ijroiset(label_imp, filename, suffix, target): rm.reset() -def load_rois_from_zip(path): +def load_rois_from_zip(path, rm): """Load ROIs from the given zip file and add them to the RoiManager. Parameters ---------- path : string Path to the ROI zip file. + rm : RoiManager + The ROI Manager. """ - rm = RoiManager.getInstance() - if not rm: - rm = RoiManager() - rm.reset() - rm.runCommand("Open", path) -- GitLab From 1725ac7ff988c22b15dea5587afc6c222f76a795 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 13:37:31 +0200 Subject: [PATCH 25/26] Remove unused line --- 2d_spots_in_fibers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index acf308f..34332f1 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -456,7 +456,6 @@ for index, channel in enumerate(processing_channels): quality_threshold = float(quality_thresholds[index]) spots_channel = BFopen_image(path_to_image, channel, series_number) spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, spot_diameter, quality_threshold) - # save_image_as_IJtif(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) save_labelimage_as_ijroiset(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_binary, "Multiply create") -- GitLab From 5147222ec3466788869a32d38d066a417f6ac7a5 Mon Sep 17 00:00:00 2001 From: schlda00 <kai.schleicher@unibas.ch> Date: Wed, 16 Apr 2025 14:07:59 +0200 Subject: [PATCH 26/26] Pass RoiManager to functions --- 2d_spots_in_fibers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/2d_spots_in_fibers.py b/2d_spots_in_fibers.py index 34332f1..4e0a816 100644 --- a/2d_spots_in_fibers.py +++ b/2d_spots_in_fibers.py @@ -456,12 +456,12 @@ for index, channel in enumerate(processing_channels): quality_threshold = float(quality_thresholds[index]) spots_channel = BFopen_image(path_to_image, channel, series_number) spots_label_imp = run_trackmate_dog_spot_detector(spots_channel, spot_diameter, quality_threshold) - save_labelimage_as_ijroiset(spots_label_imp, filename, "spots_ch" + str(channel), parent_dir) + save_labelimage_as_ijroiset(spots_label_imp, rm, filename, "spots_ch" + str(channel), parent_dir) spots_binary_imp = convert_labelimage_to_binary(spots_label_imp) dapi_positive_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_binary, "Multiply create") dapi_negative_spots_binary = ImageCalculator.run(spots_binary_imp, dapi_positive_spots_binary, "Subtract create") - load_rois_from_zip(fiber_segmentation_roiset) + load_rois_from_zip(fiber_segmentation_roiset, rm) fiber_label_IDs, n_spots_per_fiber = measure_intensity_sum(spots_binary_imp, rm) _, n_dapi_positive_spots_per_fiber = measure_intensity_sum(dapi_positive_spots_binary, rm) _, n_dapi_negative_spots_per_fiber = measure_intensity_sum(dapi_negative_spots_binary, rm) -- GitLab