# HG changeset patch # User lldelisle # Date 1708000089 0 # Node ID 45375dc5dd8dcd5f62e2e5e69b70abc266edec4d # Parent bd5771ff6aa3fc103749b1070de69666e4a677fb planemo upload for repository https://github.com/lldelisle/tools-lldelisle/tree/master/tools/omero_hyperstack_to_gastruloid_measurements commit 5e181f51312e53a72d354584439fa9359cd16723-dirty diff -r bd5771ff6aa3 -r 45375dc5dd8d 1-omero_timelapse_image_to_measurements_phase.groovy --- a/1-omero_timelapse_image_to_measurements_phase.groovy Wed Dec 20 20:07:13 2023 +0000 +++ b/1-omero_timelapse_image_to_measurements_phase.groovy Thu Feb 15 12:28:09 2024 +0000 @@ -5,11 +5,11 @@ // merge the analysis script with templates available at // https://github.com/BIOP/OMERO-scripts/tree/025047955b5c1265e1a93b259c1de4600d00f107/Fiji -// Last modification: 2023-12-20 +// Last modification: 2024-02-14 /* * = COPYRIGHT = - * © All rights reserved. ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE, Switzerland, BioImaging And Optics Platform (BIOP), 2023 + * © All rights reserved. ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE, Switzerland, BioImaging And Optics Platform (BIOP), 2024 * * Licensed under the BSD-3-Clause License: * Redistribution and use in source and binary forms, with or without modification, are permitted provided @@ -45,19 +45,22 @@ // The option 'rescue' allows to only process images without ROIs and // tables and generate the final table -// Without this option, the job will fail if a ROI or a table exists +// The option 'replace_at_runtime' allows to remove tables at start +// and ROIs on each image just before processing. -// If a final table exists it will fail in both modes +// The option 'use_existing' allows to +// Recompute only spine -// The option use_existing allows to -// Recompute only spine +// Without 'use_existing' or 'replace_at_runtime', the job will fail if a final table exists + +// Without 'use_existing' or 'replace_at_runtime' or 'rescue', the job will fail if ROI exists // This macro works both in headless // or GUI // In both modes, -// The result table and the result ROI are sent to omero -// The measures are: Area,Perim.,Circ.,Feret,FeretX,FeretY,FeretAngle,MinFeret,AR,Round,Solidity,Unit,Date,Version,IlastikProject,ProbabilityThreshold,MinSizeParticle,MinDiameter,ClosenessTolerance,MinSimilarity,RadiusMedian,BaseImage,ROI,Time,ROI_type,XCentroid,YCentroid,LargestRadius,SpineLength,ElongationIndex[,Date_rerun_spine,Version_rerun_spine] +// The result table and the result ROIs are sent to omero +// The measures are: Area,Perim.,Circ.,Feret,FeretX,FeretY,FeretAngle,MinFeret,AR,Round,Solidity,Unit,Date,Version,IlastikProject,ProbabilityThreshold,ThresholdingMethod,OptionsDo,OptionsIterations,OptionsCount,FillHolesBefore,RadiusMedian,MinSizeParticle,MinDiameter,ClosenessTolerance,MinSimilarity,BaseImage,ROI,Time,ROI_type,XCentroid,YCentroid,LargestRadius,SpineLength,ElongationIndex[,Date_rerun_spine,Version_rerun_spine] // LargestRadius and SpineLength are set to 0 if no circle was found. // ElongationIndex is set to 0 if a gastruloid was found and to -1 if no gastruloid was found. @@ -397,26 +400,39 @@ File ilastik_project, String ilastik_project_type, Integer ilastik_label_OI, Double probability_threshold, Double radius_median, - Integer min_size_particle, Boolean get_spine, - Integer minimum_diameter, Integer closeness_tolerance, Double min_similarity, + Double min_size_particle, Boolean get_spine, + Double minimum_diameter_um, Double closeness_tolerance_um, Double min_similarity, String ilastik_project_short_name, File output_directory, Boolean headless_mode, Boolean debug, String tool_version, Boolean use_existing, String final_object, Boolean rescue, Integer ilastik_label_BG, Double probability_threshold_BG, - Boolean keep_only_largest, String segmentation_method) { + Boolean keep_only_largest, String segmentation_method, + Boolean replace_at_runtime, + String thresholding_method, String options_do, + Integer options_iteration, Integer options_count, + Boolean fill_holes_before_median) { robustlyGetAll(dataset_wrp, "image", user_client).each{ ImageWrapper img_wrp -> + if (replace_at_runtime) { + List rois = robustlyGetROIs(image_wrp, user_client) + if (!rois.isEmpty()) { + robustlyDeleteROIs(image_wrp, user_client, rois) + } + } processImage(user_client, img_wrp, ilastik_project, ilastik_project_type, ilastik_label_OI, probability_threshold, radius_median, min_size_particle, get_spine, - minimum_diameter, closeness_tolerance, min_similarity, + minimum_diameter_um, closeness_tolerance_um, min_similarity, ilastik_project_short_name, output_directory, headless_mode, debug, tool_version, use_existing, final_object, rescue, ilastik_label_BG, probability_threshold_BG, - keep_only_largest, segmentation_method) + keep_only_largest, segmentation_method, + thresholding_method, options_do, + options_iteration, options_count, + fill_holes_before_median) } } @@ -424,26 +440,34 @@ File ilastik_project, String ilastik_project_type, Integer ilastik_label_OI, Double probability_threshold, Double radius_median, - Integer min_size_particle, Boolean get_spine, - Integer minimum_diameter, Integer closeness_tolerance, Double min_similarity, + Double min_size_particle, Boolean get_spine, + Double minimum_diameter_um, Double closeness_tolerance_um, Double min_similarity, String ilastik_project_short_name, File output_directory, Boolean headless_mode, Boolean debug, String tool_version, Boolean use_existing, String final_object, Boolean rescue, Integer ilastik_label_BG, Double probability_threshold_BG, - Boolean keep_only_largest, String segmentation_method) { + Boolean keep_only_largest, String segmentation_method, + Boolean replace_at_runtime, + String thresholding_method, String options_do, + Integer options_iteration, Integer options_count, + Boolean fill_holes_before_median) { robustlyGetAll(plate_wrp, "well", user_client).each{ well_wrp -> processSingleWell(user_client, well_wrp, ilastik_project, ilastik_project_type, ilastik_label_OI, probability_threshold, radius_median, min_size_particle, get_spine, - minimum_diameter, closeness_tolerance, min_similarity, + minimum_diameter_um, closeness_tolerance_um, min_similarity, ilastik_project_short_name, output_directory, headless_mode, debug, tool_version, use_existing, final_object, rescue, ilastik_label_BG, probability_threshold_BG, - keep_only_largest, segmentation_method) + keep_only_largest, segmentation_method, + replace_at_runtime, + thresholding_method, options_do, + options_iteration, options_count, + fill_holes_before_median) } } @@ -451,41 +475,58 @@ File ilastik_project, String ilastik_project_type, Integer ilastik_label_OI, Double probability_threshold, Double radius_median, - Integer min_size_particle, Boolean get_spine, - Integer minimum_diameter, Integer closeness_tolerance, Double min_similarity, + Double min_size_particle, Boolean get_spine, + Double minimum_diameter_um, Double closeness_tolerance_um, Double min_similarity, String ilastik_project_short_name, File output_directory, Boolean headless_mode, Boolean debug, String tool_version, Boolean use_existing, String final_object, Boolean rescue, Integer ilastik_label_BG, Double probability_threshold_BG, - Boolean keep_only_largest, String segmentation_method) { + Boolean keep_only_largest, String segmentation_method, + Boolean replace_at_runtime, + String thresholding_method, String options_do, + Integer options_iteration, Integer options_count, + Boolean fill_holes_before_median) { well_wrp.getWellSamples().each{ - processImage(user_client, it.getImage(), + ImageWrapper img_wrp = it.getImage() + if (replace_at_runtime) { + List rois = robustlyGetROIs(img_wrp, user_client) + if (!rois.isEmpty()) { + robustlyDeleteROIs(img_wrp, user_client, rois) + } + } + processImage(user_client, img_wrp, ilastik_project, ilastik_project_type, ilastik_label_OI, probability_threshold, radius_median, min_size_particle, get_spine, - minimum_diameter, closeness_tolerance, min_similarity, + minimum_diameter_um, closeness_tolerance_um, min_similarity, ilastik_project_short_name, output_directory, headless_mode, debug, tool_version, use_existing, final_object, rescue, ilastik_label_BG, probability_threshold_BG, - keep_only_largest, segmentation_method) + keep_only_largest, segmentation_method, + thresholding_method, options_do, + options_iteration, options_count, + fill_holes_before_median) } } def processImage(Client user_client, ImageWrapper image_wrp, File ilastik_project, String ilastik_project_type, // String ilastik_strategy, Integer ilastik_label_OI, - Double probability_threshold, Double radius_median, Integer min_size_particle, + Double probability_threshold, Double radius_median, Double min_size_particle, Boolean get_spine, - Integer minimum_diameter, Integer closeness_tolerance, Double min_similarity, + Double minimum_diameter_um, Double closeness_tolerance_um, Double min_similarity, String ilastik_project_short_name, File output_directory, Boolean headless_mode, Boolean debug, String tool_version, Boolean use_existing, String final_object, Boolean rescue, Integer ilastik_label_BG, Double probability_threshold_BG, - Boolean keep_only_largest, String segmentation_method) { + Boolean keep_only_largest, String segmentation_method, + String thresholding_method, String options_do, + Integer options_iteration, Integer options_count, + Boolean fill_holes_before_median) { IJ.run("Close All", "") IJ.run("Clear Results") @@ -584,7 +625,7 @@ use_existing = false rescue = false rois = robustlyGetROIs(image_wrp, user_client) - if (rois.size() > 0) { + if (!rois.isEmpty()) { // Clean existing ROIs robustlyDeleteROIs(image_wrp, user_client, rois) } @@ -693,17 +734,19 @@ // Get only the channel with bright field mask_imp = new Duplicator().run(imp, ilastik_input_ch, ilastik_input_ch, 1, 1, 1, nT); // Run convert to mask - (new Thresholder()).convertStackToBinary(mask_imp); + Thresholder my_thresholder = new Thresholder() + my_thresholder.setMethod(thresholding_method) + my_thresholder.setBackground("Light") + Prefs.blackBackground = true + my_thresholder.convertStackToBinary(mask_imp) } // This title will appear in the result table mask_imp.setTitle(image_basename) if (!headless_mode) { mask_imp.show() } - - // clean the mask a bit - // Before we were doing: - // IJ.run(mask_ilastik_imp, "Options...", "iterations=10 count=3 black do=Open") - // Now: - // (Romain proposed 5 as radius_median) + IJ.run(mask_imp, "Options...", "iterations=" + options_iteration + " count=" + options_count + " black do=" + options_do + " stack") + if (fill_holes_before_median) { + IJ.run(mask_imp, "Fill Holes", "stack") + } println "Smoothing mask" // Here I need to check if we first fill holes or first do the median @@ -714,7 +757,8 @@ // find gastruloids and measure them IJ.run("Set Measurements...", "area feret's perimeter shape display redirect=None decimal=3") - IJ.run("Set Scale...", "distance=1 known=" + scale + " unit=micron") + + IJ.run(mask_imp, "Set Scale...", "distance=1 known=" + scale + " unit=micron") pixelWidth = mask_imp.getCalibration().pixelWidth println "pixelWidth is " + pixelWidth // Exclude the edge @@ -732,6 +776,13 @@ for (int t=1;t<=nT;t++) { // Don't ask me why we need to refer to Z pos and not T/Frame ArrayList all_rois_inT = ov.findAll{ roi -> roi.getZPosition() == t} + // When there is a single time the ROI has ZPosition to 0: + if (nT == 1) { + all_rois_inT = (ov as List) + if (all_rois_inT == null) { + all_rois_inT = [] + } + } println "There are " + all_rois_inT.size() + " in time " + t if (all_rois_inT.size() > 0) { largest_roi_inT = Collections.max(all_rois_inT, Comparator.comparing((roi) -> roi.getStatistics().area )) @@ -744,6 +795,9 @@ // Update the position before adding to the clean_overlay largest_roi_inT.setPosition( ilastik_input_ch, 1, t) clean_overlay.add(largest_roi_inT) + if (!headless_mode) { + rm.addRoi(largest_roi_inT) + } } } else { // We keep all @@ -752,6 +806,10 @@ ov.each{ Roi roi -> // Don't ask me why we need to refer to Z pos and not T/Frame t = roi.getZPosition() + // When there is a single time the ROI has ZPosition to 0: + if (nT == 1) { + t = 1 + } id = lastID[t - 1] + 1 roi.setName("Gastruloid_t" + t + "_id" + id) // Increase lastID: @@ -759,6 +817,9 @@ // Update the position before adding to the clean_overlay roi.setPosition( ilastik_input_ch, 1, t) clean_overlay.add(roi) + if (!headless_mode) { + rm.addRoi(roi) + } } // Fill timepoints with no ROI with notfound: Roi roi @@ -789,15 +850,21 @@ if (segmentation_method == "ilastik") { rt.setValue("IlastikProject", row, ilastik_project_short_name) rt.setValue("ProbabilityThreshold", row, probability_threshold) + rt.setValue("ThresholdingMethod", row, "NA") } else { rt.setValue("IlastikProject", row, "NA") rt.setValue("ProbabilityThreshold", row, "NA") + rt.setValue("ThresholdingMethod", row, thresholding_method) } + rt.setValue("OptionsDo", row, options_do) + rt.setValue("OptionsIterations", row, options_iteration) + rt.setValue("OptionsCount", row, options_count) + rt.setValue("FillHolesBefore", row, "" + fill_holes_before_median) + rt.setValue("RadiusMedian", row, radius_median) rt.setValue("MinSizeParticle", row, min_size_particle) - rt.setValue("MinDiameter", row, minimum_diameter) - rt.setValue("ClosenessTolerance", row, closeness_tolerance) + rt.setValue("MinDiameter", row, minimum_diameter_um) + rt.setValue("ClosenessTolerance", row, closeness_tolerance_um) rt.setValue("MinSimilarity", row, min_similarity) - rt.setValue("RadiusMedian", row, radius_median) String label = rt.getLabel(row) rt.setValue("BaseImage", row, label.split(":")[0]) rt.setValue("ROI", row, label.split(":")[1]) @@ -852,8 +919,8 @@ for ( int row = 0;row tables = robustlyGetTables(image_wrp, user_client) if (!tables.isEmpty()) { - throw new Exception("There should be no table associated to the image before segmentation. Please clean the image.") + if (replace_at_runtime) { + robustlyDeleteTables(image_wrp, user_client) + } else { + throw new Exception("There should be no table associated to the image before segmentation. Please clean the image.") + } } if (!rescue) { List rois = robustlyGetROIs(image_wrp, user_client) if (!rois.isEmpty()) { - throw new Exception("There should be no ROIs associated to the image before segmentation. Please clean the image.") + if (replace_at_runtime) { + robustlyDeleteROIs(image_wrp, user_client, rois) + } else { + throw new Exception("There should be no ROIs associated to the image before segmentation. Please clean the image.") + } } } } @@ -1217,17 +1305,20 @@ ilastik_project, ilastik_project_type, ilastik_label_OI, probability_threshold, radius_median, min_size_particle, - get_spine, minimum_diameter, closeness_tolerance, min_similarity, + get_spine, minimum_diameter_um, closeness_tolerance_um, min_similarity, ilastik_project_short_name, output_directory, headless_mode, debug, tool_version, use_existing, "image", rescue, ilastik_label_BG, probability_threshold_BG, - keep_only_largest, segmentation_method) + keep_only_largest, segmentation_method, + thresholding_method, options_do, + options_iteration, options_count, + fill_holes_before_median) break case "dataset": DatasetWrapper dataset_wrp = robustlyGetOne(id, "dataset", user_client) - if (use_existing) { + if (use_existing || replace_at_runtime) { // Remove the tables associated to the dataset robustlyDeleteTables(dataset_wrp, user_client) } else if (rescue) { @@ -1244,20 +1335,24 @@ ilastik_project, ilastik_project_type, ilastik_label_OI, probability_threshold, radius_median, min_size_particle, - get_spine, minimum_diameter, closeness_tolerance, min_similarity, + get_spine, minimum_diameter_um, closeness_tolerance_um, min_similarity, ilastik_project_short_name, output_directory, headless_mode, debug, tool_version, use_existing, "dataset", rescue, ilastik_label_BG, probability_threshold_BG, - keep_only_largest, segmentation_method) + keep_only_largest, segmentation_method, + replace_at_runtime, + thresholding_method, options_do, + options_iteration, options_count, + fill_holes_before_median) // upload the table on OMERO super_table.setName(table_name + "_global") robustlyAddAndReplaceTable(dataset_wrp, user_client, super_table) break case "well": WellWrapper well_wrp = robustlyGetOne(id, "well", user_client) - if (use_existing) { + if (use_existing || replace_at_runtime) { // Remove the tables associated to the well robustlyDeleteTables(well_wrp, user_client) } else if (rescue) { @@ -1274,20 +1369,24 @@ ilastik_project, ilastik_project_type, ilastik_label_OI, probability_threshold, radius_median, min_size_particle, - get_spine, minimum_diameter, closeness_tolerance, min_similarity, + get_spine, minimum_diameter_um, closeness_tolerance_um, min_similarity, ilastik_project_short_name, output_directory, headless_mode, debug, tool_version, use_existing, "well", rescue, ilastik_label_BG, probability_threshold_BG, - keep_only_largest, segmentation_method) + keep_only_largest, segmentation_method, + replace_at_runtime, + thresholding_method, options_do, + options_iteration, options_count, + fill_holes_before_median) // upload the table on OMERO super_table.setName(table_name + "_global") robustlyAddAndReplaceTable(well_wrp, user_client, super_table) break case "plate": PlateWrapper plate_wrp = robustlyGetOne(id, "plate", user_client) - if (use_existing) { + if (use_existing || replace_at_runtime) { // Remove the tables associated to the plate robustlyDeleteTables(plate_wrp, user_client) } else if (rescue) { @@ -1304,13 +1403,17 @@ ilastik_project, ilastik_project_type, ilastik_label_OI, probability_threshold, radius_median, min_size_particle, - get_spine, minimum_diameter, closeness_tolerance, min_similarity, + get_spine, minimum_diameter_um, closeness_tolerance_um, min_similarity, ilastik_project_short_name, output_directory, headless_mode, debug, tool_version, use_existing, "plate", rescue, ilastik_label_BG, probability_threshold_BG, - keep_only_largest, segmentation_method) + keep_only_largest, segmentation_method, + replace_at_runtime, + thresholding_method, options_do, + options_iteration, options_count, + fill_holes_before_median) // upload the table on OMERO super_table.setName(table_name + "_global") robustlyAddAndReplaceTable(plate_wrp, user_client, super_table) diff -r bd5771ff6aa3 -r 45375dc5dd8d README.md --- a/README.md Wed Dec 20 20:07:13 2023 +0000 +++ b/README.md Thu Feb 15 12:28:09 2024 +0000 @@ -1,7 +1,44 @@ # OMERO hyperstack to gastruloid measurements +## Set up user credentials on Galaxy to connect to other omero instance + +To enable users to set their credentials for this tool, +make sure the file `config/user_preferences_extra.yml` has the following section: + +``` + omero_account: + description: Your OMERO instance connection credentials + inputs: + - name: username + label: Username + type: text + required: False + - name: password + label: Password + type: password + required: False +``` + +## Dependencies + +This tool requires the channels: `--channel conda-forge --channel bioconda --channel defaults --channel pytorch --channel ilastik-forge`. + + ## CHANGELOG +### 20240214 + +- Update fiji, max_inscribed_circles, omero_ij, simple_omero_client +- Allow to do 'Replace at runtime' where it deletes the ROIs of the image before running the analysis on the image and remove all tables linked to the object on which the script is run. +- Add a parameter for the thresholding method when using 'convert_to_mask'. +- Allow to run 'Options...' and 'Fill Holes' between the mask and the median step. +- As a consequence the tables have more columns than before. +- Use um as unit for minimum_diameter and closeness_tolerance. +- Use Double for minimum_diameter_um, closeness_tolerance_um and min_size_particle +- Change the default of min_size_particle from 5000 to 20000. +- Change the default of minimume_diameter from 20 to 40. +- Change the default of radius_median from 20 to 10. + ### 20231220 - Add a new parameter: segmentation_method which can be 'ilastik' or 'convert_to_mask'. If 'convert_to_mask' is chosen, it does an autothreshold. diff -r bd5771ff6aa3 -r 45375dc5dd8d omero_hyperstack_to_gastruloid_measurements.xml --- a/omero_hyperstack_to_gastruloid_measurements.xml Wed Dec 20 20:07:13 2023 +0000 +++ b/omero_hyperstack_to_gastruloid_measurements.xml Thu Feb 15 12:28:09 2024 +0000 @@ -1,6 +1,6 @@ - 20231220 + 20240214 @@ -29,8 +29,28 @@ + + + + + + + + + + + + + + + + + + + + @@ -40,18 +60,34 @@ - - +
+ + + + + + + + + + + + + +
+ + +
- fiji + fiji python - fiji-max_inscribed_circles + fiji-max_inscribed_circles fiji-ilastik - fiji-omero_ij - fiji-simple_omero_client + fiji-omero_ij + fiji-simple_omero_client output.log + 'USERNAME="",PASSWORD="",credentials="${credentials}",host="${omero_host}",port="${omero_port}",object_type="${omero_object.object_type}",id="${omero_object.omero_id}",segmentation_method="${mode.use_ilastik.segmentation_method}",use_existing="${mode.use_existing}",ilastik_project="$ilastik_project_file",ilastik_project_short_name="$ilastik_project_name",ilastik_project_type="${mode.use_ilastik.ilastik_project_type}",ilastik_label_OI="${mode.use_ilastik.ilastik_label_OI}",probability_threshold="${mode.use_ilastik.probability_threshold}",radius_median="${mode.radius_median}",min_size_particle="${mode.min_size_particle}",get_spine="true",minimum_diameter_um="${minimum_diameter_um}",closeness_tolerance_um="${closeness_tolerance_um}",min_similarity="${min_similarity}",output_directory="output",debug="${debug}",rescue="${mode.rescue}",ilastik_label_BG="${mode.use_ilastik.background.ilastik_label_BG}",probability_threshold_BG="${mode.use_ilastik.background.probability_threshold_BG}",keep_only_largest="${mode.keep_only_largest}",replace_at_runtime="${mode.replace_at_runtime}",thresholding_method="${mode.use_ilastik.thresholding_method}",options_do="${mode.options.options_do}",options_iteration="${mode.options.options_iteration}",options_count="${mode.options.options_count}",fill_holes_before_median="${mode.fill_holes_before_median}"' > output.log ]]> @@ -113,17 +149,26 @@ + + - + + + + + + + + @@ -138,15 +183,16 @@ - - + + + - - + + @@ -188,7 +234,7 @@ /* * = COPYRIGHT = - * © All rights reserved. ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE, Switzerland, BioImaging And Optics Platform (BIOP), 2023 + * © All rights reserved. ECOLE POLYTECHNIQUE FEDERALE DE LAUSANNE, Switzerland, BioImaging And Optics Platform (BIOP), 2024 * * Licensed under the BSD-3-Clause License: * Redistribution and use in source and binary forms, with or without modification, are permitted provided