Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • imcf/myosoft-imcf
1 result
Show changes
Commits on Source (10)
# @ 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
# 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
# TODO: are the imports RoiManager and ResultsTable needed when using the services?
from ij import IJ, WindowManager as wm
from ij.plugin import Duplicator, RoiEnlarger, RoiScaler
from trainableSegmentation import WekaSegmentation
from de.biovoxxel.toolbox import Extended_Particle_Analyzer
from ij.measure import ResultsTable
import os
import sys
# Bio-formats imports
from loci.plugins import BF
from loci.plugins.in import ImporterOptions
# from loci.plugins import BF
# from loci.plugins.in import ImporterOptions
# python imports
import time
import os
#@ String (visibility=MESSAGE, value="<html><b> Welcome to Myosoft - identify fibers! </b></html>") msg1
#@ File (label="Select directory with classifiers", style="directory") classifiers_dir
#@ File (label="Select directory for output", style="directory") output_dir
#@ File (label="Select image file", description="select your image") path_to_image
#@ 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
#@ Float (label="Min Circularity", value=0.5) minCir
#@ Float (label="Max Circularity", value=1) maxCir
#@ Float (label="Min solidity", value=0.0) minSol
#@ Float (label="Max solidity", value=1) maxSol
#@ Integer (label="Min perimeter [um]", value=5) minPer
#@ Integer (label="Max perimeter [um]", value=300) maxPer
#@ Integer (label="Min min ferret [um]", value=0.1) minMinFer
#@ Integer (label="Max min ferret [um]", value=100) maxMinFer
#@ Integer (label="Min ferret AR", value=0) minFAR
#@ Integer (label="Max ferret AR", value=8) maxFAR
#@ Float (label="Min roundess", value=0.2) minRnd
#@ Float (label="Max roundess", value=1) maxRnd
#@ String (visibility=MESSAGE, value="<html><b> Expand ROIS to match fibers </b></html>") msg3
#@ Float (label="ROI expansion [microns]", value=1) enlarge
#@ 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
from ch.epfl.biop.ij2command import Labels2CompositeRois
# TrackMate imports
from fiji.plugin.trackmate import Logger, Model, Settings, TrackMate
from fiji.plugin.trackmate.action import LabelImgExporter
from fiji.plugin.trackmate.cellpose import CellposeDetectorFactory
from fiji.plugin.trackmate.cellpose.CellposeSettings import PretrainedModel
from fiji.plugin.trackmate.features import FeatureFilter
from fiji.plugin.trackmate.providers import (
SpotAnalyzerProvider,
SpotMorphologyAnalyzerProvider,
)
from fiji.plugin.trackmate.tracking.jaqaman import SparseLAPTrackerFactory
from ij import IJ
from ij import WindowManager as wm
from ij.measure import ResultsTable
from ij.plugin import Duplicator, ImageCalculator, RoiEnlarger
from imcflibs import pathtools
from imcflibs.imagej import bioformats as bf
from imcflibs.imagej import misc
# ─── Functions ────────────────────────────────────────────────────────────────
def fix_ij_options():
"""put IJ into a defined state
"""
"""Put IJ into a defined state."""
# disable inverting LUT
IJ.run("Appearance...", " menu=0 16-bit=Automatic")
# set foreground color to be white, background black
......@@ -68,7 +89,7 @@ def fix_ij_options():
def fix_ij_dirs(path):
"""use forward slashes in directory paths
"""use forward slashes in directory paths.
Parameters
----------
......@@ -87,61 +108,72 @@ def fix_ij_dirs(path):
return fixed_path
def open_image_with_BF(path_to_file):
""" use Bio-Formats to opens the first image from an image file path
def fix_BF_czi_imagetitle(imp):
"""Fix the title of an image read using the bio-formats importer.
The title is modified to remove the ".czi" extension and replace
spaces with underscores.
Parameters
----------
path_to_file : string
path to the image file
imp : ij.ImagePlus
The image to be processed.
Returns
-------
ImagePlus
the first imp stored in a give file
string
The modified title of the image.
"""
options = ImporterOptions()
options.setColorMode(ImporterOptions.COLOR_MODE_GRAYSCALE)
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 = os.path.basename(imp.getShortTitle())
# remove the ".czi" extension
image_title = image_title.replace(".czi", "")
# replace spaces with underscores
image_title = image_title.replace(" ", "_")
# remove any double underscores
image_title = image_title.replace("_-_", "")
# remove any double underscores
image_title = image_title.replace("__", "_")
# remove any "#" characters
image_title = image_title.replace("#", "Series")
return image_title
def preprocess_membrane_channel(imp):
"""apply myosoft pre-processing steps for the membrane channel
def do_background_correction(imp, gaussian_radius=20):
"""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
----------
imp : ImagePlus
a single channel image of the membrane staining
imp : ij.ImagePlus
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")
IJ.run(imp, "Apply LUT", "")
IJ.run(imp, "Enhance Contrast", "saturated=1")
IJ.run(imp, "8-bit", "")
IJ.run(imp, "Invert", "")
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")
imp_bgd = imp.duplicate()
IJ.run(
imp_bgd,
"Gaussian Blur...",
"sigma=" + str(gaussian_radius) + " scaled",
)
return ImageCalculator.run(imp, imp_bgd, "Divide create 32-bit")
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
----------
imp : ImagePlus
imp : ij.ImagePlus
the imp from which to get the threshold value
channel : integer
the channel in which to get the treshold
......@@ -153,7 +185,7 @@ def get_threshold_from_method(imp, channel, method):
list
the upper and the lower threshold (integer values)
"""
imp.setC(channel) # starts at 1
imp.setC(channel) # starts at 1
ip = imp.getProcessor()
ip.setAutoThreshold(method + " dark")
lower_thr = ip.getMinThreshold()
......@@ -163,52 +195,194 @@ def get_threshold_from_method(imp, channel, method):
return lower_thr, upper_thr
def apply_weka_model(model_path, imp, tiles_per_dim):
"""apply a pretrained WEKA model to an ImagePlus
def run_tm(
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
----------
model_path : string
path to the model file
imp : ImagePlus
ImagePlus to apply the model to
tiles_per_dim : integer
tiles the imp to save RAM
implus : ij.ImagePlus
ImagePlus on which to run the function
channel_seg : int
Channel of interest
cellpose_env : str
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
-------
ImagePlus
the result of the WEKA segmentation. One channel per class.
ij.ImagePlus
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
def process_weka_result(imp):
"""apply myosoft pre-processing steps for the imp after WEKA classification to prepare it
for ROI detection with the extended particle analyzer
# Get image dimensions and calibration
dims = implus.getDimensions()
cal = implus.getCalibration()
# 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
----------
imp : ImagePlus
a single channel (= desired class) of the WEKA classification result imp
settings : Settings
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", "")
IJ.run(imp, "Median...", "radius=3")
IJ.run(imp, "Gaussian Blur...", "sigma=2")
IJ.run(imp, "Auto Threshold", "method=MaxEntropy")
IJ.run(imp, "Invert", "")
filter = FeatureFilter(filter_name, filter_value[0], True)
settings.addSpotFilter(filter)
filter = FeatureFilter(filter_name, filter_value[1], False)
settings.addSpotFilter(filter)
return settings
def delete_channel(imp, channel_number):
"""delete a channel from target imp
"""Delete a channel from target imp.
Parameters
----------
imp : ImagePlus
imp : ij.ImagePlus
the imp from which to delete target channel
channel_number : integer
the channel number to be deleted. starts at 0.
......@@ -217,59 +391,14 @@ def delete_channel(imp, channel_number):
IJ.run(imp, "Delete Slice", "delete=channel")
def run_extended_particle_analyzer( imp, eda_parameters ):
"""identifies ROIs in target imp using the extended particle analyzer of the BioVoxxel toolbox
with given parameters
def measure_in_all_rois(imp, channel, rm):
"""Gives measurements for all ROIs in ROIManager.
Parameters
----------
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"
Measures in all ROIS on a given channel of imp all parameters that are set in IJ "Set Measurements".
Parameters
----------
imp : ImagePlus
imp : ij.ImagePlus
the imp to measure on
channel : integer
the channel to measure in. starts at 1.
......@@ -277,12 +406,12 @@ def measure_in_all_rois( imp, channel, rm ):
a reference of the IJ-RoiManager
"""
imp.setC(channel)
rm.runCommand(imp,"Deselect")
rm.runCommand(imp,"Measure")
rm.runCommand(imp, "Deselect")
rm.runCommand(imp, "Measure")
def change_all_roi_color( rm, color ):
"""change the color of all ROIs in the RoiManager
def change_all_roi_color(rm, color):
"""Cchange the color of all ROIs in the RoiManager.
Parameters
----------
......@@ -292,13 +421,13 @@ def change_all_roi_color( rm, color ):
the desired color. e.g. "green", "red", "yellow", "magenta" ...
"""
number_of_rois = rm.getCount()
for roi in range( number_of_rois ):
for roi in range(number_of_rois):
rm.select(roi)
rm.runCommand("Set Color", color)
def change_subset_roi_color( rm, selected_rois, color ):
"""change the color of selected ROIs in the RoiManager
def change_subset_roi_color(rm, selected_rois, color):
"""Change the color of selected ROIs in the RoiManager.
Parameters
----------
......@@ -316,21 +445,21 @@ def change_subset_roi_color( rm, selected_rois, color ):
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
----------
rm : RoiManager
a reference of the IJ-RoiManager
imp : ImagePlus
imp : ij.ImagePlus
the imp on which to show the ROIs
"""
imp.show()
rm.runCommand(imp,"Show All")
rm.runCommand(imp, "Show All")
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
----------
......@@ -342,8 +471,8 @@ def save_all_rois(rm, target):
rm.runCommand("Save", target)
def save_selected_rois( rm, selected_rois, target ):
"""save selected ROIs in the RoiManager as zip to target path
def save_selected_rois(rm, selected_rois, target):
"""Save selected ROIs in the RoiManager as zip to target path.
Parameters
----------
......@@ -360,8 +489,8 @@ def save_selected_rois( rm, selected_rois, target ):
rm.runCommand("Deselect")
def enlarge_all_rois( amount_in_um, rm, pixel_size_in_um ):
"""enlarges all ROIs in the RoiManager by x scaled units
def enlarge_all_rois(amount_in_um, rm, pixel_size_in_um):
"""Enlarges all ROIs in the RoiManager by x scaled units.
Parameters
----------
......@@ -380,13 +509,15 @@ def enlarge_all_rois( amount_in_um, rm, pixel_size_in_um ):
rm.addRoi(enlarged_roi)
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.
def select_positive_fibers(imp, channel, rm, min_intensity):
"""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
Parameters
----------
imp : ImagePlus
imp : ij.ImagePlus
the imp on which to measure
channel : integer
the channel on which to measure. starts at 1
......@@ -412,8 +543,10 @@ def select_positive_fibers( imp, channel, rm, min_intensity ):
return selected_rois
def preset_results_column( rt, column, value):
"""pre-set all rows in given column of the IJ-ResultsTable with desired value
def preset_results_column(rt, column, 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
----------
......@@ -424,14 +557,14 @@ def preset_results_column( rt, column, value):
value : string or float or integer
the value to be set
"""
for i in range( rt.size() ):
for i in range(rt.size()):
rt.setValue(column, i, value)
rt.show("Results")
def add_results( rt, column, row, value ):
"""adds a value in desired rows of a given column
def add_results(rt, column, row, value):
"""Adds a value in desired rows of a given column.
Parameters
----------
......@@ -444,27 +577,27 @@ def add_results( rt, column, row, value ):
value : string or float or integer
the value to be set
"""
for i in range( len( row ) ):
for i in range(len(row)):
rt.setValue(column, row[i], value)
rt.show("Results")
def enhance_contrast( imp ):
"""use "Auto" Contrast & Brightness settings in each channel of imp
def enhance_contrast(imp):
"""Use "Auto" Contrast & Brightness settings in each channel of imp.
Parameters
----------
imp : ImagePlus
imp : ij.ImagePlus
the imp on which to change C&B
"""
for channel in range( imp.getDimensions()[2] ):
imp.setC(channel + 1) # IJ channels start at 1
for channel in range(imp.getDimensions()[2]):
imp.setC(channel + 1) # IJ channels start at 1
IJ.run(imp, "Enhance Contrast", "saturated=0.35")
def renumber_rois(rm):
"""rename all ROIs in the RoiManager according to their number
"""Rename all ROIs in the RoiManager according to their number.
Parameters
----------
......@@ -472,12 +605,12 @@ def renumber_rois(rm):
a reference of the IJ-RoiManager
"""
number_of_rois = rm.getCount()
for roi in range( number_of_rois ):
rm.rename( roi, str(roi + 1) )
for roi in range(number_of_rois):
rm.rename(roi, str(roi + 1))
def setup_defined_ij(rm, rt):
"""set up a clean and defined Fiji user environment
"""Set up a clean and defined Fiji user environment.
Parameters
----------
......@@ -487,124 +620,165 @@ def setup_defined_ij(rm, rt):
a reference of the IJ-ResultsTable
"""
fix_ij_options()
rm.runCommand('reset')
rm.runCommand("reset")
rt.reset()
IJ.log("\\Clear")
execution_start_time = time.time()
setup_defined_ij(rm, rt)
print rt.size()
# open image using Bio-Formats
path_to_image = fix_ij_dirs(path_to_image)
raw = open_image_with_BF(path_to_image)
# 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 = fix_ij_dirs(output_dir) + "/" + 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) )
classifiers_dir = fix_ij_dirs(classifiers_dir)
primary_model = classifiers_dir + "/" + "primary.model"
secondary_model = classifiers_dir + "/" + "secondary_central_nuclei.model"
# update the log for the user
IJ.log( "Now working on " + str(raw_image_title) )
if raw_image_calibration.scaled() == False:
IJ.log("Your image is not spatially calibrated! Size measurements are only possible in [px].")
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( "roundness = " + str(minRnd) + "-" + str(maxRnd) )
IJ.log( "solidity = " + str(minSol) + "-" + str(maxSol) )
IJ.log( "feret_ar = " + str(minFAR) + "-" + str(maxFAR) )
IJ.log( "min_feret = " + str(minMinFer) + "-" + str(maxMinFer) )
IJ.log( "ROI expansion [microns] = " + str(enlarge) )
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)
membrane = Duplicator().run(raw, membrane_channel, membrane_channel, 1, 1, 1, 1) # imp, firstC, lastC, firstZ, lastZ, firstT, lastT
preprocess_membrane_channel(membrane)
weka_result1 = apply_weka_model(primary_model, membrane, tiling_factor )
delete_channel(weka_result1, 1)
weka_result2 = apply_weka_model(secondary_model, weka_result1, tiling_factor )
delete_channel(weka_result2, 1)
weka_result2.setCalibration(raw_image_calibration)
process_weka_result(weka_result2)
IJ.saveAs(weka_result2, "Tiff", output_dir + "/" + raw_image_title + "_all_fibers_binary")
eda_parameters = [minAr, maxAr, minPer, maxPer, minCir, maxCir, minRnd, maxRnd, minSol, maxSol, minFAR, maxFAR, minMinFer, maxMinFer]
raw.show() # EPA will not work if no image is shown
run_extended_particle_analyzer(weka_result2, eda_parameters)
# modify rois
rm.hide()
raw.hide()
enlarge_all_rois( enlarge, rm, raw_image_calibration.pixelWidth )
renumber_rois(rm)
save_all_rois( rm, output_dir + "/" + raw_image_title + "_all_fiber_rois.zip" )
# 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, output_dir + "/" + raw_image_title + "_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 )
rt = ResultsTable.getResultsTable("Results")
print rt.size()
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(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 )
total_execution_time_min = (time.time() - execution_start_time) / 60.0
IJ.log("total time in minutes: " + str(total_execution_time_min))
IJ.log( "~~ all done ~~" )
IJ.selectWindow("Log")
IJ.saveAs("Text", str(output_dir + "/" + raw_image_title + "_all_fibers_Log"))
if close_raw == True:
raw.close()
\ No newline at end of file
# ─── Main Code ────────────────────────────────────────────────────────────────
if __name__ == "__main__":
execution_start_time = time.time()
IJ.log("\\Clear")
misc.timed_log("Script starting")
setup_defined_ij(rm, rt)
file_list = pathtools.listdir_matching(
src_dir.getPath(), filename_filter, fullpath=True
)
out_dir_info = pathtools.parse_path(output_dir)
for index, file in enumerate(file_list):
# 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"
)
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 it once since we'll use the same settings everytime
if 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)
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],
)
IJ.saveAs(
imp_result,
"Tiff",
os.path.join(output_dir, raw_image_title + "_all_fibers_binary"),
)
command.run(Labels2CompositeRois, True, "rm", rm, "imp", imp_result).get()
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")
)
# 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 + "_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)
rt = ResultsTable.getResultsTable("Results")
# print(rt.size())
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>
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
classification, filter them according to the morphometric gates and save the
- 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
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 as well to choose & apply the morphometric gates.
- 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
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
corresponding image.
......@@ -32,14 +32,14 @@ Original code: <https://github.com/Hyojung-Choo/Myosoft/tree/Myosoft-hub>
information of a MHC staining channel.
- 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
its corresponding image.
- Includes identification of double and triple positive combinations.
- 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.
- Allows to manually select measurement parameters and the measurement channel.
......