diff --git a/1_identify_fibers.py b/1_identify_fibers.py
index f33076fda4f01cead2b932bec5c3b6dd76c9dfc6..186b49230ba18181cd03f60525ba4333e16e31b6 100755
--- a/1_identify_fibers.py
+++ b/1_identify_fibers.py
@@ -1,57 +1,78 @@
+# @ 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 ~~")
diff --git a/README.md b/README.md
index 81a4b80af5f90a525212f294b2eb855e6be3a186..5b106e1f781e1988516ad1adad9ca4a199793ee0 100644
--- a/README.md
+++ b/README.md
@@ -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.