4
stars
117
commits
Jupyter Notebook
primary language
Sep 11, 2026
updated
Patients living with neurofibromatosis type 1 (NF1) often develop neurofibromas (nfs), which are complex and benign tumors that can transform into malignant disease. However, there are only two FDA-approved therapies for NF1-associated inoperable plexiform neurofibromas (pnfs): mirdametinib and selumetinib. Thus, we urgently need more therapeutic options for neurofibromas.
To address this, we have developed a 3D patient-derived tumor organoid model of NF1. We developed a modified 3D cell painting protocol to generate high-content imaging data from these organoids. This repository contains the code and documentation for a comprehensive analysis pipeline to process and analyze these 3D organoid models of NF1.
This pipeline was developed specifically for the NF1 3D organoid dataset, but the modular design allows for adaptation to other 3D microscopy datasets.
| 405 channel | 488 channel | 555 channel | 640 channel |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
| Organoid | Nuclei | Cell | Cytoplasm |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
The pipeline follows a hierarchical processing structure:
We present a full workflow to profile 3-dimensional images of organoids. Our end-to-end system processes raw 3D microscopy data through illumination correction, segmentation, feature extraction, quality control, and image-based profiling.
Execution strategy:
flowchart TD
A[Raw microscopy images] --> B[Stage 0: data preprocessing]
B --> C1[Z-stack creation]
B --> C2["(optional) deconvolution"]
C1 --> D[Deconvolved z-stack images]
C2 --> D
D --> E[Stage 1: image quality control]
E --> F1[Blur detection]
E --> F2[Saturation detection]
F1 --> G[QC flags & reports]
F2 --> G
G --> H[Stage 2: Image segmentation]
H --> I1[Nuclei segmentation]
H --> I2[Cell segmentation]
H --> I3[Organoid segmentation]
I1 --> J[Segmentation refinement]
I2 --> J
I3 --> J
J --> K[3D segmentation masks]
K --> L[Stage 3: feature extraction]
L --> M1[Area + size]
L --> M2[Intensity]
L --> M3[Texture]
L --> M4[Colocalization]
L --> M5[Neighbors]
L --> M6[Deep learning features]
M1 --> N[feature matrices]
M2 --> N
M3 --> N
M4 --> N
M5 --> N
M6 --> N
N --> O[Stage 4: image-based profiling]
style B fill:#40BA40,stroke:#000,stroke-width:2px
style E fill:#40BA40,stroke:#000,stroke-width:2px
style H fill:#40BA40,stroke:#000,stroke-width:2px
style L fill:#40BA40,stroke:#000,stroke-width:2px
style O fill:#40BA40,stroke:#000,stroke-width:2px
Directory: 0.preprocessing_data/
Purpose: Transform raw microscopy data into standardized 3D z-stack images ready for analysis.
Inputs:
Outputs:
data/{patient}/zstack_images/{well_fov}/{channel}.tifExecution:
# Example for a specific patient (NF0014)
cd 0.preprocessing_data
python scripts/1.make_zstack_and_copy_over.py --patient NF0014_T1
# Process for the CQ1 microscope
python scripts/1z.make_zstack_and_copy_over_CQ1.py --patient NF0014_T1
Directory: 1.image_quality_control/
Inputs:
Outputs:
data/{patient}/qc_flags.csvPurpose: Assess image quality and flag problematic well FOVs before segmentation.
Quality metrics:
Execution:
cd 1.image_quality_control
jupyter nbconvert --to notebook --execute notebooks/*.ipynb
Directory: 2.segment_images/
Purpose: Generate 3D masks for nuclei, cells, cytoplasm, and whole organoids. We refer to each mask category as a "compartment".
Execution:
cd 2.segment_images
sbatch grand_parent_segmentation.sh

flowchart TD
A[raw image] --> B[thresholding]
A --> C[butterworth low-pass filter]
B --> D1[globular & cluster]
B --> D2[small & dissociated]
B --> D3[elongated]
D1 --> E1[gaussian smoothing with sigma=2.5]
D2 --> E2[gaussian smoothing with sigma=3.0]
D3 --> E3[gaussian smoothing with sigma=4.0]
E1 --> F1[otsu's thresholding]
E2 --> F1
E3 --> F1
F1 --> I1[binary mask of organoid]
I1 --> J1[no dilation of the mask]
I1 --> J2[dilation ball radius=1]
I1 --> J3[dilation ball radius=10]
J1 --> H1[3D seeded watershed segmentation with nuclei masks as seeds]
J2 --> H1
J3 --> H1
C --> Z1[globular & cluster]
C --> Z2[small & dissociated]
C --> Z3[elongated]
Z1 --> Y1[no gaussian smoothing]
Z2 --> Y2[gaussian smoothing with sigma=1.0]
Z3 --> Y3[gaussian smoothing with sigma=1.0]
Y1 --> X1[sobel filter]
Y2 --> X1
Y3 --> X1
X1 --> W1[connectivity=1; compactness=1]
X1 --> W2[connectivity=1; compactness=0]
X1 --> W3[connectivity=0; compactness=0]
W1 --> H1
W2 --> H1
W3 --> H1
Organoid segmentation was performed using CellPose SAM on Gaussian-smoothed (sigma=10.0) AGP channel images with a size constraint of 200 pixel diameter.
Directory: 3.cellprofiling/
Purpose: Extract all morphology (hand-drawn) features (e.g., shape, intensity, texture, etc.) from segmented objects.
To maximize parallelization and processing speed, our featurization strategy follows a three-level hierarchical job submission structure.
Run_featurization_grandparent.sh)
Run_featurization_parent.sh)
Feature types: For more details on feature types and extraction methods, refer to the Features/ documentation.
We extract hand-drawn features across multiple categories (e.g., shape, intensity, texture) for each compartment and channel combination.
In addition, we extract deep learning-based features using the sammed3d model to capture complex morphological phenotypes that may not be described by hand-crafted features.
Sammed3d features are extracted as 384-dimensional embeddings per channel per object using the CLS token output from the vit encoder on the whole volume.
Additionally, we take a nucleocentric feature extraction approach where we extract features from a cropped volumes centered around each nucleus.
We use sammed3d to extract features from these nucleocentric volumes, and we z-maximally project the nucleocentric volumes to extract 2D features using morphem features.
morphem features are extracted as 384-dimensional embeddings per channel per object using the CLS token output from a separate vit encoder.
| Feature type | Description |
|---|---|
| Area + size | object size, area, perimeter, etc. |
| Colocalization | overlap of signals between channels (e.g., pearson correlation) |
| Intensity | mean, median, max, min, etc. intensity values per object |
| Granularity | "granularity of pixel intensities" |
| Neighbors | number of neighboring objects, distance to neighbors |
| Texture | haralick features, gabor filters, etc. |
| Deep learning features | sammed3d vit-based embeddings |
| morphem features | vit-based embeddings from nucleocentric 2D projections |
Directory: 4.processing_image_based_profiles/
Purpose: Merge, normalize, and aggregate features across wells and patients, preparing data for downstream analyses.
Inputs:
Outputs:
Profiles are generated at multiple levels with profiles being generated for each profile type:
Profile types:
With the combination of 6 profile types and 4 profile levels, we generate a total of 24 different profile outputs.
Each profile output is saved as a parquet file in the data/{patient}/image_based_profiles/.
Three processing tracks run in parallel — hand-crafted morphology features, SAMMed3D deep learning embeddings, and morphem deep learning embeddings — each following a distinct path determined by how the features were extracted and which compartments they cover.
flowchart TD
A["<b>Feature parquets</b><br/>from Stage 3 (per well-FOV)"] --> B{Feature type}
B -->|Hand-crafted morphology| C["<b>Hand-crafted pipeline</b><br/>merge compartments → QC → normalize → select → aggregate"]
B -->|"SAMMed3D 3D embeddings<br/>SC · organoid · nucleocentric"| D["<b>SAMMed3D pipeline</b><br/>combine → normalize → select → aggregate"]
B -->|"morphem 2D embeddings<br/>nucleocentric only"| E["<b>morphem pipeline</b><br/>combine → normalize → select → aggregate"]
Traditional morphology features (shape, intensity, texture, colocalization, neighbors, granularity) extracted per object across four segmented compartments: Nuclei, Cell, Cytoplasm, and Organoid. Single-cell profiles are formed by joining Nuclei + Cell + Cytoplasm features into one row per cell. A nucleocentric representation is also included — see the box below.
Hand-crafted features require merging across compartments and a two-stage QC cascade (organoid first, then single-cell inheriting organoid flags) before normalization.
flowchart TD
classDef out fill:#f5f5f5,stroke:#aaa,stroke-dasharray:4 4,color:#444
subgraph merge["Merge"]
A["<b>Merge feature parquets</b><br/>one parquet per compartment × feature type<br/>DuckDB"]
A --> B["<b>Merge single cells</b><br/>join Nuclei + Cell + Cytoplasm + Nucleocentric<br/>into one row per cell"]
B --> C["<b>Organoid–cell relationships</b><br/>spatially assign cells to parent organoids<br/>Mahalanobis distance · shell classification"]
C -.-> oMerge[/"sc_related.parquet<br/>organoid_related.parquet"/]:::out
end
C --> D["<b>Combine profiles</b><br/>concat per-FOV parquets across patient<br/>drop brightfield channels · DuckDB union_by_name"]
D -.-> oCombine[/"sc.parquet · organoid.parquet"/]:::out
D --> E["<b>Annotate</b><br/>join platemap metadata (treatment · target · patient)<br/>pycytominer.annotate"]
E -.-> oAnnotate[/"sc_anno.parquet<br/>organoid_anno.parquet"/]:::out
subgraph qc["Quality control"]
E --> F["<b>Organoid QC</b><br/>flag NaN rows + size outliers via z-score<br/>cosmicqc.find_outliers"]
F -.-> oOrgQC[/"organoid_flagged_outliers.parquet"/]:::out
F --> G["<b>Single-cell QC</b><br/>flag NaN · inherit organoid flags · nucleus size outliers<br/>cosmicqc.find_outliers"]
G -.-> oSCQC[/"sc_flagged_outliers.parquet"/]:::out
end
G --> H["<b>Normalize</b><br/>MAD-robustize · ref: QC-passing DMSO rows<br/>pycytominer.normalize"]
H -.-> oNorm[/"sc_norm.parquet<br/>organoid_norm.parquet"/]:::out
H --> I["<b>Feature select</b><br/>drop-NA · blocklist · correlation · variance<br/>fit on DMSO + Staurosporine · apply to all treatments<br/>pycytominer.feature_select"]
I -.-> oFS[/"sc_fs.parquet<br/>organoid_fs.parquet"/]:::out
I --> J["<b>Aggregate</b><br/>median by well · median by treatment (consensus)<br/>pycytominer.aggregate"]
J -.-> oAgg[/"sc_agg_well_level.parquet<br/>sc_consensus.parquet"/]:::out
SAMMed3D is a 3D ViT-based model that extracts 384-dimensional embeddings from the CLS token of the encoder, applied to whole 3D object volumes per fluorescence channel. It covers three object types:
SAMMed3D features are pre-computed per object in Stage 3 and do not require compartment merging or QC.
flowchart TD
classDef out fill:#f5f5f5,stroke:#aaa,stroke-dasharray:4 4,color:#444
subgraph inputs["SAMMed3D inputs (from Stage 3)"]
A1["<b>Single-cell embeddings</b><br/>Nuclei · Cell · Cytoplasm volumes<br/>384-dim per channel per object"]
A2["<b>Organoid embeddings</b><br/>full organoid volumes<br/>384-dim per channel per object"]
A3["<b>Nucleocentric embeddings</b><br/>3D crops centered on each nucleus<br/>384-dim per channel per crop"]
end
A1 --> B["<b>Combine profiles</b><br/>concat per-FOV parquets across patient<br/>drop brightfield channels · DuckDB union_by_name"]
A2 --> B
A3 --> B
B -.-> oCombine[/"sammed_sc.parquet · sammed_organoid.parquet<br/>sammed_nucleocentric.parquet"/]:::out
B --> C["<b>Annotate</b><br/>join platemap metadata (treatment · target · patient)<br/>pycytominer.annotate"]
C -.-> oAnnotate[/"sammed_sc_anno.parquet · sammed_organoid_anno.parquet<br/>nucleocentric_sammed_anno.parquet"/]:::out
C --> D["<b>Normalize</b><br/>MAD-robustize · ref: all DMSO rows<br/>pycytominer.normalize"]
D -.-> oNorm[/"sammed_sc_norm.parquet · sammed_organoid_norm.parquet<br/>sammed_nucleocentric_norm.parquet"/]:::out
D --> E["<b>Feature select</b><br/>drop-NA · blocklist · correlation · variance<br/>fit on DMSO + Staurosporine · apply to all treatments<br/>pycytominer.feature_select"]
E -.-> oFS[/"sammed_sc_fs.parquet · sammed_organoid_fs.parquet<br/>sammed_nucleocentric_fs.parquet"/]:::out
E --> F["<b>Aggregate</b><br/>median by well · median by treatment (consensus)<br/>pycytominer.aggregate"]
F -.-> oAgg[/"sammed_sc_agg_well_level.parquet · sammed_organoid_agg_well_level.parquet<br/>sammed_nucleocentric_agg_well_level.parquet<br/>sammed_sc_consensus.parquet · sammed_organoid_consensus.parquet<br/>sammed_nucleocentric_consensus.parquet"/]:::out
morphem is a 2D ViT-based model that extracts 384-dimensional embeddings from the CLS token of the encoder. It is applied exclusively to nucleocentric volumes — but unlike SAMMed3D, it operates on 2D maximum-intensity projections of those volume crops rather than the full 3D crop. This collapses the z-axis and captures a 2D summary of the nuclear microenvironment per channel. We named these features "CHAMMI75" features in our internal pipeline, but we refer to them as "morphem features" in this repository for clarity and consistency with the model name.
morphem features are pre-computed per nucleus in Stage 3 and do not require compartment merging or QC.
flowchart TD
classDef out fill:#f5f5f5,stroke:#aaa,stroke-dasharray:4 4,color:#444
A["<b>Nucleocentric embeddings</b><br/>2D max projections of nucleus-centered crops<br/>384-dim per channel per nucleus · morphem"]
A --> B["<b>Combine profiles</b><br/>concat per-FOV parquets across patient<br/>drop brightfield channels · DuckDB union_by_name"]
B -.-> oCombine[/"nucleocentric_chammi.parquet"/]:::out
B --> C["<b>Annotate</b><br/>join platemap metadata (treatment · target · patient)<br/>pycytominer.annotate"]
C -.-> oAnnotate[/"nucleocentric_morphem_anno.parquet"/]:::out
C --> D["<b>Normalize</b><br/>MAD-robustize · ref: all DMSO rows<br/>pycytominer.normalize"]
D -.-> oNorm[/"nucleocentric_morphem_norm.parquet"/]:::out
D --> E["<b>Feature select</b><br/>drop-NA · blocklist · correlation · variance<br/>fit on DMSO + Staurosporine · apply to all treatments<br/>pycytominer.feature_select"]
E -.-> oFS[/"nucleocentric_morphem_fs.parquet"/]:::out
E --> F["<b>Aggregate</b><br/>median by well · median by treatment (consensus)<br/>pycytominer.aggregate"]
F -.-> oAgg[/"nucleocentric_morphem_agg_well_level.parquet<br/>nucleocentric_morphem_consensus.parquet"/]:::out
Execution:
cd 4.processing_image_based_profiles
sbatch merge_features_grand_parent.sh
The pipeline expects data organized in this hierarchy:
NF1_3D_organoid_profiling_pipeline/
├── data/
│ ├── patient_IDs.txt
│ ├── NF0014_T1/
│ │ ├── zstack_images/
│ │ │ ├── C4-2/
│ │ │ │ ├── 405.Tif # DNA channel
│ │ │ │ ├── 488.Tif # ER channel
│ │ │ │ ├── 555.Tif # golgi channel
│ │ │ │ ├── 568.Tif # F-actin channel
│ │ │ │ └── 640.Tif # mito channel
│ │ │ └── ... (Other well fovs)
│ │ ├── segmentation_masks/
│ │ │ ├── C4-2/
│ │ │ │ ├── Organoid_mask.tif
│ │ │ │ ├── Nuclei_mask.tif
│ │ │ │ ├── Cell_mask.tif
│ │ │ │ └── Cytoplasm_derived.tif
│ │ │ └── ... (Other well fovs)
│ │ ├── extracted_features/
│ │ │ ├── C4-2/
│ │ │ │ ├── AreaSizeShape_Nuclei_DNA_CPU.parquet
│ │ │ │ ├── Intensity_Cell_488_GPU.parquet
│ │ │ │ ├── Texture_Cytoplasm_640_CPU.parquet
│ │ │ │ └── ... (125-189 Files)
│ │ │ └── ...
│ │ ├── image_based_profiles/
│ │ │ ├── 0.converted_profiles/
│ │ │ │ ├── C4-2/
│ │ │ │ │ ├── sc_related.parquet
│ │ │ │ │ └── organoid_related.parquet
│ │ │ ├── 1.combined_profiles/
│ │ │ │ ├── sc.parquet
│ │ │ │ └── organoid.parquet
│ │ │ ├── 2.annotated_profiles/
│ │ │ ├── 3.normalized_profiles/
│ │ │ ├── 4.feature_selected_profiles/
│ │ │ └── 5.aggregated_profiles/
│ │ └── qc_flags.parquet
│ ├── NF0016_T1/
│ │ └── ... (Same structure)
│ └── all_patient_profiles/
│ ├── sc_consensus.parquet
│ ├── organoid_consensus.parquet
│ ├── well_aggregated.parquet
│ └── patient_aggregated.parquet
├── models/
│ └── sam-med3d-turbo.pth
├── environments/
│ ├── GFF_preprocessing.yml
│ ├── GFF_segmentation.yml
│ └── ... (Conda environments)
└── ... (Code directories 0-6)
Z-stack images:
{channel}.tif where channel ∈ {405, 488, 555, 640}Segmentation masks:
{compartment}_mask.tifFeature files:
{feature}_{compartment}_{channel}_{processor}_features.parquetIntensity_Nuclei_405_GPU_features.parquetProfile files:
{level}_{aggregation}.parquetsc_consensus.parquetThe pipeline processes four fluorescent imaging channels: Note that while these are the channels we have used for our NF1 3D organoid dataset, the pipeline is designed to be flexible and adaptable to other channel configurations as needed. the channel information is stored in the metadata and used throughout the pipeline to ensure correct processing and feature extraction for each channel.
| Name | fluorophore | ex(nm) | em(nm) | dichroic | target | organelle |
|---|---|---|---|---|---|---|
| 405 | Hoechst 33342 | 361 | 486 | 405 | DNA | nucleus |
| 488 | Cona alexa fluor 488 | 495 | 519 | 488 | ER | ER |
| 555 | WGA alexa fluor 555 | 555 | 580 | 555 | membranes | golgi/plasma memb |
| 640 | Mitotracker deep red | 644 | 665 | 640 | mitochondria | mitochondria |
Imaging parameters:
We recommend using uv, mamba or conda to create the required environments. we have written a makefile to help with conda environment creation and management.
cd environments || exit
make --always-make
cd .. || exit
For uv users, you can also create the environments with:
source uv_setup.sh
The utilities under utils/src/ are now structured as installable packages.
For local development, install them in editable mode:
cd utils
pip install -e .
Note that the utilites should be imported into compute environments. see the
Environments module for installing the utils. there is a makefile in the
Environments module that installs the environments with utils.
Storage requirements:
Number of fovs per well varies between 7-25 with typically 60 wells per patient. Per patient well fovs can range from 420 to 1500 depending on the experiment design.
Storage estimates (per patient):
| Well fovs | storage (TB) |
|---|---|
| 400 | ~0.4-0.8 |
| 500 | ~0.5-1.0 |
| 1000 | ~1.0-2.0 |
| 1500 | ~1.5-3.0 |
The raw and processed imaging data are not quite publicly available at this time. We will have data available at some point on the NF data portal via Synapse.
For more information on the NF1 organoid profiling project, please see the following associated repositories:
If you use this pipeline in your research, please cite it using the metadata in CITATION.cff.
Jupyter Notebook
96.4%
Python
3.1%
4
stars
117
commits
Jupyter Notebook
primary language
Sep 11, 2026
updated
Patients living with neurofibromatosis type 1 (NF1) often develop neurofibromas (nfs), which are complex and benign tumors that can transform into malignant disease. However, there are only two FDA-approved therapies for NF1-associated inoperable plexiform neurofibromas (pnfs): mirdametinib and selumetinib. Thus, we urgently need more therapeutic options for neurofibromas.
To address this, we have developed a 3D patient-derived tumor organoid model of NF1. We developed a modified 3D cell painting protocol to generate high-content imaging data from these organoids. This repository contains the code and documentation for a comprehensive analysis pipeline to process and analyze these 3D organoid models of NF1.
This pipeline was developed specifically for the NF1 3D organoid dataset, but the modular design allows for adaptation to other 3D microscopy datasets.
| 405 channel | 488 channel | 555 channel | 640 channel |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
| Organoid | Nuclei | Cell | Cytoplasm |
|---|---|---|---|
![]() | ![]() | ![]() | ![]() |
The pipeline follows a hierarchical processing structure:
We present a full workflow to profile 3-dimensional images of organoids. Our end-to-end system processes raw 3D microscopy data through illumination correction, segmentation, feature extraction, quality control, and image-based profiling.
Execution strategy:
flowchart TD
A[Raw microscopy images] --> B[Stage 0: data preprocessing]
B --> C1[Z-stack creation]
B --> C2["(optional) deconvolution"]
C1 --> D[Deconvolved z-stack images]
C2 --> D
D --> E[Stage 1: image quality control]
E --> F1[Blur detection]
E --> F2[Saturation detection]
F1 --> G[QC flags & reports]
F2 --> G
G --> H[Stage 2: Image segmentation]
H --> I1[Nuclei segmentation]
H --> I2[Cell segmentation]
H --> I3[Organoid segmentation]
I1 --> J[Segmentation refinement]
I2 --> J
I3 --> J
J --> K[3D segmentation masks]
K --> L[Stage 3: feature extraction]
L --> M1[Area + size]
L --> M2[Intensity]
L --> M3[Texture]
L --> M4[Colocalization]
L --> M5[Neighbors]
L --> M6[Deep learning features]
M1 --> N[feature matrices]
M2 --> N
M3 --> N
M4 --> N
M5 --> N
M6 --> N
N --> O[Stage 4: image-based profiling]
style B fill:#40BA40,stroke:#000,stroke-width:2px
style E fill:#40BA40,stroke:#000,stroke-width:2px
style H fill:#40BA40,stroke:#000,stroke-width:2px
style L fill:#40BA40,stroke:#000,stroke-width:2px
style O fill:#40BA40,stroke:#000,stroke-width:2px
Directory: 0.preprocessing_data/
Purpose: Transform raw microscopy data into standardized 3D z-stack images ready for analysis.
Inputs:
Outputs:
data/{patient}/zstack_images/{well_fov}/{channel}.tifExecution:
# Example for a specific patient (NF0014)
cd 0.preprocessing_data
python scripts/1.make_zstack_and_copy_over.py --patient NF0014_T1
# Process for the CQ1 microscope
python scripts/1z.make_zstack_and_copy_over_CQ1.py --patient NF0014_T1
Directory: 1.image_quality_control/
Inputs:
Outputs:
data/{patient}/qc_flags.csvPurpose: Assess image quality and flag problematic well FOVs before segmentation.
Quality metrics:
Execution:
cd 1.image_quality_control
jupyter nbconvert --to notebook --execute notebooks/*.ipynb
Directory: 2.segment_images/
Purpose: Generate 3D masks for nuclei, cells, cytoplasm, and whole organoids. We refer to each mask category as a "compartment".
Execution:
cd 2.segment_images
sbatch grand_parent_segmentation.sh

flowchart TD
A[raw image] --> B[thresholding]
A --> C[butterworth low-pass filter]
B --> D1[globular & cluster]
B --> D2[small & dissociated]
B --> D3[elongated]
D1 --> E1[gaussian smoothing with sigma=2.5]
D2 --> E2[gaussian smoothing with sigma=3.0]
D3 --> E3[gaussian smoothing with sigma=4.0]
E1 --> F1[otsu's thresholding]
E2 --> F1
E3 --> F1
F1 --> I1[binary mask of organoid]
I1 --> J1[no dilation of the mask]
I1 --> J2[dilation ball radius=1]
I1 --> J3[dilation ball radius=10]
J1 --> H1[3D seeded watershed segmentation with nuclei masks as seeds]
J2 --> H1
J3 --> H1
C --> Z1[globular & cluster]
C --> Z2[small & dissociated]
C --> Z3[elongated]
Z1 --> Y1[no gaussian smoothing]
Z2 --> Y2[gaussian smoothing with sigma=1.0]
Z3 --> Y3[gaussian smoothing with sigma=1.0]
Y1 --> X1[sobel filter]
Y2 --> X1
Y3 --> X1
X1 --> W1[connectivity=1; compactness=1]
X1 --> W2[connectivity=1; compactness=0]
X1 --> W3[connectivity=0; compactness=0]
W1 --> H1
W2 --> H1
W3 --> H1
Organoid segmentation was performed using CellPose SAM on Gaussian-smoothed (sigma=10.0) AGP channel images with a size constraint of 200 pixel diameter.
Directory: 3.cellprofiling/
Purpose: Extract all morphology (hand-drawn) features (e.g., shape, intensity, texture, etc.) from segmented objects.
To maximize parallelization and processing speed, our featurization strategy follows a three-level hierarchical job submission structure.
Run_featurization_grandparent.sh)
Run_featurization_parent.sh)
Feature types: For more details on feature types and extraction methods, refer to the Features/ documentation.
We extract hand-drawn features across multiple categories (e.g., shape, intensity, texture) for each compartment and channel combination.
In addition, we extract deep learning-based features using the sammed3d model to capture complex morphological phenotypes that may not be described by hand-crafted features.
Sammed3d features are extracted as 384-dimensional embeddings per channel per object using the CLS token output from the vit encoder on the whole volume.
Additionally, we take a nucleocentric feature extraction approach where we extract features from a cropped volumes centered around each nucleus.
We use sammed3d to extract features from these nucleocentric volumes, and we z-maximally project the nucleocentric volumes to extract 2D features using morphem features.
morphem features are extracted as 384-dimensional embeddings per channel per object using the CLS token output from a separate vit encoder.
| Feature type | Description |
|---|---|
| Area + size | object size, area, perimeter, etc. |
| Colocalization | overlap of signals between channels (e.g., pearson correlation) |
| Intensity | mean, median, max, min, etc. intensity values per object |
| Granularity | "granularity of pixel intensities" |
| Neighbors | number of neighboring objects, distance to neighbors |
| Texture | haralick features, gabor filters, etc. |
| Deep learning features | sammed3d vit-based embeddings |
| morphem features | vit-based embeddings from nucleocentric 2D projections |
Directory: 4.processing_image_based_profiles/
Purpose: Merge, normalize, and aggregate features across wells and patients, preparing data for downstream analyses.
Inputs:
Outputs:
Profiles are generated at multiple levels with profiles being generated for each profile type:
Profile types:
With the combination of 6 profile types and 4 profile levels, we generate a total of 24 different profile outputs.
Each profile output is saved as a parquet file in the data/{patient}/image_based_profiles/.
Three processing tracks run in parallel — hand-crafted morphology features, SAMMed3D deep learning embeddings, and morphem deep learning embeddings — each following a distinct path determined by how the features were extracted and which compartments they cover.
flowchart TD
A["<b>Feature parquets</b><br/>from Stage 3 (per well-FOV)"] --> B{Feature type}
B -->|Hand-crafted morphology| C["<b>Hand-crafted pipeline</b><br/>merge compartments → QC → normalize → select → aggregate"]
B -->|"SAMMed3D 3D embeddings<br/>SC · organoid · nucleocentric"| D["<b>SAMMed3D pipeline</b><br/>combine → normalize → select → aggregate"]
B -->|"morphem 2D embeddings<br/>nucleocentric only"| E["<b>morphem pipeline</b><br/>combine → normalize → select → aggregate"]
Traditional morphology features (shape, intensity, texture, colocalization, neighbors, granularity) extracted per object across four segmented compartments: Nuclei, Cell, Cytoplasm, and Organoid. Single-cell profiles are formed by joining Nuclei + Cell + Cytoplasm features into one row per cell. A nucleocentric representation is also included — see the box below.
Hand-crafted features require merging across compartments and a two-stage QC cascade (organoid first, then single-cell inheriting organoid flags) before normalization.
flowchart TD
classDef out fill:#f5f5f5,stroke:#aaa,stroke-dasharray:4 4,color:#444
subgraph merge["Merge"]
A["<b>Merge feature parquets</b><br/>one parquet per compartment × feature type<br/>DuckDB"]
A --> B["<b>Merge single cells</b><br/>join Nuclei + Cell + Cytoplasm + Nucleocentric<br/>into one row per cell"]
B --> C["<b>Organoid–cell relationships</b><br/>spatially assign cells to parent organoids<br/>Mahalanobis distance · shell classification"]
C -.-> oMerge[/"sc_related.parquet<br/>organoid_related.parquet"/]:::out
end
C --> D["<b>Combine profiles</b><br/>concat per-FOV parquets across patient<br/>drop brightfield channels · DuckDB union_by_name"]
D -.-> oCombine[/"sc.parquet · organoid.parquet"/]:::out
D --> E["<b>Annotate</b><br/>join platemap metadata (treatment · target · patient)<br/>pycytominer.annotate"]
E -.-> oAnnotate[/"sc_anno.parquet<br/>organoid_anno.parquet"/]:::out
subgraph qc["Quality control"]
E --> F["<b>Organoid QC</b><br/>flag NaN rows + size outliers via z-score<br/>cosmicqc.find_outliers"]
F -.-> oOrgQC[/"organoid_flagged_outliers.parquet"/]:::out
F --> G["<b>Single-cell QC</b><br/>flag NaN · inherit organoid flags · nucleus size outliers<br/>cosmicqc.find_outliers"]
G -.-> oSCQC[/"sc_flagged_outliers.parquet"/]:::out
end
G --> H["<b>Normalize</b><br/>MAD-robustize · ref: QC-passing DMSO rows<br/>pycytominer.normalize"]
H -.-> oNorm[/"sc_norm.parquet<br/>organoid_norm.parquet"/]:::out
H --> I["<b>Feature select</b><br/>drop-NA · blocklist · correlation · variance<br/>fit on DMSO + Staurosporine · apply to all treatments<br/>pycytominer.feature_select"]
I -.-> oFS[/"sc_fs.parquet<br/>organoid_fs.parquet"/]:::out
I --> J["<b>Aggregate</b><br/>median by well · median by treatment (consensus)<br/>pycytominer.aggregate"]
J -.-> oAgg[/"sc_agg_well_level.parquet<br/>sc_consensus.parquet"/]:::out
SAMMed3D is a 3D ViT-based model that extracts 384-dimensional embeddings from the CLS token of the encoder, applied to whole 3D object volumes per fluorescence channel. It covers three object types:
SAMMed3D features are pre-computed per object in Stage 3 and do not require compartment merging or QC.
flowchart TD
classDef out fill:#f5f5f5,stroke:#aaa,stroke-dasharray:4 4,color:#444
subgraph inputs["SAMMed3D inputs (from Stage 3)"]
A1["<b>Single-cell embeddings</b><br/>Nuclei · Cell · Cytoplasm volumes<br/>384-dim per channel per object"]
A2["<b>Organoid embeddings</b><br/>full organoid volumes<br/>384-dim per channel per object"]
A3["<b>Nucleocentric embeddings</b><br/>3D crops centered on each nucleus<br/>384-dim per channel per crop"]
end
A1 --> B["<b>Combine profiles</b><br/>concat per-FOV parquets across patient<br/>drop brightfield channels · DuckDB union_by_name"]
A2 --> B
A3 --> B
B -.-> oCombine[/"sammed_sc.parquet · sammed_organoid.parquet<br/>sammed_nucleocentric.parquet"/]:::out
B --> C["<b>Annotate</b><br/>join platemap metadata (treatment · target · patient)<br/>pycytominer.annotate"]
C -.-> oAnnotate[/"sammed_sc_anno.parquet · sammed_organoid_anno.parquet<br/>nucleocentric_sammed_anno.parquet"/]:::out
C --> D["<b>Normalize</b><br/>MAD-robustize · ref: all DMSO rows<br/>pycytominer.normalize"]
D -.-> oNorm[/"sammed_sc_norm.parquet · sammed_organoid_norm.parquet<br/>sammed_nucleocentric_norm.parquet"/]:::out
D --> E["<b>Feature select</b><br/>drop-NA · blocklist · correlation · variance<br/>fit on DMSO + Staurosporine · apply to all treatments<br/>pycytominer.feature_select"]
E -.-> oFS[/"sammed_sc_fs.parquet · sammed_organoid_fs.parquet<br/>sammed_nucleocentric_fs.parquet"/]:::out
E --> F["<b>Aggregate</b><br/>median by well · median by treatment (consensus)<br/>pycytominer.aggregate"]
F -.-> oAgg[/"sammed_sc_agg_well_level.parquet · sammed_organoid_agg_well_level.parquet<br/>sammed_nucleocentric_agg_well_level.parquet<br/>sammed_sc_consensus.parquet · sammed_organoid_consensus.parquet<br/>sammed_nucleocentric_consensus.parquet"/]:::out
morphem is a 2D ViT-based model that extracts 384-dimensional embeddings from the CLS token of the encoder. It is applied exclusively to nucleocentric volumes — but unlike SAMMed3D, it operates on 2D maximum-intensity projections of those volume crops rather than the full 3D crop. This collapses the z-axis and captures a 2D summary of the nuclear microenvironment per channel. We named these features "CHAMMI75" features in our internal pipeline, but we refer to them as "morphem features" in this repository for clarity and consistency with the model name.
morphem features are pre-computed per nucleus in Stage 3 and do not require compartment merging or QC.
flowchart TD
classDef out fill:#f5f5f5,stroke:#aaa,stroke-dasharray:4 4,color:#444
A["<b>Nucleocentric embeddings</b><br/>2D max projections of nucleus-centered crops<br/>384-dim per channel per nucleus · morphem"]
A --> B["<b>Combine profiles</b><br/>concat per-FOV parquets across patient<br/>drop brightfield channels · DuckDB union_by_name"]
B -.-> oCombine[/"nucleocentric_chammi.parquet"/]:::out
B --> C["<b>Annotate</b><br/>join platemap metadata (treatment · target · patient)<br/>pycytominer.annotate"]
C -.-> oAnnotate[/"nucleocentric_morphem_anno.parquet"/]:::out
C --> D["<b>Normalize</b><br/>MAD-robustize · ref: all DMSO rows<br/>pycytominer.normalize"]
D -.-> oNorm[/"nucleocentric_morphem_norm.parquet"/]:::out
D --> E["<b>Feature select</b><br/>drop-NA · blocklist · correlation · variance<br/>fit on DMSO + Staurosporine · apply to all treatments<br/>pycytominer.feature_select"]
E -.-> oFS[/"nucleocentric_morphem_fs.parquet"/]:::out
E --> F["<b>Aggregate</b><br/>median by well · median by treatment (consensus)<br/>pycytominer.aggregate"]
F -.-> oAgg[/"nucleocentric_morphem_agg_well_level.parquet<br/>nucleocentric_morphem_consensus.parquet"/]:::out
Execution:
cd 4.processing_image_based_profiles
sbatch merge_features_grand_parent.sh
The pipeline expects data organized in this hierarchy:
NF1_3D_organoid_profiling_pipeline/
├── data/
│ ├── patient_IDs.txt
│ ├── NF0014_T1/
│ │ ├── zstack_images/
│ │ │ ├── C4-2/
│ │ │ │ ├── 405.Tif # DNA channel
│ │ │ │ ├── 488.Tif # ER channel
│ │ │ │ ├── 555.Tif # golgi channel
│ │ │ │ ├── 568.Tif # F-actin channel
│ │ │ │ └── 640.Tif # mito channel
│ │ │ └── ... (Other well fovs)
│ │ ├── segmentation_masks/
│ │ │ ├── C4-2/
│ │ │ │ ├── Organoid_mask.tif
│ │ │ │ ├── Nuclei_mask.tif
│ │ │ │ ├── Cell_mask.tif
│ │ │ │ └── Cytoplasm_derived.tif
│ │ │ └── ... (Other well fovs)
│ │ ├── extracted_features/
│ │ │ ├── C4-2/
│ │ │ │ ├── AreaSizeShape_Nuclei_DNA_CPU.parquet
│ │ │ │ ├── Intensity_Cell_488_GPU.parquet
│ │ │ │ ├── Texture_Cytoplasm_640_CPU.parquet
│ │ │ │ └── ... (125-189 Files)
│ │ │ └── ...
│ │ ├── image_based_profiles/
│ │ │ ├── 0.converted_profiles/
│ │ │ │ ├── C4-2/
│ │ │ │ │ ├── sc_related.parquet
│ │ │ │ │ └── organoid_related.parquet
│ │ │ ├── 1.combined_profiles/
│ │ │ │ ├── sc.parquet
│ │ │ │ └── organoid.parquet
│ │ │ ├── 2.annotated_profiles/
│ │ │ ├── 3.normalized_profiles/
│ │ │ ├── 4.feature_selected_profiles/
│ │ │ └── 5.aggregated_profiles/
│ │ └── qc_flags.parquet
│ ├── NF0016_T1/
│ │ └── ... (Same structure)
│ └── all_patient_profiles/
│ ├── sc_consensus.parquet
│ ├── organoid_consensus.parquet
│ ├── well_aggregated.parquet
│ └── patient_aggregated.parquet
├── models/
│ └── sam-med3d-turbo.pth
├── environments/
│ ├── GFF_preprocessing.yml
│ ├── GFF_segmentation.yml
│ └── ... (Conda environments)
└── ... (Code directories 0-6)
Z-stack images:
{channel}.tif where channel ∈ {405, 488, 555, 640}Segmentation masks:
{compartment}_mask.tifFeature files:
{feature}_{compartment}_{channel}_{processor}_features.parquetIntensity_Nuclei_405_GPU_features.parquetProfile files:
{level}_{aggregation}.parquetsc_consensus.parquetThe pipeline processes four fluorescent imaging channels: Note that while these are the channels we have used for our NF1 3D organoid dataset, the pipeline is designed to be flexible and adaptable to other channel configurations as needed. the channel information is stored in the metadata and used throughout the pipeline to ensure correct processing and feature extraction for each channel.
| Name | fluorophore | ex(nm) | em(nm) | dichroic | target | organelle |
|---|---|---|---|---|---|---|
| 405 | Hoechst 33342 | 361 | 486 | 405 | DNA | nucleus |
| 488 | Cona alexa fluor 488 | 495 | 519 | 488 | ER | ER |
| 555 | WGA alexa fluor 555 | 555 | 580 | 555 | membranes | golgi/plasma memb |
| 640 | Mitotracker deep red | 644 | 665 | 640 | mitochondria | mitochondria |
Imaging parameters:
We recommend using uv, mamba or conda to create the required environments. we have written a makefile to help with conda environment creation and management.
cd environments || exit
make --always-make
cd .. || exit
For uv users, you can also create the environments with:
source uv_setup.sh
The utilities under utils/src/ are now structured as installable packages.
For local development, install them in editable mode:
cd utils
pip install -e .
Note that the utilites should be imported into compute environments. see the
Environments module for installing the utils. there is a makefile in the
Environments module that installs the environments with utils.
Storage requirements:
Number of fovs per well varies between 7-25 with typically 60 wells per patient. Per patient well fovs can range from 420 to 1500 depending on the experiment design.
Storage estimates (per patient):
| Well fovs | storage (TB) |
|---|---|
| 400 | ~0.4-0.8 |
| 500 | ~0.5-1.0 |
| 1000 | ~1.0-2.0 |
| 1500 | ~1.5-3.0 |
The raw and processed imaging data are not quite publicly available at this time. We will have data available at some point on the NF data portal via Synapse.
For more information on the NF1 organoid profiling project, please see the following associated repositories:
If you use this pipeline in your research, please cite it using the metadata in CITATION.cff.
Jupyter Notebook
96.4%
Python
3.1%