mcmicroprep 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mcmicroprep-0.1.0/PKG-INFO +133 -0
- mcmicroprep-0.1.0/README.md +116 -0
- mcmicroprep-0.1.0/mcmicroprep/__init__.py +0 -0
- mcmicroprep-0.1.0/mcmicroprep/companion_script.py +105 -0
- mcmicroprep-0.1.0/mcmicroprep/core.py +40 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/__init__.py +0 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/__pycache__/__init__.cpython-312.pyc +0 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/__pycache__/base.cpython-312.pyc +0 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/__pycache__/olympus.cpython-312.pyc +0 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/__pycache__/rarecyte.cpython-312.pyc +0 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/base.py +18 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/olympus.py +47 -0
- mcmicroprep-0.1.0/mcmicroprep/microscopes/rarecyte.py +31 -0
- mcmicroprep-0.1.0/mcmicroprep/preparemcmicro.py +34 -0
- mcmicroprep-0.1.0/mcmicroprep/templates/common/base.config +28 -0
- mcmicroprep-0.1.0/mcmicroprep/templates/common/batch_submission.sh +74 -0
- mcmicroprep-0.1.0/mcmicroprep/templates/common/markers.csv +5 -0
- mcmicroprep-0.1.0/mcmicroprep/templates/common/mcmicro_template.sh +16 -0
- mcmicroprep-0.1.0/mcmicroprep/templates/params/olympus.yml +18 -0
- mcmicroprep-0.1.0/mcmicroprep/templates/params/rarecyte.yml +16 -0
- mcmicroprep-0.1.0/mcmicroprep/templates/params_base.yml +10 -0
- mcmicroprep-0.1.0/pyproject.toml +21 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: mcmicroprep
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary:
|
|
5
|
+
Author: Ajit Johnson Nirmal
|
|
6
|
+
Author-email: ajitjohnson.n@gmail.com
|
|
7
|
+
Requires-Python: >=3.10,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
11
|
+
Requires-Dist: lxml (>=6.0.0,<7.0.0)
|
|
12
|
+
Requires-Dist: ome-types (>=0.6.1,<0.7.0)
|
|
13
|
+
Requires-Dist: pandas (>=2.3.1,<3.0.0)
|
|
14
|
+
Requires-Dist: pydantic (>=2.11.7,<3.0.0)
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# ๐งช mcmicroprep ๐
|
|
18
|
+
|
|
19
|
+
A **command-line tool** for preparing multiplexed imaging datasets (๐ฆ Olympus, ๐ฉธ RareCyte) for the MCMICRO Nextflow pipeline.
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
## ๐ ๏ธ Installation
|
|
23
|
+
|
|
24
|
+
1. **Prerequisites**
|
|
25
|
+
|
|
26
|
+
- Conda or Miniconda installed ๐
|
|
27
|
+
- Python **3.10+** environment ๐
|
|
28
|
+
- SLURM & Nextflow (`labsyspharm/mcmicro`) on your `$PATH`
|
|
29
|
+
|
|
30
|
+
2. **Create Conda env**
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
conda create -n mcmicroprep python=3.12
|
|
34
|
+
conda activate mcmicroprep
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
3. **Install package**
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install mcmicroprep
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## ๐ Expected Dataset Structure
|
|
44
|
+
|
|
45
|
+
Your dataset root should contain one subdirectory per slide. Structures vary by vendor:
|
|
46
|
+
|
|
47
|
+
### ๐ฆ Olympus
|
|
48
|
+
|
|
49
|
+
Each *slide directory* must contain at least one `*_frames/` folder โ-- this is the minimum required structure. Additional files or folders may be present and do not need to be removed.
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
.DATASET FOLDER
|
|
53
|
+
โโโ slide1/
|
|
54
|
+
โ โโโ image1_frames/
|
|
55
|
+
โ โโโ image2_frames/
|
|
56
|
+
โโโ slide2/
|
|
57
|
+
โโโ slideN/
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
After running for **Olympus**: each slide/image folder would be as follows
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
slide1/
|
|
64
|
+
โโโ raw/ # image1_frames/, image2_frames/
|
|
65
|
+
โโโ misc_files/ # JSON, logs
|
|
66
|
+
โโโ batch_submission.sh # pipeline wrapper
|
|
67
|
+
โโโ mcmicro_template.sh # Nextflow template
|
|
68
|
+
โโโ base.config
|
|
69
|
+
โโโ markers.csv
|
|
70
|
+
โโโ params.yml
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### ๐ฉธ RareCyte
|
|
74
|
+
|
|
75
|
+
Slide dirs may contain `*.rcpnl` at any depth: โ-- this is the minimum required structure. Additional files or folders may be present and do not need to be removed.
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
/path/to/dataset/
|
|
79
|
+
โโโ slide1/
|
|
80
|
+
โ โโโ img001.rcpnl
|
|
81
|
+
โ โโโ subA/img002.rcpnl
|
|
82
|
+
โ โโโ other files
|
|
83
|
+
โโโ slideN/
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
After running for **RareCyte**:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
slide1/
|
|
90
|
+
โโโ raw/ # all .rcpnl files
|
|
91
|
+
โ โโโ img001.rcpnl
|
|
92
|
+
โ โโโ img002.rcpnl
|
|
93
|
+
โโโ misc_files/ # CSV, text
|
|
94
|
+
โโโ batch_submission.sh
|
|
95
|
+
โโโ mcmicro_template.sh
|
|
96
|
+
โโโ base.config
|
|
97
|
+
โโโ markers.csv
|
|
98
|
+
โโโ params.yml
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## ๐ Usage
|
|
102
|
+
|
|
103
|
+
> **Note**: Configured for the HMS O2 cluster (SLURM). Generalize by editing SLURM directives in `templates/common/`.
|
|
104
|
+
|
|
105
|
+
### ๐ฆ Olympus
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
preparemcmicro \
|
|
109
|
+
--microscope olympus \
|
|
110
|
+
--image-root /path/to/dataset
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### ๐ฉธ RareCyte
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
preparemcmicro \
|
|
117
|
+
--microscope rarecyte \
|
|
118
|
+
--image-root /path/to/dataset
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## ๐ ๏ธ Next Steps for Users
|
|
122
|
+
|
|
123
|
+
1. โ๏ธ **Edit **`` in each slide directory to include your experiment-specific cycle-to-marker mappings.
|
|
124
|
+
2. ๐ค **Upload** the entire processed dataset folder to the O2 cluster if you ran this locally.
|
|
125
|
+
3. ๐ **Start the job** on O2:
|
|
126
|
+
```bash
|
|
127
|
+
cd /n/scratch/users/USERNAME/<DATASET FOLDER>
|
|
128
|
+
bash batch_submission.sh --dataset_path /n/scratch/users/USERNAME/<DATASET FOLDER>
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Happy processing! ๐ฌ
|
|
132
|
+
|
|
133
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# ๐งช mcmicroprep ๐
|
|
2
|
+
|
|
3
|
+
A **command-line tool** for preparing multiplexed imaging datasets (๐ฆ Olympus, ๐ฉธ RareCyte) for the MCMICRO Nextflow pipeline.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## ๐ ๏ธ Installation
|
|
7
|
+
|
|
8
|
+
1. **Prerequisites**
|
|
9
|
+
|
|
10
|
+
- Conda or Miniconda installed ๐
|
|
11
|
+
- Python **3.10+** environment ๐
|
|
12
|
+
- SLURM & Nextflow (`labsyspharm/mcmicro`) on your `$PATH`
|
|
13
|
+
|
|
14
|
+
2. **Create Conda env**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
conda create -n mcmicroprep python=3.12
|
|
18
|
+
conda activate mcmicroprep
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
3. **Install package**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install mcmicroprep
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## ๐ Expected Dataset Structure
|
|
28
|
+
|
|
29
|
+
Your dataset root should contain one subdirectory per slide. Structures vary by vendor:
|
|
30
|
+
|
|
31
|
+
### ๐ฆ Olympus
|
|
32
|
+
|
|
33
|
+
Each *slide directory* must contain at least one `*_frames/` folder โ-- this is the minimum required structure. Additional files or folders may be present and do not need to be removed.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
.DATASET FOLDER
|
|
37
|
+
โโโ slide1/
|
|
38
|
+
โ โโโ image1_frames/
|
|
39
|
+
โ โโโ image2_frames/
|
|
40
|
+
โโโ slide2/
|
|
41
|
+
โโโ slideN/
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
After running for **Olympus**: each slide/image folder would be as follows
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
slide1/
|
|
48
|
+
โโโ raw/ # image1_frames/, image2_frames/
|
|
49
|
+
โโโ misc_files/ # JSON, logs
|
|
50
|
+
โโโ batch_submission.sh # pipeline wrapper
|
|
51
|
+
โโโ mcmicro_template.sh # Nextflow template
|
|
52
|
+
โโโ base.config
|
|
53
|
+
โโโ markers.csv
|
|
54
|
+
โโโ params.yml
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### ๐ฉธ RareCyte
|
|
58
|
+
|
|
59
|
+
Slide dirs may contain `*.rcpnl` at any depth: โ-- this is the minimum required structure. Additional files or folders may be present and do not need to be removed.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
/path/to/dataset/
|
|
63
|
+
โโโ slide1/
|
|
64
|
+
โ โโโ img001.rcpnl
|
|
65
|
+
โ โโโ subA/img002.rcpnl
|
|
66
|
+
โ โโโ other files
|
|
67
|
+
โโโ slideN/
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
After running for **RareCyte**:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
slide1/
|
|
74
|
+
โโโ raw/ # all .rcpnl files
|
|
75
|
+
โ โโโ img001.rcpnl
|
|
76
|
+
โ โโโ img002.rcpnl
|
|
77
|
+
โโโ misc_files/ # CSV, text
|
|
78
|
+
โโโ batch_submission.sh
|
|
79
|
+
โโโ mcmicro_template.sh
|
|
80
|
+
โโโ base.config
|
|
81
|
+
โโโ markers.csv
|
|
82
|
+
โโโ params.yml
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## ๐ Usage
|
|
86
|
+
|
|
87
|
+
> **Note**: Configured for the HMS O2 cluster (SLURM). Generalize by editing SLURM directives in `templates/common/`.
|
|
88
|
+
|
|
89
|
+
### ๐ฆ Olympus
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
preparemcmicro \
|
|
93
|
+
--microscope olympus \
|
|
94
|
+
--image-root /path/to/dataset
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### ๐ฉธ RareCyte
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
preparemcmicro \
|
|
101
|
+
--microscope rarecyte \
|
|
102
|
+
--image-root /path/to/dataset
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## ๐ ๏ธ Next Steps for Users
|
|
106
|
+
|
|
107
|
+
1. โ๏ธ **Edit **`` in each slide directory to include your experiment-specific cycle-to-marker mappings.
|
|
108
|
+
2. ๐ค **Upload** the entire processed dataset folder to the O2 cluster if you ran this locally.
|
|
109
|
+
3. ๐ **Start the job** on O2:
|
|
110
|
+
```bash
|
|
111
|
+
cd /n/scratch/users/USERNAME/<DATASET FOLDER>
|
|
112
|
+
bash batch_submission.sh --dataset_path /n/scratch/users/USERNAME/<DATASET FOLDER>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Happy processing! ๐ฌ
|
|
116
|
+
|
|
File without changes
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# %%
|
|
2
|
+
import dataclasses
|
|
3
|
+
import lxml.etree
|
|
4
|
+
import ome_types
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import pathlib
|
|
7
|
+
import pydantic
|
|
8
|
+
import re
|
|
9
|
+
import sys
|
|
10
|
+
import uuid
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# %%
|
|
14
|
+
@pydantic.validate_arguments
|
|
15
|
+
@dataclasses.dataclass
|
|
16
|
+
class AlignInfo:
|
|
17
|
+
version: str
|
|
18
|
+
unknown1: str
|
|
19
|
+
unknown2: str
|
|
20
|
+
unknown3: str
|
|
21
|
+
unknown4: str
|
|
22
|
+
pixel_size_x: float
|
|
23
|
+
pixel_size_y: float
|
|
24
|
+
tile_size_x: int
|
|
25
|
+
tile_size_y: int
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# in_path = pathlib.Path(sys.argv[1])
|
|
29
|
+
# align_info_raw = re.split(r"\s+", (in_path / "AlignInfo").read_text().rstrip())
|
|
30
|
+
# replaced by ajit
|
|
31
|
+
def generate_companion_ome(in_path):
|
|
32
|
+
in_path = pathlib.Path(in_path)
|
|
33
|
+
align_info_raw = re.split(r"\s+", (in_path / "AlignInfo").read_text().rstrip())
|
|
34
|
+
assert align_info_raw[0] == "SAlignInfo2"
|
|
35
|
+
align_info = AlignInfo(*align_info_raw)
|
|
36
|
+
|
|
37
|
+
tree = lxml.etree.parse(in_path / "ScanSpace")
|
|
38
|
+
points = pd.read_xml(in_path / "ScanSpace", xpath="./vPoints/point")
|
|
39
|
+
# vInvSrtPrm maps point indices to field indices (.tif numbers).
|
|
40
|
+
points["field"] = [int(index.text) for index in tree.find("vInvSrtPrm")]
|
|
41
|
+
points = points.sort_values("field")
|
|
42
|
+
num_channels = int(tree.find("vOMs").attrib["n"])
|
|
43
|
+
channel_indices = list(range(num_channels))
|
|
44
|
+
|
|
45
|
+
ome = ome_types.OME()
|
|
46
|
+
field_groups = points.groupby("iAI", sort=False)
|
|
47
|
+
for _, plane_data in field_groups:
|
|
48
|
+
# Ensure we have all channels and they are already sorted.
|
|
49
|
+
assert list(plane_data.iOM) == channel_indices
|
|
50
|
+
channels = []
|
|
51
|
+
planes = []
|
|
52
|
+
tiff_data_blocks = []
|
|
53
|
+
for p in plane_data.itertuples():
|
|
54
|
+
channels.append(ome_types.model.Channel())
|
|
55
|
+
planes.append(
|
|
56
|
+
ome_types.model.Plane(
|
|
57
|
+
position_x=p.fStgX,
|
|
58
|
+
position_x_unit="ยตm",
|
|
59
|
+
position_y=p.fStgY,
|
|
60
|
+
position_y_unit="ยตm",
|
|
61
|
+
the_z=0,
|
|
62
|
+
the_t=0,
|
|
63
|
+
the_c=p.iOM,
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
tiff_data_blocks.append(
|
|
67
|
+
ome_types.model.TiffData(
|
|
68
|
+
uuid=ome_types.model.TiffData.UUID(
|
|
69
|
+
file_name=f"{p.field}.tif",
|
|
70
|
+
value=uuid.uuid4().urn,
|
|
71
|
+
),
|
|
72
|
+
first_c=p.iOM,
|
|
73
|
+
plane_count=1,
|
|
74
|
+
),
|
|
75
|
+
)
|
|
76
|
+
pixels = ome_types.model.Pixels(
|
|
77
|
+
dimension_order="XYCZT",
|
|
78
|
+
type="uint16",
|
|
79
|
+
size_x=align_info.tile_size_x,
|
|
80
|
+
size_y=align_info.tile_size_y,
|
|
81
|
+
size_z=1,
|
|
82
|
+
size_c=num_channels,
|
|
83
|
+
size_t=1,
|
|
84
|
+
physical_size_x=align_info.pixel_size_x,
|
|
85
|
+
physical_size_x_unit="ยตm",
|
|
86
|
+
physical_size_y=align_info.pixel_size_y,
|
|
87
|
+
physical_size_y_unit="ยตm",
|
|
88
|
+
channels=channels,
|
|
89
|
+
planes=planes,
|
|
90
|
+
tiff_data_blocks=tiff_data_blocks,
|
|
91
|
+
)
|
|
92
|
+
image = ome_types.model.Image(pixels=pixels)
|
|
93
|
+
ome.images.append(image)
|
|
94
|
+
|
|
95
|
+
with open(in_path / "image.companion.ome", "w", encoding="utf-8") as f:
|
|
96
|
+
f.write(ome.to_xml())
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def main(frames_path):
|
|
100
|
+
generate_companion_ome(frames_path)
|
|
101
|
+
|
|
102
|
+
if __name__ == "__main__":
|
|
103
|
+
import sys
|
|
104
|
+
main(sys.argv[1])
|
|
105
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import shutil
|
|
3
|
+
from mcmicroprep.microscopes.base import MicroscopeHandler
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CorePipeline:
|
|
7
|
+
def __init__(self, handler: MicroscopeHandler, image_root: Path):
|
|
8
|
+
self.handler = handler
|
|
9
|
+
self.image_root = Path(image_root)
|
|
10
|
+
self.templates = Path(__file__).parent / "templates"
|
|
11
|
+
|
|
12
|
+
def run(self):
|
|
13
|
+
# 1๏ธโฃ Organize raw imagery in-place
|
|
14
|
+
self.handler.restructure_raw(self.image_root)
|
|
15
|
+
# 2๏ธโฃ Enforce dataset structure (expect image_root to be the dataset root)
|
|
16
|
+
# No additional moving of slide folders; image_root should contain slide dirs directly.
|
|
17
|
+
# 3๏ธโฃ Generate boilerplate
|
|
18
|
+
self._generate_boilerplate()
|
|
19
|
+
|
|
20
|
+
def _generate_boilerplate(self):
|
|
21
|
+
# Copy common boilerplate templates
|
|
22
|
+
common = self.templates / "common"
|
|
23
|
+
for fname in [
|
|
24
|
+
"batch_submission.sh",
|
|
25
|
+
"mcmicro_template.sh",
|
|
26
|
+
"base.config",
|
|
27
|
+
"markers.csv",
|
|
28
|
+
]:
|
|
29
|
+
src = common / fname
|
|
30
|
+
dst = self.image_root / fname
|
|
31
|
+
if not dst.exists():
|
|
32
|
+
shutil.copy(src, dst)
|
|
33
|
+
if dst.suffix == ".sh":
|
|
34
|
+
dst.chmod(dst.stat().st_mode | 0o111)
|
|
35
|
+
|
|
36
|
+
# Copy microscope-specific params YAML
|
|
37
|
+
params_src = self.templates / "params" / f"{self.handler.name}.yml"
|
|
38
|
+
params_dst = self.image_root / "params.yml"
|
|
39
|
+
if params_src.exists() and not params_dst.exists():
|
|
40
|
+
shutil.copy(params_src, params_dst)
|
|
File without changes
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
class MicroscopeHandler(ABC):
|
|
5
|
+
"""
|
|
6
|
+
Base class for microscope-specific handlers.
|
|
7
|
+
"""
|
|
8
|
+
name: str # e.g. 'olympus'
|
|
9
|
+
|
|
10
|
+
@abstractmethod
|
|
11
|
+
def restructure_raw(self, image_root: Path):
|
|
12
|
+
"""Reorganize raw files into raw/ and misc_files/ in place."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
@abstractmethod
|
|
16
|
+
def params_template(self) -> dict:
|
|
17
|
+
"""(Optional) Return dict for param overrides; not used for file-based params."""
|
|
18
|
+
pass
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import subprocess
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from mcmicroprep.microscopes.base import MicroscopeHandler
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class OlympusHandler(MicroscopeHandler):
|
|
9
|
+
name = "olympus"
|
|
10
|
+
|
|
11
|
+
def restructure_raw(self, image_root: Path):
|
|
12
|
+
for slide in image_root.iterdir():
|
|
13
|
+
if not slide.is_dir() or slide.name in [
|
|
14
|
+
"dataset",
|
|
15
|
+
"templates",
|
|
16
|
+
"microscopes",
|
|
17
|
+
]:
|
|
18
|
+
continue
|
|
19
|
+
raw = slide / "raw"
|
|
20
|
+
misc = slide / "misc_files"
|
|
21
|
+
raw.mkdir(exist_ok=True)
|
|
22
|
+
misc.mkdir(exist_ok=True)
|
|
23
|
+
|
|
24
|
+
for item in list(slide.iterdir()):
|
|
25
|
+
if item.is_dir() and item.name.endswith("_frames"):
|
|
26
|
+
item.rename(raw / item.name)
|
|
27
|
+
elif item.name not in ["raw", "misc_files"]:
|
|
28
|
+
item.rename(misc / item.name)
|
|
29
|
+
|
|
30
|
+
# Only run companion script if missing the OME file
|
|
31
|
+
for frame in raw.iterdir():
|
|
32
|
+
ome = frame / "image.companion.ome"
|
|
33
|
+
if not ome.exists():
|
|
34
|
+
# Invoke installed module, not script in cwd
|
|
35
|
+
subprocess.run(
|
|
36
|
+
[
|
|
37
|
+
sys.executable,
|
|
38
|
+
"-m",
|
|
39
|
+
"mcmicroprep.companion_script",
|
|
40
|
+
str(frame),
|
|
41
|
+
],
|
|
42
|
+
check=True,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
def params_template(self) -> dict:
|
|
46
|
+
# Not used when file-based params selection is preferred
|
|
47
|
+
return {}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from mcmicroprep.microscopes.base import MicroscopeHandler
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RareCyteHandler(MicroscopeHandler):
|
|
7
|
+
name = "rarecyte"
|
|
8
|
+
|
|
9
|
+
def restructure_raw(self, image_root: Path):
|
|
10
|
+
for slide in image_root.iterdir():
|
|
11
|
+
if not slide.is_dir() or slide.name in ["templates", "microscopes"]:
|
|
12
|
+
continue
|
|
13
|
+
raw = slide / "raw"
|
|
14
|
+
misc = slide / "misc_files"
|
|
15
|
+
raw.mkdir(exist_ok=True)
|
|
16
|
+
misc.mkdir(exist_ok=True)
|
|
17
|
+
|
|
18
|
+
# Flatten: move all .rcpnl files directly into raw/
|
|
19
|
+
for path in slide.rglob("*.rcpnl"):
|
|
20
|
+
if raw in path.parents or misc in path.parents:
|
|
21
|
+
continue
|
|
22
|
+
dest = raw / path.name
|
|
23
|
+
path.rename(dest)
|
|
24
|
+
|
|
25
|
+
# Move all other top-level items to misc_files/
|
|
26
|
+
for item in list(slide.iterdir()):
|
|
27
|
+
if item.name not in ["raw", "misc_files"]:
|
|
28
|
+
item.rename(misc / item.name)
|
|
29
|
+
|
|
30
|
+
def params_template(self) -> dict:
|
|
31
|
+
return {}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
import argparse
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from mcmicroprep.core import CorePipeline
|
|
5
|
+
from mcmicroprep.microscopes.olympus import OlympusHandler
|
|
6
|
+
from mcmicroprep.microscopes.rarecyte import RareCyteHandler
|
|
7
|
+
|
|
8
|
+
HANDLERS = {
|
|
9
|
+
"olympus": OlympusHandler,
|
|
10
|
+
"rarecyte": RareCyteHandler,
|
|
11
|
+
# add more handlers here
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
parser = argparse.ArgumentParser("Prepare data for MCMicro")
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"--microscope",
|
|
19
|
+
choices=HANDLERS,
|
|
20
|
+
required=True,
|
|
21
|
+
help="Microscope vendor (e.g., olympus, rarecyte)",
|
|
22
|
+
)
|
|
23
|
+
parser.add_argument(
|
|
24
|
+
"--image-root", required=True, help="Root folder containing slide directories"
|
|
25
|
+
)
|
|
26
|
+
args = parser.parse_args()
|
|
27
|
+
|
|
28
|
+
handler = HANDLERS[args.microscope]()
|
|
29
|
+
pipeline = CorePipeline(handler, Path(args.image_root))
|
|
30
|
+
pipeline.run()
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
main()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
process {
|
|
2
|
+
withName:illumination {
|
|
3
|
+
cpus = 4
|
|
4
|
+
time = '12h'
|
|
5
|
+
}
|
|
6
|
+
withName:ashlar {
|
|
7
|
+
cpus = 1
|
|
8
|
+
time = '36h'
|
|
9
|
+
queue = 'medium'
|
|
10
|
+
}
|
|
11
|
+
withName: 'segmentation:worker' {
|
|
12
|
+
time = '2h'
|
|
13
|
+
queue = 'gpu_quad'
|
|
14
|
+
}
|
|
15
|
+
withName:s3seg {
|
|
16
|
+
cpus = 4
|
|
17
|
+
time = '2h'
|
|
18
|
+
}
|
|
19
|
+
withName:mcquant {
|
|
20
|
+
cpus = 1
|
|
21
|
+
queue = 'medium'
|
|
22
|
+
time = '96h'
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
report {
|
|
27
|
+
enabled = false
|
|
28
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Parse arguments
|
|
4
|
+
OPTS=$(getopt -o "" --long dataset_path:,mcmicro_template:,markers_csv:,params_yml: -n 'submission.sh' -- "$@")
|
|
5
|
+
|
|
6
|
+
if [ $? != 0 ]; then
|
|
7
|
+
echo "Failed parsing options." >&2
|
|
8
|
+
exit 1
|
|
9
|
+
fi
|
|
10
|
+
|
|
11
|
+
eval set -- "$OPTS"
|
|
12
|
+
|
|
13
|
+
dataset_path=""
|
|
14
|
+
mcmicro_template=""
|
|
15
|
+
markers_csv=""
|
|
16
|
+
params_yml=""
|
|
17
|
+
|
|
18
|
+
# Extract arguments
|
|
19
|
+
while true; do
|
|
20
|
+
case "$1" in
|
|
21
|
+
--dataset_path ) dataset_path="$2"; shift 2 ;;
|
|
22
|
+
--mcmicro_template ) mcmicro_template="$2"; shift 2 ;;
|
|
23
|
+
--markers_csv ) markers_csv="$2"; shift 2 ;;
|
|
24
|
+
--params_yml ) params_yml="$2"; shift 2 ;;
|
|
25
|
+
-- ) shift; break ;;
|
|
26
|
+
* ) break ;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
# Check dataset_path
|
|
31
|
+
if [[ -z "$dataset_path" || ! -d "$dataset_path" ]]; then
|
|
32
|
+
echo "Error: dataset_path is required and must exist: $dataset_path"
|
|
33
|
+
exit 1
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Assign default paths if optional ones are not provided
|
|
37
|
+
if [[ -z "$mcmicro_template" ]]; then
|
|
38
|
+
mcmicro_template="${dataset_path%/}/mcmicro_template.sh"
|
|
39
|
+
echo "No mcmicro_template provided. Using default: $mcmicro_template"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if [[ -z "$markers_csv" ]]; then
|
|
43
|
+
markers_csv="${dataset_path%/}/markers.csv"
|
|
44
|
+
echo "No markers_csv provided. Using default: $markers_csv"
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
if [[ -z "$params_yml" ]]; then
|
|
48
|
+
params_yml="${dataset_path%/}/params.yml"
|
|
49
|
+
echo "No params_yml provided. Using default: $params_yml"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Check that all necessary files exist now
|
|
53
|
+
if [[ ! -f "$mcmicro_template" ]]; then
|
|
54
|
+
echo "Error: mcmicro_template not found at $mcmicro_template"
|
|
55
|
+
exit 1
|
|
56
|
+
fi
|
|
57
|
+
|
|
58
|
+
if [[ ! -f "$markers_csv" ]]; then
|
|
59
|
+
echo "Error: markers_csv not found at $markers_csv"
|
|
60
|
+
exit 1
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
if [[ ! -f "$params_yml" ]]; then
|
|
64
|
+
echo "Error: params_yml not found at $params_yml"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
# Submit jobs for each raw folder
|
|
69
|
+
for raw_folder in "$dataset_path"/raw/*_frames; do
|
|
70
|
+
if [ -d "$raw_folder" ]; then
|
|
71
|
+
echo "Submitting job for: $raw_folder"
|
|
72
|
+
sbatch "$mcmicro_template" "$raw_folder"
|
|
73
|
+
fi
|
|
74
|
+
done
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#SBATCH -p short
|
|
3
|
+
#SBATCH -J nextflow_O2
|
|
4
|
+
#SBATCH -t 0-12:00
|
|
5
|
+
#SBATCH --mem=1G
|
|
6
|
+
#SBATCH --mail-type=END
|
|
7
|
+
|
|
8
|
+
SAMPLEDIR=$1
|
|
9
|
+
SAMPLEID=$(basename $SAMPLEDIR)
|
|
10
|
+
|
|
11
|
+
module purge
|
|
12
|
+
module load java
|
|
13
|
+
|
|
14
|
+
/n/groups/lsp/mcmicro/tools/o2/config_pre_reg.sh -s -u $SAMPLEDIR > $SAMPLEDIR/memory.config
|
|
15
|
+
|
|
16
|
+
nextflow run labsyspharm/mcmicro -profile O2,WSI,GPU --in $SAMPLEDIR -w /n/scratch/users/${USER:0:1}/$USER/work -c $(dirname "$SAMPLEDIR")/base.config -c $SAMPLEDIR/memory.config -publish_dir_mode link
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
workflow:
|
|
2
|
+
start-at: illumination
|
|
3
|
+
stop-at: quantification
|
|
4
|
+
background: false
|
|
5
|
+
segmentation-channel: 1 1
|
|
6
|
+
multi-formats: .ome
|
|
7
|
+
tma: false
|
|
8
|
+
options:
|
|
9
|
+
ashlar: --flip-y --filter-sigma 1
|
|
10
|
+
unmicst: --channel 1 --scalingFactor 0.5 --tool unmicst-duo --outlier 99.99
|
|
11
|
+
s3seg: --maxima-footprint-size 15 --area-max 50000 --expand-size 6 --pixelSize 0.325 --mean-intensity-min 80
|
|
12
|
+
mcquant: --masks nucleiRing.ome.tif cellRing.ome.tif cytoRing.ome.tif
|
|
13
|
+
coreograph: --channel 1
|
|
14
|
+
modules:
|
|
15
|
+
watershed:
|
|
16
|
+
name: s3seg
|
|
17
|
+
container: labsyspharm/s3segmenter
|
|
18
|
+
version: 1.5.5-large
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
workflow:
|
|
2
|
+
start-at: illumination
|
|
3
|
+
stop-at: quantification
|
|
4
|
+
background: false
|
|
5
|
+
segmentation-channel: 1 1
|
|
6
|
+
tma: false
|
|
7
|
+
options:
|
|
8
|
+
unmicst: --channel 1 --scalingFactor 0.5 --tool unmicst-duo --outlier 99.99
|
|
9
|
+
s3seg: --maxima-footprint-size 15 --area-max 50000 --expand-size 6 --pixelSize 0.325 --mean-intensity-min 80
|
|
10
|
+
mcquant: --masks nucleiRing.ome.tif cellRing.ome.tif cytoRing.ome.tif
|
|
11
|
+
coreograph: --channel 1
|
|
12
|
+
modules:
|
|
13
|
+
watershed:
|
|
14
|
+
name: s3seg
|
|
15
|
+
container: labsyspharm/s3segmenter
|
|
16
|
+
version: 1.5.5-large
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "mcmicroprep"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = ["Ajit Johnson Nirmal <ajitjohnson.n@gmail.com>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
|
|
8
|
+
[tool.poetry.dependencies]
|
|
9
|
+
python = "^3.10"
|
|
10
|
+
lxml = "^6.0.0"
|
|
11
|
+
ome-types = "^0.6.1"
|
|
12
|
+
pandas = "^2.3.1"
|
|
13
|
+
pydantic = "^2.11.7"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["poetry-core"]
|
|
18
|
+
build-backend = "poetry.core.masonry.api"
|
|
19
|
+
|
|
20
|
+
[tool.poetry.scripts]
|
|
21
|
+
preparemcmicro = "mcmicroprep.preparemcmicro:main"
|