Skip to content
Snippets Groups Projects
Commit fedca541 authored by Laurent Guerard's avatar Laurent Guerard
Browse files

Merge branch 'cellpose_rewrite'

parents 7003049f 06c407b9
Branches
Tags
No related merge requests found
# @ String (visibility=MESSAGE, value="<html><b> Welcome to Myosoft - identify fibers! </b></html>") msg1
# @ File (label="Select folder with your images", description="select folder with your images", style="directory") src_dir
# @ String(label="Extension for the images to look for", value="czi") filename_filter
# @ File (label="Select directory for output", style="directory") output_dir
# @ File(label="Cellpose environment folder", style="directory", description="Folder with the cellpose env") cellpose_dir
# @ Boolean (label="close image after processing", description="tick this box when using batch mode", value=False) close_raw
# @ String (visibility=MESSAGE, value="<html><b> Morphometric Gates </b></html>") msg2
# @ Integer (label="Min Area [um²]", value=10) minAr
# @ Integer (label="Max Area [um²]", value=6000) maxAr
# @ Double (label="Min Circularity", value=0.5) minCir
# @ Double (label="Max Circularity", value=1) maxCir
# @ Integer (label="Min perimeter [um]", value=5) minPer
# @ Integer (label="Max perimeter [um]", value=300) maxPer
# @ String (visibility=MESSAGE, value="<html><b> Expand ROIS to match fibers </b></html>") msg3
# @ Double (label="ROI expansion [microns]", value=1) enlarge_radius
# @ String (visibility=MESSAGE, value="<html><b> channel positions in the hyperstack </b></html>") msg5
# @ Integer (label="Membrane staining channel number", style="slider", min=1, max=5, value=1) membrane_channel
# @ Integer (label="Fiber staining (MHC) channel number (0=skip)", style="slider", min=0, max=5, value=3) fiber_channel
# @ Integer (label="minimum fiber intensity (0=auto)", description="0 = automatic threshold detection", value=0) min_fiber_intensity
# @ CommandService command
# @ RoiManager rm
# @ ResultsTable rt
# this is a python rewrite of the original ijm published at # this is a python rewrite of the original ijm published at
# https://github.com/Hyojung-Choo/Myosoft/blob/Myosoft-hub/Scripts/central%20nuclei%20counter.ijm # https://github.com/Hyojung-Choo/Myosoft/blob/Myosoft-hub/Scripts/central%20nuclei%20counter.ijm
# ─── Requirements ─────────────────────────────────────────────────────────────
# List of update sites needed for the code
# * TrackMate-Cellpose
# * IMCF
# * PTBIOP
# * CLIJ-CLIJ2
# ─── Imports ──────────────────────────────────────────────────────────────────
# IJ imports # IJ imports
# TODO: are the imports RoiManager and ResultsTable needed when using the services? # TODO: are the imports RoiManager and ResultsTable needed when using the services?
from ij import IJ, WindowManager as wm import os
from ij.plugin import Duplicator, RoiEnlarger, RoiScaler import sys
from trainableSegmentation import WekaSegmentation
from de.biovoxxel.toolbox import Extended_Particle_Analyzer
from ij.measure import ResultsTable
# Bio-formats imports # Bio-formats imports
from loci.plugins import BF # from loci.plugins import BF
from loci.plugins.in import ImporterOptions # from loci.plugins.in import ImporterOptions
# python imports # python imports
import time import time
import os
#@ String (visibility=MESSAGE, value="<html><b> Welcome to Myosoft - identify fibers! </b></html>") msg1 from ch.epfl.biop.ij2command import Labels2CompositeRois
#@ File (label="Select directory with classifiers", style="directory") classifiers_dir
#@ File (label="Select directory for output", style="directory") output_dir # TrackMate imports
#@ File (label="Select image file", description="select your image") path_to_image from fiji.plugin.trackmate import Logger, Model, Settings, TrackMate
#@ Boolean (label="close image after processing", description="tick this box when using batch mode", value=False) close_raw from fiji.plugin.trackmate.action import LabelImgExporter
#@ String (visibility=MESSAGE, value="<html><b> Morphometric Gates </b></html>") msg2 from fiji.plugin.trackmate.cellpose import CellposeDetectorFactory
#@ Integer (label="Min Area [um²]", value=10) minAr from fiji.plugin.trackmate.cellpose.CellposeSettings import PretrainedModel
#@ Integer (label="Max Area [um²]", value=6000) maxAr from fiji.plugin.trackmate.features import FeatureFilter
#@ Float (label="Min Circularity", value=0.5) minCir from fiji.plugin.trackmate.providers import (
#@ Float (label="Max Circularity", value=1) maxCir SpotAnalyzerProvider,
#@ Float (label="Min solidity", value=0.0) minSol SpotMorphologyAnalyzerProvider,
#@ Float (label="Max solidity", value=1) maxSol )
#@ Integer (label="Min perimeter [um]", value=5) minPer from fiji.plugin.trackmate.tracking.jaqaman import SparseLAPTrackerFactory
#@ Integer (label="Max perimeter [um]", value=300) maxPer from ij import IJ
#@ Integer (label="Min min ferret [um]", value=0.1) minMinFer from ij import WindowManager as wm
#@ Integer (label="Max min ferret [um]", value=100) maxMinFer from ij.measure import ResultsTable
#@ Integer (label="Min ferret AR", value=0) minFAR from ij.plugin import Duplicator, ImageCalculator, RoiEnlarger
#@ Integer (label="Max ferret AR", value=8) maxFAR from imcflibs import pathtools
#@ Float (label="Min roundess", value=0.2) minRnd from imcflibs.imagej import bioformats as bf
#@ Float (label="Max roundess", value=1) maxRnd from imcflibs.imagej import misc
#@ String (visibility=MESSAGE, value="<html><b> Expand ROIS to match fibers </b></html>") msg3
#@ Float (label="ROI expansion [microns]", value=1) enlarge # ─── Functions ────────────────────────────────────────────────────────────────
#@ String (visibility=MESSAGE, value="<html><b> channel positions in the hyperstack </b></html>") msg5
#@ Integer (label="Membrane staining channel number", style="slider", min=1, max=5, value=1) membrane_channel
#@ Integer (label="Fiber staining (MHC) channel number (0=skip)", style="slider", min=0, max=5, value=3) fiber_channel
#@ Integer (label="minimum fiber intensity (0=auto)", description="0 = automatic threshold detection", value=0) min_fiber_intensity
#@ Integer (label="sub-tiling to economize RAM", style="slider", min=1, max=8, value=4) tiling_factor
#@ RoiManager rm
#@ ResultsTable rt
def fix_ij_options(): def fix_ij_options():
"""put IJ into a defined state """Put IJ into a defined state."""
"""
# disable inverting LUT # disable inverting LUT
IJ.run("Appearance...", " menu=0 16-bit=Automatic") IJ.run("Appearance...", " menu=0 16-bit=Automatic")
# set foreground color to be white, background black # set foreground color to be white, background black
...@@ -68,7 +89,7 @@ def fix_ij_options(): ...@@ -68,7 +89,7 @@ def fix_ij_options():
def fix_ij_dirs(path): def fix_ij_dirs(path):
"""use forward slashes in directory paths """use forward slashes in directory paths.
Parameters Parameters
---------- ----------
...@@ -87,61 +108,72 @@ def fix_ij_dirs(path): ...@@ -87,61 +108,72 @@ def fix_ij_dirs(path):
return fixed_path return fixed_path
def open_image_with_BF(path_to_file): def fix_BF_czi_imagetitle(imp):
""" use Bio-Formats to opens the first image from an image file path """Fix the title of an image read using the bio-formats importer.
The title is modified to remove the ".czi" extension and replace
spaces with underscores.
Parameters Parameters
---------- ----------
path_to_file : string imp : ij.ImagePlus
path to the image file The image to be processed.
Returns Returns
------- -------
ImagePlus string
the first imp stored in a give file The modified title of the image.
""" """
options = ImporterOptions() image_title = os.path.basename(imp.getShortTitle())
options.setColorMode(ImporterOptions.COLOR_MODE_GRAYSCALE) # remove the ".czi" extension
options.setAutoscale(True)
options.setId(path_to_file)
imps = BF.openImagePlus(options) # is an array of ImagePlus
return imps[0]
def fix_BF_czi_imagetitle(imp):
image_title = os.path.basename( imp.getShortTitle() )
image_title = image_title.replace(".czi", "") image_title = image_title.replace(".czi", "")
# replace spaces with underscores
image_title = image_title.replace(" ", "_") image_title = image_title.replace(" ", "_")
# remove any double underscores
image_title = image_title.replace("_-_", "") image_title = image_title.replace("_-_", "")
# remove any double underscores
image_title = image_title.replace("__", "_") image_title = image_title.replace("__", "_")
# remove any "#" characters
image_title = image_title.replace("#", "Series") image_title = image_title.replace("#", "Series")
return image_title return image_title
def preprocess_membrane_channel(imp): def do_background_correction(imp, gaussian_radius=20):
"""apply myosoft pre-processing steps for the membrane channel """Perform background correction on an image.
This is done by applying a Gaussian blur to the image and then dividing the
original image by the blurred image.
Parameters Parameters
---------- ----------
imp : ImagePlus imp : ij.ImagePlus
a single channel image of the membrane staining The image to be corrected.
gaussian_radius : int
The radius of the Gaussian filter to be used. Default value is 20.
Returns
-------
ij.ImagePlus
The background-corrected image.
""" """
IJ.run(imp, "Enhance Contrast", "saturated=0.35") imp_bgd = imp.duplicate()
IJ.run(imp, "Apply LUT", "") IJ.run(
IJ.run(imp, "Enhance Contrast", "saturated=1") imp_bgd,
IJ.run(imp, "8-bit", "") "Gaussian Blur...",
IJ.run(imp, "Invert", "") "sigma=" + str(gaussian_radius) + " scaled",
IJ.run(imp, "Convolve...", "text1=[-1.0 -1.0 -1.0 -1.0 -1.0\n-1.0 -1.0 -1.0 -1.0 0\n-1.0 -1.0 24.0 -1.0 -1.0\n-1.0 -1.0 -1.0 -1.0 -1.0\n-1.0 -1.0 -1.0 -1.0 0] normalize") )
return ImageCalculator.run(imp, imp_bgd, "Divide create 32-bit")
def get_threshold_from_method(imp, channel, method): def get_threshold_from_method(imp, channel, method):
"""returns the threshold value of chosen IJ AutoThreshold method in desired channel """Get the value of automated threshold method.
Returns the threshold value of chosen IJ AutoThreshold method in desired channel.
Parameters Parameters
---------- ----------
imp : ImagePlus imp : ij.ImagePlus
the imp from which to get the threshold value the imp from which to get the threshold value
channel : integer channel : integer
the channel in which to get the treshold the channel in which to get the treshold
...@@ -153,7 +185,7 @@ def get_threshold_from_method(imp, channel, method): ...@@ -153,7 +185,7 @@ def get_threshold_from_method(imp, channel, method):
list list
the upper and the lower threshold (integer values) the upper and the lower threshold (integer values)
""" """
imp.setC(channel) # starts at 1 imp.setC(channel) # starts at 1
ip = imp.getProcessor() ip = imp.getProcessor()
ip.setAutoThreshold(method + " dark") ip.setAutoThreshold(method + " dark")
lower_thr = ip.getMinThreshold() lower_thr = ip.getMinThreshold()
...@@ -163,52 +195,194 @@ def get_threshold_from_method(imp, channel, method): ...@@ -163,52 +195,194 @@ def get_threshold_from_method(imp, channel, method):
return lower_thr, upper_thr return lower_thr, upper_thr
def apply_weka_model(model_path, imp, tiles_per_dim): def run_tm(
"""apply a pretrained WEKA model to an ImagePlus implus,
channel_seg,
cellpose_env,
seg_model,
diam_seg,
channel_sec=0,
quality_thresh=[0, 0],
intensity_thresh=[0, 0],
circularity_thresh=[0, 0],
perimeter_thresh=[0, 0],
area_thresh=[0, 0],
crop_roi=None,
use_gpu=True,
):
"""
Function to run TrackMate on open data, applying filters to spots.
Parameters Parameters
---------- ----------
model_path : string implus : ij.ImagePlus
path to the model file ImagePlus on which to run the function
imp : ImagePlus channel_seg : int
ImagePlus to apply the model to Channel of interest
tiles_per_dim : integer cellpose_env : str
tiles the imp to save RAM Path to the cellpose environment
seg_model : PretrainedModel
Model to use for the segmentation
diam_seg : float
Diameter to use for segmentation
channel_sec : int, optional
Secondary channel to use for segmentation, by default 0
quality_thresh : float, optional
Threshold for quality filtering, by default None
intensity_thresh : float, optional
Threshold for intensity filtering, by default None
circularity_thresh : float, optional
Threshold for circularity filtering, by default None
perimeter_thresh : float, optional
Threshold for perimeter filtering, by default None
area_thresh : float, optional
Threshold for area filtering, by default None
crop_roi : ROI, optional
ROI to crop on the image, by default None
use_gpu : bool, optional
Boolean to use GPU or not, by default True
Returns Returns
------- -------
ImagePlus ij.ImagePlus
the result of the WEKA segmentation. One channel per class. Label image with the segmented objects
""" """
segmentator = WekaSegmentation()
segmentator.loadClassifier( model_path )
result = segmentator.applyClassifier( imp, [tiles_per_dim, tiles_per_dim], 0, True ) #ImagePlus imp, int[x,y,z] tilesPerDim, int numThreads (0=all), boolean probabilityMaps
return result
# Get image dimensions and calibration
def process_weka_result(imp): dims = implus.getDimensions()
"""apply myosoft pre-processing steps for the imp after WEKA classification to prepare it cal = implus.getCalibration()
for ROI detection with the extended particle analyzer
# If the image has more than one slice, adjust the dimensions
if implus.getNSlices() > 1:
implus.setDimensions(dims[2], dims[4], dims[3])
# Set ROI if provided
if crop_roi is not None:
implus.setRoi(crop_roi)
# Initialize TrackMate model
model = Model()
model.setLogger(Logger.IJTOOLBAR_LOGGER)
# Prepare settings for TrackMate
settings = Settings(implus)
settings.detectorFactory = CellposeDetectorFactory()
# Configure detector settings
settings.detectorSettings["TARGET_CHANNEL"] = channel_seg
settings.detectorSettings["OPTIONAL_CHANNEL_2"] = channel_sec
settings.detectorSettings["CELLPOSE_PYTHON_FILEPATH"] = os.path.join(
cellpose_env, "python.exe"
)
settings.detectorSettings["CELLPOSE_MODEL_FILEPATH"] = os.path.join(
os.environ["USERPROFILE"], ".cellpose", "models"
)
settings.detectorSettings["CELLPOSE_MODEL"] = seg_model
settings.detectorSettings["CELL_DIAMETER"] = diam_seg
settings.detectorSettings["USE_GPU"] = use_gpu
settings.detectorSettings["SIMPLIFY_CONTOURS"] = True
settings.initialSpotFilterValue = -1.0
# Add spot analyzers
spotAnalyzerProvider = SpotAnalyzerProvider(1)
spotMorphologyProvider = SpotMorphologyAnalyzerProvider(1)
for key in spotAnalyzerProvider.getKeys():
settings.addSpotAnalyzerFactory(spotAnalyzerProvider.getFactory(key))
for key in spotMorphologyProvider.getKeys():
settings.addSpotAnalyzerFactory(spotMorphologyProvider.getFactory(key))
# Apply spot filters based on thresholds
if any(quality_thresh):
settings = set_trackmate_filter(settings, "QUALITY", quality_thresh)
if any(intensity_thresh):
settings = set_trackmate_filter(
settings, "MEAN_INTENSITY_CH" + str(channel_seg), intensity_thresh
)
if any(circularity_thresh):
settings = set_trackmate_filter(settings, "CIRCULARITY", circularity_thresh)
if any(area_thresh):
settings = set_trackmate_filter(settings, "AREA", area_thresh)
if any(perimeter_thresh):
settings = set_trackmate_filter(settings, "PERIMETER", perimeter_thresh)
# print(settings)
# Configure tracker
settings.trackerFactory = SparseLAPTrackerFactory()
settings.trackerSettings = settings.trackerFactory.getDefaultSettings()
# settings.addTrackAnalyzer(TrackDurationAnalyzer())
settings.trackerSettings["LINKING_MAX_DISTANCE"] = 3.0
settings.trackerSettings["GAP_CLOSING_MAX_DISTANCE"] = 3.0
settings.trackerSettings["MAX_FRAME_GAP"] = 2
# Initialize TrackMate with model and settings
trackmate = TrackMate(model, settings)
trackmate.computeSpotFeatures(True)
trackmate.computeTrackFeatures(False)
# Check input validity
if not trackmate.checkInput():
sys.exit(str(trackmate.getErrorMessage()))
return
# Process the data
if not trackmate.process():
if "[SparseLAPTracker] The spot collection is empty." in str(
trackmate.getErrorMessage()
):
return IJ.createImage(
"Untitled",
"8-bit black",
implus.getWidth(),
implus.getHeight(),
implus.getNFrames(),
)
else:
sys.exit(str(trackmate.getErrorMessage()))
return
# Export the label image
# sm = SelectionModel(model)
exportSpotsAsDots = False
exportTracksOnly = False
label_imp = LabelImgExporter.createLabelImagePlus(
trackmate, exportSpotsAsDots, exportTracksOnly, False
)
label_imp.setDimensions(1, dims[3], dims[4])
label_imp.setCalibration(cal)
implus.setDimensions(dims[2], dims[3], dims[4])
return label_imp
def set_trackmate_filter(settings, filter_name, filter_value):
"""Sets a TrackMate spot filter with specified filter name and values.
Parameters Parameters
---------- ----------
imp : ImagePlus settings : Settings
a single channel (= desired class) of the WEKA classification result imp TrackMate settings object to which the filter will be added.
filter_name : str
The name of the filter to be applied.
filter_value : list
A list containing two values for the filter. The first value is
applied as an above-threshold filter, and the second as a below-threshold filter.
""" """
IJ.run(imp, "8-bit", "") filter = FeatureFilter(filter_name, filter_value[0], True)
IJ.run(imp, "Median...", "radius=3") settings.addSpotFilter(filter)
IJ.run(imp, "Gaussian Blur...", "sigma=2") filter = FeatureFilter(filter_name, filter_value[1], False)
IJ.run(imp, "Auto Threshold", "method=MaxEntropy") settings.addSpotFilter(filter)
IJ.run(imp, "Invert", "") return settings
def delete_channel(imp, channel_number): def delete_channel(imp, channel_number):
"""delete a channel from target imp """Delete a channel from target imp.
Parameters Parameters
---------- ----------
imp : ImagePlus imp : ij.ImagePlus
the imp from which to delete target channel the imp from which to delete target channel
channel_number : integer channel_number : integer
the channel number to be deleted. starts at 0. the channel number to be deleted. starts at 0.
...@@ -217,59 +391,14 @@ def delete_channel(imp, channel_number): ...@@ -217,59 +391,14 @@ def delete_channel(imp, channel_number):
IJ.run(imp, "Delete Slice", "delete=channel") IJ.run(imp, "Delete Slice", "delete=channel")
def run_extended_particle_analyzer( imp, eda_parameters ): def measure_in_all_rois(imp, channel, rm):
"""identifies ROIs in target imp using the extended particle analyzer of the BioVoxxel toolbox """Gives measurements for all ROIs in ROIManager.
with given parameters
Parameters Measures in all ROIS on a given channel of imp all parameters that are set in IJ "Set Measurements".
----------
imp : ImagePlus
the image on which to run the EPA on. Should be 8-bit thresholded
eda_parameters : array
all user defined parameters to restrict ROI identification
"""
epa = Extended_Particle_Analyzer()
epa.readInputImageParameters(imp)
epa.setDefaultParameterFields()
# expose all parameters explicitly
epa.usePixel = False
epa.usePixelForOutput = False
epa.Area = str(eda_parameters[0]) + "-" + str(eda_parameters[1])
epa.Extent = "0.00-1.00"
epa.Perimeter = str(eda_parameters[2]) + "-" + str(eda_parameters[3])
epa.Circularity = str(eda_parameters[4]) + "-" + str(eda_parameters[5])
epa.Roundness = str(eda_parameters[6]) + "-" + str(eda_parameters[7])
epa.Solidity = str(eda_parameters[8]) + "-" + str(eda_parameters[9])
epa.Compactness = "0.00-1.00"
epa.AR = "0-Infinity"
epa.FeretAR = str(eda_parameters[10]) + "-" + str(eda_parameters[11])
epa.EllipsoidAngle = "0-180"
epa.MaxFeret = "0-Infinity"
epa.MinFeret = str(eda_parameters[12]) + "-" + str(eda_parameters[13])
epa.FeretAngle = "0-180"
epa.COV = "0.00-1.00"
epa.Output = "Nothing"
epa.Redirect = "None"
epa.Correction = "None"
epa.Reset = False
epa.DisplayResults = False
epa.ClearResults = False
epa.Summarize = False
epa.AddToManager = True
epa.ExcludeEdges = False
epa.IncludeHoles = False
epa.defineParticleAnalyzers()
epa.particleAnalysis( imp.getProcessor(), imp, imp.getTitle() )
def measure_in_all_rois( imp, channel, rm ):
"""measures in all ROIS on a given channel of imp all parameters that are set in IJ "Set Measurements"
Parameters Parameters
---------- ----------
imp : ImagePlus imp : ij.ImagePlus
the imp to measure on the imp to measure on
channel : integer channel : integer
the channel to measure in. starts at 1. the channel to measure in. starts at 1.
...@@ -277,12 +406,12 @@ def measure_in_all_rois( imp, channel, rm ): ...@@ -277,12 +406,12 @@ def measure_in_all_rois( imp, channel, rm ):
a reference of the IJ-RoiManager a reference of the IJ-RoiManager
""" """
imp.setC(channel) imp.setC(channel)
rm.runCommand(imp,"Deselect") rm.runCommand(imp, "Deselect")
rm.runCommand(imp,"Measure") rm.runCommand(imp, "Measure")
def change_all_roi_color( rm, color ): def change_all_roi_color(rm, color):
"""change the color of all ROIs in the RoiManager """Cchange the color of all ROIs in the RoiManager.
Parameters Parameters
---------- ----------
...@@ -292,13 +421,13 @@ def change_all_roi_color( rm, color ): ...@@ -292,13 +421,13 @@ def change_all_roi_color( rm, color ):
the desired color. e.g. "green", "red", "yellow", "magenta" ... the desired color. e.g. "green", "red", "yellow", "magenta" ...
""" """
number_of_rois = rm.getCount() number_of_rois = rm.getCount()
for roi in range( number_of_rois ): for roi in range(number_of_rois):
rm.select(roi) rm.select(roi)
rm.runCommand("Set Color", color) rm.runCommand("Set Color", color)
def change_subset_roi_color( rm, selected_rois, color ): def change_subset_roi_color(rm, selected_rois, color):
"""change the color of selected ROIs in the RoiManager """Change the color of selected ROIs in the RoiManager.
Parameters Parameters
---------- ----------
...@@ -316,21 +445,21 @@ def change_subset_roi_color( rm, selected_rois, color ): ...@@ -316,21 +445,21 @@ def change_subset_roi_color( rm, selected_rois, color ):
def show_all_rois_on_image(rm, imp): def show_all_rois_on_image(rm, imp):
"""shows all ROIs in the ROiManager on imp """Shows all ROIs in the ROiManager on imp.
Parameters Parameters
---------- ----------
rm : RoiManager rm : RoiManager
a reference of the IJ-RoiManager a reference of the IJ-RoiManager
imp : ImagePlus imp : ij.ImagePlus
the imp on which to show the ROIs the imp on which to show the ROIs
""" """
imp.show() imp.show()
rm.runCommand(imp,"Show All") rm.runCommand(imp, "Show All")
def save_all_rois(rm, target): def save_all_rois(rm, target):
"""save all ROIs in the RoiManager as zip to target path """Save all ROIs in the RoiManager as zip to target path.
Parameters Parameters
---------- ----------
...@@ -342,8 +471,8 @@ def save_all_rois(rm, target): ...@@ -342,8 +471,8 @@ def save_all_rois(rm, target):
rm.runCommand("Save", target) rm.runCommand("Save", target)
def save_selected_rois( rm, selected_rois, target ): def save_selected_rois(rm, selected_rois, target):
"""save selected ROIs in the RoiManager as zip to target path """Save selected ROIs in the RoiManager as zip to target path.
Parameters Parameters
---------- ----------
...@@ -360,8 +489,8 @@ def save_selected_rois( rm, selected_rois, target ): ...@@ -360,8 +489,8 @@ def save_selected_rois( rm, selected_rois, target ):
rm.runCommand("Deselect") rm.runCommand("Deselect")
def enlarge_all_rois( amount_in_um, rm, pixel_size_in_um ): def enlarge_all_rois(amount_in_um, rm, pixel_size_in_um):
"""enlarges all ROIs in the RoiManager by x scaled units """Enlarges all ROIs in the RoiManager by x scaled units.
Parameters Parameters
---------- ----------
...@@ -380,13 +509,15 @@ def enlarge_all_rois( amount_in_um, rm, pixel_size_in_um ): ...@@ -380,13 +509,15 @@ def enlarge_all_rois( amount_in_um, rm, pixel_size_in_um ):
rm.addRoi(enlarged_roi) rm.addRoi(enlarged_roi)
def select_positive_fibers( imp, channel, rm, min_intensity ): def select_positive_fibers(imp, channel, rm, min_intensity):
"""For all ROIs in the RoiManager, select ROIs based on intensity measurement in given channel of imp. """Select ROIs in ROIManager based on intensity in specific channel.
For all ROIs in the RoiManager, select ROIs based on intensity measurement in given channel of imp.
See https://imagej.nih.gov/ij/developer/api/ij/process/ImageStatistics.html See https://imagej.nih.gov/ij/developer/api/ij/process/ImageStatistics.html
Parameters Parameters
---------- ----------
imp : ImagePlus imp : ij.ImagePlus
the imp on which to measure the imp on which to measure
channel : integer channel : integer
the channel on which to measure. starts at 1 the channel on which to measure. starts at 1
...@@ -412,8 +543,10 @@ def select_positive_fibers( imp, channel, rm, min_intensity ): ...@@ -412,8 +543,10 @@ def select_positive_fibers( imp, channel, rm, min_intensity ):
return selected_rois return selected_rois
def preset_results_column( rt, column, value): def preset_results_column(rt, column, value):
"""pre-set all rows in given column of the IJ-ResultsTable with desired value """Pre-set values in selected column from the ResultsTable.
Pre-set all rows in given column of the IJ-ResultsTable with desired value.
Parameters Parameters
---------- ----------
...@@ -424,14 +557,14 @@ def preset_results_column( rt, column, value): ...@@ -424,14 +557,14 @@ def preset_results_column( rt, column, value):
value : string or float or integer value : string or float or integer
the value to be set the value to be set
""" """
for i in range( rt.size() ): for i in range(rt.size()):
rt.setValue(column, i, value) rt.setValue(column, i, value)
rt.show("Results") rt.show("Results")
def add_results( rt, column, row, value ): def add_results(rt, column, row, value):
"""adds a value in desired rows of a given column """Adds a value in desired rows of a given column.
Parameters Parameters
---------- ----------
...@@ -444,27 +577,27 @@ def add_results( rt, column, row, value ): ...@@ -444,27 +577,27 @@ def add_results( rt, column, row, value ):
value : string or float or integer value : string or float or integer
the value to be set the value to be set
""" """
for i in range( len( row ) ): for i in range(len(row)):
rt.setValue(column, row[i], value) rt.setValue(column, row[i], value)
rt.show("Results") rt.show("Results")
def enhance_contrast( imp ): def enhance_contrast(imp):
"""use "Auto" Contrast & Brightness settings in each channel of imp """Use "Auto" Contrast & Brightness settings in each channel of imp.
Parameters Parameters
---------- ----------
imp : ImagePlus imp : ij.ImagePlus
the imp on which to change C&B the imp on which to change C&B
""" """
for channel in range( imp.getDimensions()[2] ): for channel in range(imp.getDimensions()[2]):
imp.setC(channel + 1) # IJ channels start at 1 imp.setC(channel + 1) # IJ channels start at 1
IJ.run(imp, "Enhance Contrast", "saturated=0.35") IJ.run(imp, "Enhance Contrast", "saturated=0.35")
def renumber_rois(rm): def renumber_rois(rm):
"""rename all ROIs in the RoiManager according to their number """Rename all ROIs in the RoiManager according to their number.
Parameters Parameters
---------- ----------
...@@ -472,12 +605,12 @@ def renumber_rois(rm): ...@@ -472,12 +605,12 @@ def renumber_rois(rm):
a reference of the IJ-RoiManager a reference of the IJ-RoiManager
""" """
number_of_rois = rm.getCount() number_of_rois = rm.getCount()
for roi in range( number_of_rois ): for roi in range(number_of_rois):
rm.rename( roi, str(roi + 1) ) rm.rename(roi, str(roi + 1))
def setup_defined_ij(rm, rt): def setup_defined_ij(rm, rt):
"""set up a clean and defined Fiji user environment """Set up a clean and defined Fiji user environment.
Parameters Parameters
---------- ----------
...@@ -487,124 +620,165 @@ def setup_defined_ij(rm, rt): ...@@ -487,124 +620,165 @@ def setup_defined_ij(rm, rt):
a reference of the IJ-ResultsTable a reference of the IJ-ResultsTable
""" """
fix_ij_options() fix_ij_options()
rm.runCommand('reset') rm.runCommand("reset")
rt.reset() rt.reset()
IJ.log("\\Clear") IJ.log("\\Clear")
execution_start_time = time.time() # ─── Main Code ────────────────────────────────────────────────────────────────
setup_defined_ij(rm, rt) if __name__ == "__main__":
execution_start_time = time.time()
print rt.size() IJ.log("\\Clear")
misc.timed_log("Script starting")
# open image using Bio-Formats setup_defined_ij(rm, rt)
path_to_image = fix_ij_dirs(path_to_image)
raw = open_image_with_BF(path_to_image) file_list = pathtools.listdir_matching(
src_dir.getPath(), filename_filter, fullpath=True
# get image info )
raw_image_calibration = raw.getCalibration()
raw_image_title = fix_BF_czi_imagetitle(raw) out_dir_info = pathtools.parse_path(output_dir)
print("raw image title: ", str(raw_image_title))
for index, file in enumerate(file_list):
# take care of paths and directories # open image using Bio-Formats
output_dir = fix_ij_dirs(output_dir) + "/" + str(raw_image_title) + "/1_identify_fibers" file_info = pathtools.parse_path(file)
print("output_dir: ", str(output_dir)) misc.progressbar(index + 1, len(file_list), 1, "Opening : ")
raw = bf.import_image(file_info["full"])[0]
if not os.path.exists( str(output_dir) ):
os.makedirs( str(output_dir) ) # get image info
raw_image_calibration = raw.getCalibration()
classifiers_dir = fix_ij_dirs(classifiers_dir) raw_image_title = fix_BF_czi_imagetitle(raw)
primary_model = classifiers_dir + "/" + "primary.model" print("raw image title: ", str(raw_image_title))
secondary_model = classifiers_dir + "/" + "secondary_central_nuclei.model"
# take care of paths and directories
# update the log for the user output_dir = os.path.join(
IJ.log( "Now working on " + str(raw_image_title) ) out_dir_info["full"], str(raw_image_title), "1_identify_fibers"
if raw_image_calibration.scaled() == False: )
IJ.log("Your image is not spatially calibrated! Size measurements are only possible in [px].") print("output_dir: ", str(output_dir))
IJ.log( " -- settings used -- ")
IJ.log( "area = " + str(minAr) + "-" + str(maxAr) ) if not os.path.exists(str(output_dir)):
IJ.log( "perimeter = " + str(minPer) + "-" + str(maxPer) ) os.makedirs(str(output_dir))
IJ.log( "circularity = " + str(minCir) + "-" + str(maxCir) )
IJ.log( "roundness = " + str(minRnd) + "-" + str(maxRnd) ) # update the log for the user
IJ.log( "solidity = " + str(minSol) + "-" + str(maxSol) ) misc.timed_log("Now working on " + str(raw_image_title))
IJ.log( "feret_ar = " + str(minFAR) + "-" + str(maxFAR) ) if raw_image_calibration.scaled() is False:
IJ.log( "min_feret = " + str(minMinFer) + "-" + str(maxMinFer) ) IJ.log(
IJ.log( "ROI expansion [microns] = " + str(enlarge) ) "Your image is not spatially calibrated! Size measurements are only possible in [px]."
IJ.log( "Membrane channel = " + str(membrane_channel) ) )
IJ.log( "MHC positive fiber channel = " + str(fiber_channel) ) # Only print it once since we'll use the same settings everytime
IJ.log( "sub-tiling = " + str(tiling_factor) ) if index == 0:
IJ.log( " -- settings used -- ") IJ.log(" -- settings used -- ")
IJ.log("area = " + str(minAr) + "-" + str(maxAr))
# image (pre)processing and segmentation (-> ROIs) IJ.log("perimeter = " + str(minPer) + "-" + str(maxPer))
membrane = Duplicator().run(raw, membrane_channel, membrane_channel, 1, 1, 1, 1) # imp, firstC, lastC, firstZ, lastZ, firstT, lastT IJ.log("circularity = " + str(minCir) + "-" + str(maxCir))
preprocess_membrane_channel(membrane) IJ.log("ROI expansion [microns] = " + str(enlarge_radius))
weka_result1 = apply_weka_model(primary_model, membrane, tiling_factor ) IJ.log("Membrane channel = " + str(membrane_channel))
delete_channel(weka_result1, 1) IJ.log("MHC positive fiber channel = " + str(fiber_channel))
weka_result2 = apply_weka_model(secondary_model, weka_result1, tiling_factor ) # IJ.log("sub-tiling = " + str(tiling_factor))
delete_channel(weka_result2, 1) IJ.log(" -- settings used -- ")
weka_result2.setCalibration(raw_image_calibration)
process_weka_result(weka_result2) # image (pre)processing and segmentation (-> ROIs)# imp, firstC, lastC, firstZ,
IJ.saveAs(weka_result2, "Tiff", output_dir + "/" + raw_image_title + "_all_fibers_binary") # lastZ, firstT, lastT
eda_parameters = [minAr, maxAr, minPer, maxPer, minCir, maxCir, minRnd, maxRnd, minSol, maxSol, minFAR, maxFAR, minMinFer, maxMinFer] membrane = Duplicator().run(raw, membrane_channel, membrane_channel, 1, 1, 1, 1)
raw.show() # EPA will not work if no image is shown imp_bgd_corrected = do_background_correction(membrane)
run_extended_particle_analyzer(weka_result2, eda_parameters) IJ.run("Conversions...", "scale")
IJ.run(imp_bgd_corrected, "16-bit", "")
# modify rois
rm.hide() imp_result = run_tm(
raw.hide() imp_bgd_corrected,
enlarge_all_rois( enlarge, rm, raw_image_calibration.pixelWidth ) 1,
renumber_rois(rm) cellpose_dir.getPath(),
save_all_rois( rm, output_dir + "/" + raw_image_title + "_all_fiber_rois.zip" ) PretrainedModel.CYTO2,
30.0,
# check for positive fibers area_thresh=[minAr, maxAr],
if fiber_channel > 0: circularity_thresh=[minCir, maxCir],
if min_fiber_intensity == 0: perimeter_thresh=[minPer, maxPer],
min_fiber_intensity = get_threshold_from_method(raw, fiber_channel, "Mean")[0] )
IJ.log( "automatic intensity threshold detection: True" ) IJ.saveAs(
imp_result,
IJ.log( "fiber intensity threshold: " + str(min_fiber_intensity) ) "Tiff",
change_all_roi_color(rm, "blue") os.path.join(output_dir, raw_image_title + "_all_fibers_binary"),
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, output_dir + "/" + raw_image_title + "_mhc_positive_fiber_rois.zip") command.run(Labels2CompositeRois, True, "rm", rm, "imp", imp_result).get()
# measure size & shape, save enlarge_all_rois(enlarge_radius, rm, raw_image_calibration.pixelWidth)
IJ.run("Set Measurements...", "area perimeter shape feret's redirect=None decimal=4") renumber_rois(rm)
IJ.run("Clear Results", "") save_all_rois(
measure_in_all_rois( raw, membrane_channel, rm ) rm, os.path.join(output_dir, raw_image_title + "_all_fiber_rois.zip")
)
rt = ResultsTable.getResultsTable("Results")
# check for positive fibers
print rt.size() if fiber_channel > 0:
if min_fiber_intensity == 0:
if fiber_channel > 0: min_fiber_intensity = get_threshold_from_method(
print rt.size() raw, fiber_channel, "Mean"
preset_results_column( rt, "MHC Positive Fibers (magenta)", "NO" ) )[0]
print rt.size() IJ.log("automatic intensity threshold detection: True")
add_results( rt, "MHC Positive Fibers (magenta)", positive_fibers, "YES")
print rt.size() IJ.log("fiber intensity threshold: " + str(min_fiber_intensity))
change_all_roi_color(rm, "blue")
rt.save(output_dir + "/" + raw_image_title + "_all_fibers_results.csv") positive_fibers = select_positive_fibers(
print "saved the all_fibers_results.csv" raw, fiber_channel, rm, min_fiber_intensity
# dress up the original image, save a overlay-png, present original to the user )
rm.show() change_subset_roi_color(rm, positive_fibers, "magenta")
raw.show() save_selected_rois(
show_all_rois_on_image( rm, raw ) rm,
raw.setDisplayMode(IJ.COMPOSITE) positive_fibers,
enhance_contrast( raw ) os.path.join(
IJ.run("From ROI Manager", "") # ROIs -> overlays so they show up in the saved png output_dir, raw_image_title + "_mhc_positive_fiber_rois.zip"
qc_duplicate = raw.duplicate() ),
IJ.saveAs(qc_duplicate, "PNG", output_dir + "/" + raw_image_title + "_all_fibers") )
qc_duplicate.close()
wm.toFront( raw.getWindow() ) # measure size & shape, save
IJ.run("Remove Overlay", "") IJ.run(
raw.setDisplayMode(IJ.GRAYSCALE) "Set Measurements...",
show_all_rois_on_image( rm, raw ) "area perimeter shape feret's redirect=None decimal=4",
total_execution_time_min = (time.time() - execution_start_time) / 60.0 )
IJ.log("total time in minutes: " + str(total_execution_time_min)) IJ.run("Clear Results", "")
IJ.log( "~~ all done ~~" ) measure_in_all_rois(raw, membrane_channel, rm)
IJ.selectWindow("Log")
IJ.saveAs("Text", str(output_dir + "/" + raw_image_title + "_all_fibers_Log")) rt = ResultsTable.getResultsTable("Results")
if close_raw == True:
raw.close() # print(rt.size())
\ No newline at end of file
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()
total_execution_time_min = (time.time() - execution_start_time) / 60.0
IJ.log("total time in minutes: " + str(total_execution_time_min))
IJ.log("~~ all done ~~")
...@@ -9,22 +9,22 @@ Original publication: <https://doi.org/10.1371/journal.pone.0229041> ...@@ -9,22 +9,22 @@ Original publication: <https://doi.org/10.1371/journal.pone.0229041>
Original code: <https://github.com/Hyojung-Choo/Myosoft/tree/Myosoft-hub> Original code: <https://github.com/Hyojung-Choo/Myosoft/tree/Myosoft-hub>
## `1_identify_fibers.py` ## [`1_identify_fibers.py`](1_identify_fibers.py)
- Will identify all fibers based on the membrane staining using WEKA pixel - Will identify all fibers based on the membrane staining using [Cellpose](https://github.com/MouseLand/cellpose) segmentation, filter them according to the morphometric gates and save the
classification, filter them according to the morphometric gates and save the
corresponding ROIs. corresponding ROIs.
- Will now also save the WEKA segmentation as a binary so it can be edited - Need to be installed ont the machine where the script is run. Follow [this guide](https://wiki.biozentrum.unibas.ch/display/IMCF/Cellpose+python+environment) to create the environment.
- Will now also save the Cellpose segmentation as a binary so it can be edited
manually. If you do so, you need to run the "extended particle analyzer" manually. If you do so, you need to run the "extended particle analyzer"
manually as well to choose & apply the morphometric gates. manually as well to choose & apply the morphometric gates.
- Can be run in batch. - Can be run in batch.
## `2a_identify_MHC_positive_fibers.py` ## [`2a_identify_MHC_positive_fibers.py`](2a_identify_MHC_positive_fibers.py)
- Allows to manual re-run the MHC positive fiber detection. Useful in case you - Allows to manual re-run the MHC positive fiber detection. Useful in case you
would like to re-run detection with a manual threshold for an image. would like to re-run detection with a manual threshold for an image.
## `2b_central_nuclei_counter.py` ## [`2b_central_nuclei_counter.py`](2b_central_nuclei_counter.py)
- Will identify centralized nuclei given a ROI-zip together with its - Will identify centralized nuclei given a ROI-zip together with its
corresponding image. corresponding image.
...@@ -32,14 +32,14 @@ Original code: <https://github.com/Hyojung-Choo/Myosoft/tree/Myosoft-hub> ...@@ -32,14 +32,14 @@ Original code: <https://github.com/Hyojung-Choo/Myosoft/tree/Myosoft-hub>
information of a MHC staining channel. information of a MHC staining channel.
- The ROI color code is annotated in the results table. - The ROI color code is annotated in the results table.
## `2c_fibertyping.py` ## [`2c_fibertyping.py`](2c_fibertyping.py)
- Identifies positive fibers in up to 3 channels given a ROI-zip together with - Identifies positive fibers in up to 3 channels given a ROI-zip together with
its corresponding image. its corresponding image.
- Includes identification of double and triple positive combinations. - Includes identification of double and triple positive combinations.
- The ROI color code is annotated in the results table. - The ROI color code is annotated in the results table.
## `3_manual_rerun.py` ## [`3_manual_rerun.py`](3_manual_rerun.py)
- Requires an already open image with an already populated ROI manager. - Requires an already open image with an already populated ROI manager.
- Allows to manually select measurement parameters and the measurement channel. - Allows to manually select measurement parameters and the measurement channel.
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment