octopi 1.4.0__py3-none-any.whl
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.
- octopi/__init__.py +7 -0
- octopi/datasets/__init__.py +0 -0
- octopi/datasets/augment.py +83 -0
- octopi/datasets/cached_datset.py +113 -0
- octopi/datasets/dataset.py +19 -0
- octopi/datasets/generators.py +458 -0
- octopi/datasets/io.py +200 -0
- octopi/datasets/mixup.py +49 -0
- octopi/datasets/multi_config_generator.py +252 -0
- octopi/entry_points/__init__.py +0 -0
- octopi/entry_points/common.py +119 -0
- octopi/entry_points/create_slurm_submission.py +251 -0
- octopi/entry_points/groups.py +152 -0
- octopi/entry_points/run_create_targets.py +234 -0
- octopi/entry_points/run_evaluate.py +99 -0
- octopi/entry_points/run_extract_mb_picks.py +191 -0
- octopi/entry_points/run_extract_midpoint.py +143 -0
- octopi/entry_points/run_localize.py +176 -0
- octopi/entry_points/run_optuna.py +161 -0
- octopi/entry_points/run_segment.py +154 -0
- octopi/entry_points/run_train.py +189 -0
- octopi/extract/__init__.py +0 -0
- octopi/extract/localize.py +217 -0
- octopi/extract/membranebound_extract.py +263 -0
- octopi/extract/midpoint_extract.py +193 -0
- octopi/main.py +33 -0
- octopi/models/AttentionUnet.py +56 -0
- octopi/models/MedNeXt.py +111 -0
- octopi/models/ModelTemplate.py +36 -0
- octopi/models/SegResNet.py +92 -0
- octopi/models/Unet.py +59 -0
- octopi/models/UnetPlusPlus.py +47 -0
- octopi/models/__init__.py +0 -0
- octopi/models/common.py +72 -0
- octopi/processing/__init__.py +0 -0
- octopi/processing/create_targets_from_picks.py +224 -0
- octopi/processing/downloader.py +138 -0
- octopi/processing/downsample.py +125 -0
- octopi/processing/evaluate.py +302 -0
- octopi/processing/importers.py +116 -0
- octopi/processing/segmentation_from_picks.py +167 -0
- octopi/pytorch/__init__.py +0 -0
- octopi/pytorch/hyper_search.py +244 -0
- octopi/pytorch/model_search_submitter.py +291 -0
- octopi/pytorch/segmentation.py +363 -0
- octopi/pytorch/segmentation_multigpu.py +162 -0
- octopi/pytorch/trainer.py +465 -0
- octopi/pytorch_lightning/__init__.py +0 -0
- octopi/pytorch_lightning/optuna_pl_ddp.py +273 -0
- octopi/pytorch_lightning/train_pl.py +244 -0
- octopi/utils/__init__.py +0 -0
- octopi/utils/config.py +57 -0
- octopi/utils/io.py +215 -0
- octopi/utils/losses.py +86 -0
- octopi/utils/parsers.py +162 -0
- octopi/utils/progress.py +78 -0
- octopi/utils/stopping_criteria.py +143 -0
- octopi/utils/submit_slurm.py +95 -0
- octopi/utils/visualization_tools.py +290 -0
- octopi/workflows.py +262 -0
- octopi-1.4.0.dist-info/METADATA +119 -0
- octopi-1.4.0.dist-info/RECORD +65 -0
- octopi-1.4.0.dist-info/WHEEL +4 -0
- octopi-1.4.0.dist-info/entry_points.txt +3 -0
- octopi-1.4.0.dist-info/licenses/LICENSE +41 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
from octopi.utils import parsers
|
|
2
|
+
from octopi import cli_context
|
|
3
|
+
import rich_click as click
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
def my_evaluator(
|
|
7
|
+
copick_config_path: str,
|
|
8
|
+
ground_truth_user_id: str,
|
|
9
|
+
ground_truth_session_id: str,
|
|
10
|
+
predict_user_id: str,
|
|
11
|
+
predict_session_id: str,
|
|
12
|
+
save_path: str,
|
|
13
|
+
distance_threshold_scale: float,
|
|
14
|
+
object_names: List[str] = None,
|
|
15
|
+
runIDs: List[str] = None
|
|
16
|
+
):
|
|
17
|
+
import octopi.processing.evaluate as evaluate
|
|
18
|
+
|
|
19
|
+
eval = evaluate.evaluator(
|
|
20
|
+
copick_config_path,
|
|
21
|
+
ground_truth_user_id,
|
|
22
|
+
ground_truth_session_id,
|
|
23
|
+
predict_user_id,
|
|
24
|
+
predict_session_id,
|
|
25
|
+
object_names=object_names
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
eval.run(save_path=save_path, distance_threshold_scale=distance_threshold_scale, runIDs=runIDs)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@click.command('evaluate', context_settings=cli_context)
|
|
32
|
+
# Output Arguments
|
|
33
|
+
@click.option('-o','--output', type=click.Path(), default='scores',
|
|
34
|
+
help="Path to save evaluation results")
|
|
35
|
+
# Evaluation Parameters
|
|
36
|
+
@click.option('-names','--object-names', type=str, default=None,
|
|
37
|
+
callback=lambda ctx, param, value: parsers.parse_list(value) if value else None,
|
|
38
|
+
help="Optional list of object names to evaluate, e.g., ribosome,apoferritin")
|
|
39
|
+
@click.option('-dts','--distance-threshold-scale', type=float, default=0.8,
|
|
40
|
+
help="Compute Distance Threshold Based on Particle Radius")
|
|
41
|
+
# Input Arguments
|
|
42
|
+
@click.option('--run-ids', type=str, default=None,
|
|
43
|
+
callback=lambda ctx, param, value: parsers.parse_list(value) if value else None,
|
|
44
|
+
help="Optional list of run IDs to evaluate, e.g., run1,run2,run3 or [run1,run2,run3]")
|
|
45
|
+
@click.option('-psid', '--predict-session-id', type=str,
|
|
46
|
+
default='1', help="Session ID for prediction data")
|
|
47
|
+
@click.option('-puid','--predict-user-id', type=str, required=True,
|
|
48
|
+
default='octopi', help="User ID for prediction data")
|
|
49
|
+
@click.option('-gtsid','--ground-truth-session-id', type=str, default=None,
|
|
50
|
+
help="Session ID for ground truth data")
|
|
51
|
+
@click.option('-gtuid','--ground-truth-user-id', type=str, required=True,
|
|
52
|
+
help="User ID for ground truth data")
|
|
53
|
+
@click.option('-c', '--config', type=click.Path(exists=True), required=True,
|
|
54
|
+
help="Path to the copick configuration file")
|
|
55
|
+
def cli(config, ground_truth_user_id, ground_truth_session_id,
|
|
56
|
+
predict_user_id, predict_session_id, run_ids,
|
|
57
|
+
distance_threshold_scale, object_names,
|
|
58
|
+
output):
|
|
59
|
+
"""
|
|
60
|
+
Evaluate particle localization performance against ground truth annotations.
|
|
61
|
+
|
|
62
|
+
This command compares predicted particle picks against expert annotations using distance-based
|
|
63
|
+
matching. A prediction is considered correct (true positive) if it falls within a specified
|
|
64
|
+
distance threshold of a ground truth annotation. The threshold is defined as a fraction of
|
|
65
|
+
the particle's radius (default: 0.8 = 80% of radius).
|
|
66
|
+
|
|
67
|
+
Computed metrics include:
|
|
68
|
+
• Precision: Fraction of predictions that match ground truth
|
|
69
|
+
• Recall: Fraction of ground truth particles that were detected
|
|
70
|
+
• F-beta scores: Harmonic mean of precision and recall (with configurable beta)
|
|
71
|
+
|
|
72
|
+
Results are saved as a YAML file containing per-object and aggregate statistics, making it
|
|
73
|
+
easy to track model performance across experiments and compare different localization methods.
|
|
74
|
+
|
|
75
|
+
\b
|
|
76
|
+
Examples:
|
|
77
|
+
# Evaluate predictions against Data Portal annotations
|
|
78
|
+
octopi evaluate -c config.json \\
|
|
79
|
+
-gtuid data-portal -gtsid 0 \\
|
|
80
|
+
-puid octopi -psid 1 \\
|
|
81
|
+
-o evaluation_results.yaml
|
|
82
|
+
"""
|
|
83
|
+
|
|
84
|
+
# Call the evaluate function with parsed arguments
|
|
85
|
+
my_evaluator(
|
|
86
|
+
copick_config_path=config,
|
|
87
|
+
ground_truth_user_id=ground_truth_user_id,
|
|
88
|
+
ground_truth_session_id=ground_truth_session_id,
|
|
89
|
+
predict_user_id=predict_user_id,
|
|
90
|
+
predict_session_id=predict_session_id,
|
|
91
|
+
save_path=output,
|
|
92
|
+
distance_threshold_scale=distance_threshold_scale,
|
|
93
|
+
object_names=object_names,
|
|
94
|
+
runIDs=run_ids
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
if __name__ == "__main__":
|
|
99
|
+
cli()
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
from typing import List, Tuple, Optional
|
|
2
|
+
from octopi.utils import parsers
|
|
3
|
+
import rich_click as click
|
|
4
|
+
|
|
5
|
+
def extract_membrane_bound_picks(
|
|
6
|
+
config: str,
|
|
7
|
+
voxel_size: float,
|
|
8
|
+
distance_threshold: float,
|
|
9
|
+
picks_info: Tuple[str, str, str],
|
|
10
|
+
organelle_info: Tuple[str, str, str],
|
|
11
|
+
membrane_info: Tuple[str, str, str],
|
|
12
|
+
save_user_id: str,
|
|
13
|
+
save_session_id: str,
|
|
14
|
+
runIDs: List[str],
|
|
15
|
+
n_procs: int = None
|
|
16
|
+
):
|
|
17
|
+
from octopi.extract import membranebound_extract as extract
|
|
18
|
+
import multiprocess as mp
|
|
19
|
+
from tqdm import tqdm
|
|
20
|
+
import copick
|
|
21
|
+
|
|
22
|
+
# Load Copick Project for Writing
|
|
23
|
+
root = copick.from_file(config)
|
|
24
|
+
|
|
25
|
+
# Either Specify Input RunIDs or Run on All RunIDs
|
|
26
|
+
if runIDs: print('Extracting Membrane Bound Proteins on the Following RunIDs: ', runIDs)
|
|
27
|
+
run_ids = runIDs if runIDs else [run.name for run in root.runs]
|
|
28
|
+
n_run_ids = len(run_ids)
|
|
29
|
+
|
|
30
|
+
# Determine the number of processes to use
|
|
31
|
+
if n_procs is None:
|
|
32
|
+
n_procs = min(mp.cpu_count(), n_run_ids)
|
|
33
|
+
print(f"Using {n_procs} processes to parallelize across {n_run_ids} run IDs.")
|
|
34
|
+
|
|
35
|
+
# Run Membrane-Protein Isolation - Main Parallelization Loop
|
|
36
|
+
with mp.Pool(processes=n_procs) as pool:
|
|
37
|
+
with tqdm(total=n_run_ids, desc="Membrane-Protein Isolation", unit="run") as pbar:
|
|
38
|
+
worker_func = lambda run_id: extract.process_membrane_bound_extract(
|
|
39
|
+
root.get_run(run_id),
|
|
40
|
+
voxel_size,
|
|
41
|
+
picks_info,
|
|
42
|
+
membrane_info,
|
|
43
|
+
organelle_info,
|
|
44
|
+
save_user_id,
|
|
45
|
+
save_session_id,
|
|
46
|
+
distance_threshold
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
for _ in pool.imap_unordered(worker_func, run_ids, chunksize=1):
|
|
50
|
+
pbar.update(1)
|
|
51
|
+
|
|
52
|
+
print('Extraction of Membrane-Bound Proteins Complete!')
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def save_parameters(config: str,
|
|
56
|
+
voxel_size: float,
|
|
57
|
+
picks_info: tuple,
|
|
58
|
+
membrane_info: tuple,
|
|
59
|
+
organelle_info: tuple,
|
|
60
|
+
save_user_id: str,
|
|
61
|
+
save_session_id: str,
|
|
62
|
+
distance_threshold: float,
|
|
63
|
+
runIDs: list,
|
|
64
|
+
output_path: str):
|
|
65
|
+
import octopi.utils.io as io
|
|
66
|
+
import pprint
|
|
67
|
+
|
|
68
|
+
params_dict = {
|
|
69
|
+
"input": {
|
|
70
|
+
"config": config,
|
|
71
|
+
"voxel_size": voxel_size,
|
|
72
|
+
"picks_info": picks_info,
|
|
73
|
+
"membrane_info": membrane_info,
|
|
74
|
+
"organelle_info": organelle_info
|
|
75
|
+
},
|
|
76
|
+
"output": {
|
|
77
|
+
"save_user_id": save_user_id,
|
|
78
|
+
"save_session_id": save_session_id
|
|
79
|
+
},
|
|
80
|
+
"parameters": {
|
|
81
|
+
"distance_threshold": distance_threshold,
|
|
82
|
+
"runIDs": runIDs
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Print the parameters
|
|
87
|
+
print(f"\nParameters for Extraction of Membrane-Bound Picks:")
|
|
88
|
+
pprint.pprint(params_dict); print()
|
|
89
|
+
|
|
90
|
+
# Save parameters to YAML file
|
|
91
|
+
io.save_parameters_yaml(params_dict, output_path)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@click.command('membrane-extract')
|
|
95
|
+
# Output Arguments
|
|
96
|
+
@click.option('--save-session-id', type=str, required=True,
|
|
97
|
+
help="Session ID to save the new picks")
|
|
98
|
+
@click.option('--save-user-id', type=str, default=None,
|
|
99
|
+
help="User ID to save the new picks (defaults to picks user ID)")
|
|
100
|
+
# Parameters
|
|
101
|
+
@click.option('--n-procs', type=int, default=None,
|
|
102
|
+
help="Number of processes to use (defaults to CPU count)")
|
|
103
|
+
@click.option('--distance-threshold', type=float, default=10,
|
|
104
|
+
help="Distance threshold for membrane proximity")
|
|
105
|
+
# Input Arguments
|
|
106
|
+
@click.option('--runIDs', type=str, default=None,
|
|
107
|
+
callback=lambda ctx, param, value: parsers.parse_list(value) if value else None,
|
|
108
|
+
help="List of run IDs to process")
|
|
109
|
+
@click.option('--organelle-info', type=str, default=None,
|
|
110
|
+
callback=lambda ctx, param, value: parsers.parse_target(value) if value else None,
|
|
111
|
+
help='Query for the organelles segmentations (e.g., "name" or "name,user_id,session_id")')
|
|
112
|
+
@click.option('--membrane-info', type=str, default=None,
|
|
113
|
+
callback=lambda ctx, param, value: parsers.parse_target(value) if value else None,
|
|
114
|
+
help='Query for the membrane segmentation (e.g., "name" or "name,user_id,session_id")')
|
|
115
|
+
@click.option('--picks-info', type=str, required=True,
|
|
116
|
+
callback=lambda ctx, param, value: parsers.parse_target(value),
|
|
117
|
+
help='Query for the picks (e.g., "name" or "name,user_id,session_id")')
|
|
118
|
+
@click.option('-vs', '--voxel-size', type=float, default=10,
|
|
119
|
+
help="Voxel size")
|
|
120
|
+
@click.option('-c', '--config', type=click.Path(exists=True), required=True,
|
|
121
|
+
help="Path to the configuration file")
|
|
122
|
+
def cli(config, voxel_size, picks_info, membrane_info, organelle_info, runIDs,
|
|
123
|
+
distance_threshold, n_procs,
|
|
124
|
+
save_user_id, save_session_id):
|
|
125
|
+
"""
|
|
126
|
+
Extract membrane-bound picks based on proximity to organelle or membrane segmentation.
|
|
127
|
+
|
|
128
|
+
This command isolates membrane-bound proteins from segmented volumes by finding
|
|
129
|
+
particles that are within a specified distance threshold of the membrane segmentation.
|
|
130
|
+
The resulting picks are saved as zarr arrays in your copick project, organized by
|
|
131
|
+
segmentation name, user ID, and session ID for easy tracking and comparison.
|
|
132
|
+
|
|
133
|
+
\b
|
|
134
|
+
Examples:
|
|
135
|
+
# Extract membrane-bound picks with default distance threshold
|
|
136
|
+
octopi membrane-extract -c config.json \\
|
|
137
|
+
--picks-info predictions,octopi,1 \\
|
|
138
|
+
--membrane-info membrane,octopi,1 \\
|
|
139
|
+
--organelle-info organelle,octopi,1 \\
|
|
140
|
+
--save-user-id octopi \\
|
|
141
|
+
--save-session-id 1
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
run_mb_extract(
|
|
145
|
+
config, voxel_size,
|
|
146
|
+
picks_info, membrane_info, organelle_info,
|
|
147
|
+
runIDs, distance_threshold, n_procs,
|
|
148
|
+
save_user_id, save_session_id
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def run_mb_extract(
|
|
153
|
+
config, voxel_size, picks_info, membrane_info,
|
|
154
|
+
organelle_info, runIDs, distance_threshold, n_procs,
|
|
155
|
+
save_user_id, save_session_id):
|
|
156
|
+
|
|
157
|
+
# Default save_user_id to picks_info user_id if not specified
|
|
158
|
+
if save_user_id is None:
|
|
159
|
+
save_user_id = picks_info[1]
|
|
160
|
+
|
|
161
|
+
# Save parameters
|
|
162
|
+
output_yaml = f'membrane-extract_{save_user_id}_{save_session_id}.yaml'
|
|
163
|
+
save_parameters(
|
|
164
|
+
config=config,
|
|
165
|
+
voxel_size=voxel_size,
|
|
166
|
+
picks_info=picks_info,
|
|
167
|
+
membrane_info=membrane_info,
|
|
168
|
+
organelle_info=organelle_info,
|
|
169
|
+
save_user_id=save_user_id,
|
|
170
|
+
save_session_id=save_session_id,
|
|
171
|
+
distance_threshold=distance_threshold,
|
|
172
|
+
runIDs=runIDs,
|
|
173
|
+
output_path=output_yaml
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
extract_membrane_bound_picks(
|
|
177
|
+
config=config,
|
|
178
|
+
voxel_size=voxel_size,
|
|
179
|
+
distance_threshold=distance_threshold,
|
|
180
|
+
picks_info=picks_info,
|
|
181
|
+
membrane_info=membrane_info,
|
|
182
|
+
organelle_info=organelle_info,
|
|
183
|
+
save_user_id=save_user_id,
|
|
184
|
+
save_session_id=save_session_id,
|
|
185
|
+
runIDs=runIDs,
|
|
186
|
+
n_procs=n_procs,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
cli()
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from octopi.extract import midpoint_extract
|
|
2
|
+
from typing import List, Tuple, Optional
|
|
3
|
+
import argparse, pprint, copick
|
|
4
|
+
from octopi import utils
|
|
5
|
+
import multiprocess as mp
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
|
|
8
|
+
def extract_midpoint(
|
|
9
|
+
config: str,
|
|
10
|
+
voxel_size: float,
|
|
11
|
+
picks_info: Tuple[str, str, str],
|
|
12
|
+
organelle_info: Tuple[str, str, str],
|
|
13
|
+
distance_min: float,
|
|
14
|
+
distance_max: float,
|
|
15
|
+
distance_threshold: float,
|
|
16
|
+
save_session_id: str,
|
|
17
|
+
runIDs: List[str],
|
|
18
|
+
n_procs: int = None
|
|
19
|
+
):
|
|
20
|
+
|
|
21
|
+
# Load Copick Project for Writing
|
|
22
|
+
root = copick.from_file( config )
|
|
23
|
+
|
|
24
|
+
# Either Specify Input RunIDs or Run on All RunIDs
|
|
25
|
+
if runIDs: print('Extracting Midpoints on the Following RunIDs: ', runIDs)
|
|
26
|
+
run_ids = runIDs if runIDs else [run.name for run in root.runs]
|
|
27
|
+
n_run_ids = len(run_ids)
|
|
28
|
+
|
|
29
|
+
# Determine the number of processes to use
|
|
30
|
+
if n_procs is None:
|
|
31
|
+
n_procs = min(mp.cpu_count(), n_run_ids)
|
|
32
|
+
print(f"Using {n_procs} processes to parallelize across {n_run_ids} run IDs.")
|
|
33
|
+
|
|
34
|
+
# Initialize tqdm progress bar
|
|
35
|
+
with tqdm(total=n_run_ids, desc="Mid-Point SuperComplex Extraction", unit="run") as pbar:
|
|
36
|
+
for _iz in range(0, n_run_ids, n_procs):
|
|
37
|
+
|
|
38
|
+
start_idx = _iz
|
|
39
|
+
end_idx = min(_iz + n_procs, n_run_ids) # Ensure end_idx does not exceed n_run_ids
|
|
40
|
+
print(f"\nProcessing runIDs from {start_idx} -> {end_idx } (out of {n_run_ids})")
|
|
41
|
+
|
|
42
|
+
processes = []
|
|
43
|
+
for _in in range(n_procs):
|
|
44
|
+
_iz_this = _iz + _in
|
|
45
|
+
if _iz_this >= n_run_ids:
|
|
46
|
+
break
|
|
47
|
+
run_id = run_ids[_iz_this]
|
|
48
|
+
run = root.get_run(run_id)
|
|
49
|
+
p = mp.Process(
|
|
50
|
+
target=midpoint_extract.process_midpoint_extract,
|
|
51
|
+
args=(run,
|
|
52
|
+
voxel_size,
|
|
53
|
+
picks_info,
|
|
54
|
+
organelle_info,
|
|
55
|
+
distance_min,
|
|
56
|
+
distance_max,
|
|
57
|
+
distance_threshold,
|
|
58
|
+
save_session_id)
|
|
59
|
+
)
|
|
60
|
+
processes.append(p)
|
|
61
|
+
|
|
62
|
+
for p in processes:
|
|
63
|
+
p.start()
|
|
64
|
+
|
|
65
|
+
for p in processes:
|
|
66
|
+
p.join()
|
|
67
|
+
|
|
68
|
+
for p in processes:
|
|
69
|
+
p.close()
|
|
70
|
+
|
|
71
|
+
# Update tqdm progress bar
|
|
72
|
+
pbar.update(len(processes))
|
|
73
|
+
|
|
74
|
+
print('Extraction of Midpoints Complete!')
|
|
75
|
+
|
|
76
|
+
def cli():
|
|
77
|
+
parser = argparse.ArgumentParser(
|
|
78
|
+
description='Extract membrane-bound picks based on proximity to segmentation.',
|
|
79
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
80
|
+
)
|
|
81
|
+
parser.add_argument('--config', type=str, required=True, help='Path to the configuration file.')
|
|
82
|
+
parser.add_argument('--voxel-size', type=float, required=False, default=10, help='Segmentation Voxel size.')
|
|
83
|
+
parser.add_argument('--picks-info', type=utils.parse_target, required=True, help='Query for the picks (e.g., "name" or "name,user_id,session_id".).')
|
|
84
|
+
parser.add_argument('--organelle-info', type=utils.parse_target, required=False, help='Query for the organelles segmentations (e.g., "name" or "name,user_id,session_id".).')
|
|
85
|
+
parser.add_argument('--distance-min', type=float, required=False, default=10, help='Minimum distance for valid nearest neighbors.')
|
|
86
|
+
parser.add_argument('--distance-max', type=float, required=False, default=70, help='Maximum distance for valid nearest neighbors.')
|
|
87
|
+
parser.add_argument('--distance-threshold', type=float, required=False, default=25, help='Distance threshold for picks to associated organelles.')
|
|
88
|
+
parser.add_argument('--save-session-id', type=str, required=False, default=None, help='(Optional)SessionID to save the new picks. If none provided, will use the sessionID from the picks.')
|
|
89
|
+
parser.add_argument('--runIDs', type=utils.parse_list, required=False, help='(Optional) List of run IDs to process.')
|
|
90
|
+
parser.add_argument('--n-procs', type=int, required=False, default=None, help='Number of processes to use. In none providd, will use the total number of CPUs available.')
|
|
91
|
+
|
|
92
|
+
args = parser.parse_args()
|
|
93
|
+
|
|
94
|
+
# Increment session ID for the second class
|
|
95
|
+
if args.save_session_id is None:
|
|
96
|
+
args.save_session_id = args.picks_info[2]
|
|
97
|
+
args.save_user_id = args.picks_info[1]
|
|
98
|
+
|
|
99
|
+
# Save JSON with Parameters
|
|
100
|
+
output_yaml = f'midpoint-extract_{args.picks_info[1]}_{args.save_session_id}.yaml'
|
|
101
|
+
save_parameters(args, output_yaml)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
extract_midpoint(
|
|
105
|
+
config=args.config,
|
|
106
|
+
voxel_size=args.voxel_size,
|
|
107
|
+
picks_info=args.picks_info,
|
|
108
|
+
organelle_info=args.organelle_info,
|
|
109
|
+
distance_min=args.distance_min,
|
|
110
|
+
distance_max=args.distance_max,
|
|
111
|
+
distance_threshold=args.distance_threshold,
|
|
112
|
+
save_session_id=args.save_session_id,
|
|
113
|
+
runIDs=args.runIDs,
|
|
114
|
+
n_procs=args.n_procs,
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def save_parameters(args: argparse.Namespace,
|
|
118
|
+
output_path: str):
|
|
119
|
+
|
|
120
|
+
params_dict = {
|
|
121
|
+
"input": {
|
|
122
|
+
k: getattr(args, k) for k in [
|
|
123
|
+
"config", "voxel_size", "picks_info",
|
|
124
|
+
"organelle_info"
|
|
125
|
+
]
|
|
126
|
+
},
|
|
127
|
+
"output": {
|
|
128
|
+
k: getattr(args, k) for k in ["save_user_id", "save_session_id"]
|
|
129
|
+
},
|
|
130
|
+
"parameters": {
|
|
131
|
+
k: getattr(args, k) for k in ["distance_min", "distance_max", "distance_threshold", "runIDs"]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Print the parameters
|
|
136
|
+
print(f"\nParameters for Extraction of Membrane-Bound Picks:")
|
|
137
|
+
pprint.pprint(params_dict); print()
|
|
138
|
+
|
|
139
|
+
# Save parameters to YAML file
|
|
140
|
+
utils.save_parameters_yaml(params_dict, output_path)
|
|
141
|
+
|
|
142
|
+
if __name__ == "__main__":
|
|
143
|
+
cli()
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
from octopi.utils import parsers
|
|
2
|
+
from typing import List, Tuple
|
|
3
|
+
import rich_click as click
|
|
4
|
+
|
|
5
|
+
def pick_particles(
|
|
6
|
+
copick_config_path: str,
|
|
7
|
+
method: str,
|
|
8
|
+
seg_info: Tuple[str, str, str],
|
|
9
|
+
voxel_size: float,
|
|
10
|
+
pick_session_id: str,
|
|
11
|
+
pick_user_id: str,
|
|
12
|
+
radius_min_scale: float,
|
|
13
|
+
radius_max_scale: float,
|
|
14
|
+
filter_size: float,
|
|
15
|
+
pick_objects: List[str],
|
|
16
|
+
runIDs: List[str],
|
|
17
|
+
n_procs: int,
|
|
18
|
+
):
|
|
19
|
+
from octopi.workflows import localize
|
|
20
|
+
|
|
21
|
+
# Run 3D Localization
|
|
22
|
+
localize(
|
|
23
|
+
copick_config_path, voxel_size, seg_info, pick_user_id, pick_session_id, n_procs,
|
|
24
|
+
method, filter_size, radius_min_scale, radius_max_scale,
|
|
25
|
+
run_ids = runIDs, pick_objects = pick_objects
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def save_parameters(seg_info: Tuple[str, str, str],
|
|
30
|
+
config: str,
|
|
31
|
+
voxel_size: float,
|
|
32
|
+
pick_session_id: str,
|
|
33
|
+
pick_user_id: str,
|
|
34
|
+
method: str,
|
|
35
|
+
radius_min_scale: float,
|
|
36
|
+
radius_max_scale: float,
|
|
37
|
+
filter_size: float,
|
|
38
|
+
output_path: str):
|
|
39
|
+
|
|
40
|
+
import octopi.utils.io as io
|
|
41
|
+
import pprint
|
|
42
|
+
|
|
43
|
+
# Organize parameters into categories
|
|
44
|
+
params = {
|
|
45
|
+
"input": {
|
|
46
|
+
"config": config,
|
|
47
|
+
"seg_name": seg_info[0],
|
|
48
|
+
"seg_user_id": seg_info[1],
|
|
49
|
+
"seg_session_id": seg_info[2],
|
|
50
|
+
"voxel_size": voxel_size
|
|
51
|
+
},
|
|
52
|
+
"output": {
|
|
53
|
+
"pick_session_id": pick_session_id,
|
|
54
|
+
"pick_user_id": pick_user_id
|
|
55
|
+
},
|
|
56
|
+
"parameters": {
|
|
57
|
+
"method": method,
|
|
58
|
+
"radius_min_scale": radius_min_scale,
|
|
59
|
+
"radius_max_scale": radius_max_scale,
|
|
60
|
+
"filter_size": filter_size,
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Print the parameters
|
|
65
|
+
print(f"\nParameters for Localization:")
|
|
66
|
+
pprint.pprint(params); print()
|
|
67
|
+
|
|
68
|
+
# Save to YAML file
|
|
69
|
+
io.save_parameters_yaml(params, output_path)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@click.command('localize')
|
|
73
|
+
# Output Arguments
|
|
74
|
+
@click.option('-pui', '--pick-user-id', type=str, default='octopi',
|
|
75
|
+
help="User ID for the particle picks")
|
|
76
|
+
@click.option('-psid', '--pick-session-id', type=str, default='1',
|
|
77
|
+
help="Session ID for the particle picks")
|
|
78
|
+
# Localize Arguments
|
|
79
|
+
@click.option('-np', '--n-procs', type=int, default=8,
|
|
80
|
+
help="Number of CPU processes to parallelize runs across. Defaults to the max number of cores available or available runs")
|
|
81
|
+
@click.option('-obj', '--pick-objects', type=str, default=None,
|
|
82
|
+
callback=lambda ctx, param, value: parsers.parse_list(value) if value else None,
|
|
83
|
+
help="Specific Objects to Find Picks for")
|
|
84
|
+
@click.option('-fs', '--filter-size', type=int, default=10,
|
|
85
|
+
help="Filter size for localization")
|
|
86
|
+
@click.option('-rmax','--radius-max-scale', type=float, default=1.0,
|
|
87
|
+
help="Maximum radius scale for particles")
|
|
88
|
+
@click.option('-rmin', '--radius-min-scale', type=float, default=0.5,
|
|
89
|
+
help="Minimum radius scale for particles")
|
|
90
|
+
# Input Arguments
|
|
91
|
+
@click.option('--runIDs', type=str, default=None,
|
|
92
|
+
callback=lambda ctx, param, value: parsers.parse_list(value) if value else None,
|
|
93
|
+
help="List of runIDs to run inference on, e.g., run1,run2,run3 or [run1,run2,run3]")
|
|
94
|
+
@click.option('-vs', '--voxel-size', type=float, default=10,
|
|
95
|
+
help="Voxel size for localization")
|
|
96
|
+
@click.option('-sinfo', '--seg-info', type=str, default='predict,octopi,1',
|
|
97
|
+
callback=lambda ctx, param, value: parsers.parse_target(value),
|
|
98
|
+
help='Query for the organelles segmentations (e.g., "name" or "name,user_id,session_id")')
|
|
99
|
+
@click.option('-m', '--method', type=click.Choice(['watershed', 'com'], case_sensitive=False),
|
|
100
|
+
default='watershed',
|
|
101
|
+
help="Localization method to use")
|
|
102
|
+
@click.option('-c', '--config', type=click.Path(exists=True), required=True,
|
|
103
|
+
help="Path to the CoPick configuration file")
|
|
104
|
+
def cli(config, method, seg_info, voxel_size, runids,
|
|
105
|
+
radius_min_scale, radius_max_scale, filter_size, pick_objects, n_procs,
|
|
106
|
+
pick_session_id, pick_user_id):
|
|
107
|
+
"""
|
|
108
|
+
Convert Segmentation Masks to 3D Particle Coordinates.
|
|
109
|
+
|
|
110
|
+
This command converts segmentation masks into 3D particle coordinates using size-based filtering.
|
|
111
|
+
It supports two localization methods: watershed and center of mass. The resulting particle coordinates
|
|
112
|
+
in your copick project, organized by segmentation name, user ID, and session ID for easy tracking and comparison.
|
|
113
|
+
|
|
114
|
+
\b
|
|
115
|
+
Examples:
|
|
116
|
+
# Localize particles with default settings
|
|
117
|
+
octopi localize -c config.json --seg-info predict,octopi,1
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
run_localize(config, method, seg_info, voxel_size, runids,
|
|
121
|
+
radius_min_scale, radius_max_scale, filter_size, pick_objects, n_procs,
|
|
122
|
+
pick_session_id, pick_user_id)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def run_localize(config, method, seg_info, voxel_size, runids,
|
|
126
|
+
radius_min_scale, radius_max_scale, filter_size, pick_objects, n_procs,
|
|
127
|
+
pick_session_id, pick_user_id):
|
|
128
|
+
"""
|
|
129
|
+
Run the localize command.
|
|
130
|
+
"""
|
|
131
|
+
import octopi.utils.io as io
|
|
132
|
+
import multiprocess as mp
|
|
133
|
+
import copick, os
|
|
134
|
+
|
|
135
|
+
# Save JSON with Parameters
|
|
136
|
+
root = copick.from_file(config)
|
|
137
|
+
overlay_root = io.remove_prefix(root.config.overlay_root)
|
|
138
|
+
basepath = os.path.join(overlay_root, 'logs')
|
|
139
|
+
os.makedirs(basepath, exist_ok=True)
|
|
140
|
+
output_path = os.path.join(basepath, f'localize-{pick_user_id}_{pick_session_id}.yaml')
|
|
141
|
+
|
|
142
|
+
save_parameters(
|
|
143
|
+
seg_info=seg_info,
|
|
144
|
+
config=config,
|
|
145
|
+
voxel_size=voxel_size,
|
|
146
|
+
pick_session_id=pick_session_id,
|
|
147
|
+
pick_user_id=pick_user_id,
|
|
148
|
+
method=method,
|
|
149
|
+
radius_min_scale=radius_min_scale,
|
|
150
|
+
radius_max_scale=radius_max_scale,
|
|
151
|
+
filter_size=filter_size,
|
|
152
|
+
output_path=output_path
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Set multiprocessing start method
|
|
156
|
+
mp.set_start_method("spawn")
|
|
157
|
+
|
|
158
|
+
pick_particles(
|
|
159
|
+
copick_config_path=config,
|
|
160
|
+
method=method,
|
|
161
|
+
seg_info=seg_info,
|
|
162
|
+
voxel_size=voxel_size,
|
|
163
|
+
pick_session_id=pick_session_id,
|
|
164
|
+
pick_user_id=pick_user_id,
|
|
165
|
+
radius_min_scale=radius_min_scale,
|
|
166
|
+
radius_max_scale=radius_max_scale,
|
|
167
|
+
filter_size=filter_size,
|
|
168
|
+
runIDs=runids,
|
|
169
|
+
pick_objects=pick_objects,
|
|
170
|
+
n_procs=n_procs,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if __name__ == "__main__":
|
|
175
|
+
cli()
|
|
176
|
+
|