From 2ef8b8ccca30969df2239c72a57e409a691e7435 Mon Sep 17 00:00:00 2001 From: Laurent Guerard <laurent.guerard@unibas.ch> Date: Wed, 12 Feb 2025 13:53:12 +0100 Subject: [PATCH] Add compatibility with container files and formatting --- 1_identify_fibers.py | 301 ++++++++++++++++++++++++++++++++----------- 1 file changed, 224 insertions(+), 77 deletions(-) diff --git a/1_identify_fibers.py b/1_identify_fibers.py index 186b492..ffb0f5f 100755 --- a/1_identify_fibers.py +++ b/1_identify_fibers.py @@ -67,6 +67,7 @@ from ij.plugin import Duplicator, ImageCalculator, RoiEnlarger from imcflibs import pathtools from imcflibs.imagej import bioformats as bf from imcflibs.imagej import misc +from loci.formats import ImageReader, MetadataTools # ─── Functions ──────────────────────────────────────────────────────────────── @@ -625,6 +626,62 @@ def setup_defined_ij(rm, rt): IJ.log("\\Clear") +def get_series_info_from_ome_metadata(path_to_file, skip_labels=False): + """Get the Bio-Formates series count from a file on disk. + + Useful to access a specific image in a container format like .czi, .nd2, .lif... + + Parameters + ---------- + path_to_file : str + The full path to the image file. + + Returns + ------- + int + The number of Bio-Formats series detected in the image file metadata. + """ + + if not skip_labels: + reader = ImageReader() + reader.setFlattenedResolutions(False) + ome_meta = MetadataTools.createOMEXMLMetadata() + reader.setMetadataStore(ome_meta) + reader.setId(path_to_file) + series_count = reader.getSeriesCount() + + reader.close() + return series_count, range(series_count) + + else: + reader = ImageReader() + # reader.setFlattenedResolutions(True) + ome_meta = MetadataTools.createOMEXMLMetadata() + reader.setMetadataStore(ome_meta) + reader.setId(path_to_file) + series_count = reader.getSeriesCount() + + series_ids = [] + series_names = [] + x = 0 + y = 0 + for i in range(series_count): + reader.setSeries(i) + + if reader.getSizeX() > x and reader.getSizeY() > y: + name = ome_meta.getImageName(i) + + if name not in ["label image", "macro image"]: + series_ids.append(i) + series_names.append(name) + + x = reader.getSizeX() + y = reader.getSizeY() + + print(series_names) + return len(series_ids), series_ids + + # ─── Main Code ──────────────────────────────────────────────────────────────── if __name__ == "__main__": @@ -643,21 +700,69 @@ if __name__ == "__main__": # open image using Bio-Formats file_info = pathtools.parse_path(file) misc.progressbar(index + 1, len(file_list), 1, "Opening : ") - raw = bf.import_image(file_info["full"])[0] - - # get image info - raw_image_calibration = raw.getCalibration() - raw_image_title = fix_BF_czi_imagetitle(raw) - print("raw image title: ", str(raw_image_title)) - # take care of paths and directories - output_dir = os.path.join( - out_dir_info["full"], str(raw_image_title), "1_identify_fibers" + _, series_index = get_series_info_from_ome_metadata( + file_info["full"], skip_labels=True ) - print("output_dir: ", str(output_dir)) + print(series_index) + for list_index, serie_index in enumerate(series_index): + misc.progressbar(list_index + 1, len(series_index), 2, "Opening serie : ") + raw = bf.import_image(file_info["full"], series_number=serie_index)[0] + + # get image info + raw_image_calibration = raw.getCalibration() + raw_image_title = fix_BF_czi_imagetitle(raw) + print("raw image title: ", str(raw_image_title)) + + # take care of paths and directories + output_dir = os.path.join( + out_dir_info["full"], str(raw_image_title), "1_identify_fibers" + ) + print("output_dir: ", str(output_dir)) + + if not os.path.exists(str(output_dir)): + os.makedirs(str(output_dir)) + + # update the log for the user + misc.timed_log("Now working on " + str(raw_image_title)) + if raw_image_calibration.scaled() is False: + IJ.log( + "Your image is not spatially calibrated! Size measurements are only possible in [px]." + ) + # Only print settings once for the first file and first series + if index == 0 and serie_index == 0: + IJ.log(" -- settings used -- ") + IJ.log("area = " + str(minAr) + "-" + str(maxAr)) + IJ.log("perimeter = " + str(minPer) + "-" + str(maxPer)) + IJ.log("circularity = " + str(minCir) + "-" + str(maxCir)) + IJ.log("ROI expansion [microns] = " + str(enlarge_radius)) + IJ.log("Membrane channel = " + str(membrane_channel)) + IJ.log("MHC positive fiber channel = " + str(fiber_channel)) + # IJ.log("sub-tiling = " + str(tiling_factor)) + IJ.log(" -- settings used -- ") + + # image (pre)processing and segmentation (-> ROIs)# imp, firstC, lastC, firstZ, + # lastZ, firstT, lastT + membrane = Duplicator().run( + raw, membrane_channel, membrane_channel, 1, 1, 1, 1 + ) if not os.path.exists(str(output_dir)): os.makedirs(str(output_dir)) + imp_bgd_corrected = do_background_correction(membrane) + IJ.run("Conversions...", "scale") + IJ.run(imp_bgd_corrected, "16-bit", "") + + imp_result = run_tm( + imp_bgd_corrected, + 1, + cellpose_dir.getPath(), + PretrainedModel.CYTO2, + 30.0, + area_thresh=[minAr, maxAr], + circularity_thresh=[minCir, maxCir], + perimeter_thresh=[minPer, maxPer], + ) # update the log for the user misc.timed_log("Now working on " + str(raw_image_title)) @@ -700,84 +805,126 @@ if __name__ == "__main__": os.path.join(output_dir, raw_image_title + "_all_fibers_binary"), ) - command.run(Labels2CompositeRois, True, "rm", rm, "imp", imp_result).get() + IJ.saveAs( + imp_result, + "Tiff", + os.path.join( + output_dir, + raw_image_title + "_" + str(serie_index) + "_all_fibers_binary", + ), + ) - enlarge_all_rois(enlarge_radius, rm, raw_image_calibration.pixelWidth) - renumber_rois(rm) - save_all_rois( - rm, os.path.join(output_dir, raw_image_title + "_all_fiber_rois.zip") - ) + command.run(Labels2CompositeRois, True, "rm", rm, "imp", imp_result).get() - # check for positive fibers - if fiber_channel > 0: - if min_fiber_intensity == 0: - min_fiber_intensity = get_threshold_from_method( - raw, fiber_channel, "Mean" - )[0] - IJ.log("automatic intensity threshold detection: True") - - IJ.log("fiber intensity threshold: " + str(min_fiber_intensity)) - change_all_roi_color(rm, "blue") - positive_fibers = select_positive_fibers( - raw, fiber_channel, rm, min_fiber_intensity - ) - change_subset_roi_color(rm, positive_fibers, "magenta") - save_selected_rois( + enlarge_all_rois(enlarge_radius, rm, raw_image_calibration.pixelWidth) + renumber_rois(rm) + save_all_rois( rm, - positive_fibers, os.path.join( - output_dir, raw_image_title + "_mhc_positive_fiber_rois.zip" + output_dir, + raw_image_title + "_" + str(serie_index) + "_all_fiber_rois.zip", ), ) - # measure size & shape, save - IJ.run( - "Set Measurements...", - "area perimeter shape feret's redirect=None decimal=4", - ) - IJ.run("Clear Results", "") - measure_in_all_rois(raw, membrane_channel, rm) - - rt = ResultsTable.getResultsTable("Results") + # check for positive fibers + if fiber_channel > 0: + if min_fiber_intensity == 0: + min_fiber_intensity = get_threshold_from_method( + raw, fiber_channel, "Mean" + )[0] + IJ.log("automatic intensity threshold detection: True") + + IJ.log("fiber intensity threshold: " + str(min_fiber_intensity)) + change_all_roi_color(rm, "blue") + positive_fibers = select_positive_fibers( + raw, fiber_channel, rm, min_fiber_intensity + ) + change_subset_roi_color(rm, positive_fibers, "magenta") + save_selected_rois( + rm, + positive_fibers, + os.path.join( + output_dir, + raw_image_title + + "_" + + str(serie_index) + + "_mhc_positive_fiber_rois.zip", + ), + ) + + # measure size & shape, save + IJ.run( + "Set Measurements...", + "area perimeter shape feret's redirect=None decimal=4", + ) + IJ.run("Clear Results", "") + measure_in_all_rois(raw, membrane_channel, rm) - # print(rt.size()) + rt = ResultsTable.getResultsTable("Results") - if fiber_channel > 0: - # print(rt.size()) - preset_results_column(rt, "MHC Positive Fibers (magenta)", "NO") - # print(rt.size()) - add_results(rt, "MHC Positive Fibers (magenta)", positive_fibers, "YES") # print(rt.size()) - rt.save(os.path.join(output_dir, raw_image_title + "_all_fibers_results.csv")) - # print("saved the all_fibers_results.csv") - # dress up the original image, save a overlay-png, present original to the user - rm.show() - raw.show() - 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 - qc_duplicate = raw.duplicate() - IJ.saveAs( - qc_duplicate, "PNG", output_dir + "/" + raw_image_title + "_all_fibers" - ) - qc_duplicate.close() - wm.toFront(raw.getWindow()) - IJ.run("Remove Overlay", "") - raw.setDisplayMode(IJ.GRAYSCALE) - show_all_rois_on_image(rm, raw) - - IJ.selectWindow("Log") - IJ.saveAs("Text", str(output_dir + "/" + raw_image_title + "_all_fibers_Log")) - membrane.close() - imp_bgd_corrected.close() - imp_result.close() - - if close_raw == True: - raw.close() + if fiber_channel > 0: + # print(rt.size()) + preset_results_column(rt, "MHC Positive Fibers (magenta)", "NO") + # print(rt.size()) + add_results(rt, "MHC Positive Fibers (magenta)", positive_fibers, "YES") + # print(rt.size()) + + rt.save( + os.path.join( + output_dir, + raw_image_title + + "_" + + str(serie_index) + + "_all_fibers_results.csv", + ) + ) + # print("saved the all_fibers_results.csv") + # dress up the original image, save a overlay-png, present original to the user + rm.show() + raw.show() + 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 + qc_duplicate = raw.duplicate() + IJ.saveAs( + qc_duplicate, + "PNG", + output_dir + + "/" + + raw_image_title + + "_" + + str(serie_index) + + "_all_fibers", + ) + qc_duplicate.close() + wm.toFront(raw.getWindow()) + IJ.run("Remove Overlay", "") + raw.setDisplayMode(IJ.GRAYSCALE) + show_all_rois_on_image(rm, raw) + + IJ.selectWindow("Log") + IJ.saveAs( + "Text", + str( + output_dir + + "/" + + raw_image_title + + "_" + + str(serie_index) + + "_all_fibers_Log" + ), + ) + membrane.close() + imp_bgd_corrected.close() + imp_result.close() + + if close_raw == True: + raw.close() total_execution_time_min = (time.time() - execution_start_time) / 60.0 IJ.log("total time in minutes: " + str(total_execution_time_min)) -- GitLab