ci-deconvolve 0.1.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.
- ci_deconvolve/__init__.py +3 -0
- ci_deconvolve/cli.py +292 -0
- ci_deconvolve-0.1.0.dist-info/METADATA +232 -0
- ci_deconvolve-0.1.0.dist-info/RECORD +12 -0
- ci_deconvolve-0.1.0.dist-info/WHEEL +5 -0
- ci_deconvolve-0.1.0.dist-info/entry_points.txt +2 -0
- ci_deconvolve-0.1.0.dist-info/top_level.txt +2 -0
- core/__init__.py +23 -0
- core/_meta_helpers.py +272 -0
- core/deconvolve.py +1584 -0
- core/deconvolve_ci.py +2200 -0
- core/ome_zarr_io.py +258 -0
ci_deconvolve/cli.py
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import shutil
|
|
6
|
+
import sys
|
|
7
|
+
import time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Iterable
|
|
10
|
+
|
|
11
|
+
LOGGER = logging.getLogger("ci_deconvolve")
|
|
12
|
+
|
|
13
|
+
OME_TIFF_SUFFIXES = (".ome.tif", ".ome.tiff")
|
|
14
|
+
ZARR_SUFFIXES = (".zarr", ".ome.zarr")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _require_torch() -> None:
|
|
18
|
+
try:
|
|
19
|
+
import torch # noqa: F401
|
|
20
|
+
except Exception as exc:
|
|
21
|
+
raise RuntimeError(
|
|
22
|
+
"PyTorch is required but is not installed or cannot be imported. "
|
|
23
|
+
"Install a CPU or CUDA PyTorch build first, then rerun ci_deconvolve. "
|
|
24
|
+
"See https://pytorch.org/get-started/locally/ for the current install command."
|
|
25
|
+
) from exc
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _is_ome_tiff(path: Path) -> bool:
|
|
29
|
+
name = path.name.lower()
|
|
30
|
+
return path.is_file() and any(name.endswith(suffix) for suffix in OME_TIFF_SUFFIXES)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _is_ome_zarr(path: Path) -> bool:
|
|
34
|
+
name = path.name.lower()
|
|
35
|
+
return path.is_dir() and any(name.endswith(suffix) for suffix in ZARR_SUFFIXES)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _discover_inputs(path: Path) -> list[Path]:
|
|
39
|
+
if _is_ome_tiff(path) or _is_ome_zarr(path):
|
|
40
|
+
return [path]
|
|
41
|
+
if path.is_dir():
|
|
42
|
+
inputs = [
|
|
43
|
+
child
|
|
44
|
+
for child in sorted(path.iterdir(), key=lambda p: p.name.lower())
|
|
45
|
+
if _is_ome_tiff(child) or _is_ome_zarr(child)
|
|
46
|
+
]
|
|
47
|
+
if inputs:
|
|
48
|
+
return inputs
|
|
49
|
+
raise ValueError(f"No OME-TIFF files or OME-Zarr folders found in {path}")
|
|
50
|
+
raise ValueError(
|
|
51
|
+
f"Unsupported input {path}. Expected .ome.tif/.ome.tiff, .zarr/.ome.zarr, "
|
|
52
|
+
"or a folder containing those inputs."
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _stem(path: Path) -> str:
|
|
57
|
+
name = path.name
|
|
58
|
+
lower = name.lower()
|
|
59
|
+
for suffix in (".ome.tiff", ".ome.tif", ".ome.zarr", ".zarr"):
|
|
60
|
+
if lower.endswith(suffix):
|
|
61
|
+
return name[: -len(suffix)]
|
|
62
|
+
return path.stem
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _parse_int_list(raw: str) -> list[int]:
|
|
66
|
+
values = []
|
|
67
|
+
for item in str(raw or "").replace(";", ",").split(","):
|
|
68
|
+
item = item.strip()
|
|
69
|
+
if not item:
|
|
70
|
+
continue
|
|
71
|
+
try:
|
|
72
|
+
values.append(max(1, int(float(item))))
|
|
73
|
+
except ValueError as exc:
|
|
74
|
+
raise argparse.ArgumentTypeError(f"Invalid iteration count: {item!r}") from exc
|
|
75
|
+
if not values:
|
|
76
|
+
raise argparse.ArgumentTypeError("At least one iteration count is required")
|
|
77
|
+
return values
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _parse_float_list(raw: str | None) -> list[float] | None:
|
|
81
|
+
if raw is None:
|
|
82
|
+
return None
|
|
83
|
+
values = []
|
|
84
|
+
for item in str(raw).replace(";", ",").split(","):
|
|
85
|
+
item = item.strip()
|
|
86
|
+
if not item:
|
|
87
|
+
continue
|
|
88
|
+
values.append(float(item))
|
|
89
|
+
return values or None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _parse_float_or_auto(raw: str):
|
|
93
|
+
text = str(raw).strip().lower()
|
|
94
|
+
if text == "auto":
|
|
95
|
+
return "auto"
|
|
96
|
+
if text in {"none", "0", "0.0"}:
|
|
97
|
+
return 0.0
|
|
98
|
+
return float(text)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
102
|
+
parser = argparse.ArgumentParser(
|
|
103
|
+
prog="ci_deconvolve",
|
|
104
|
+
description="Run CI-RL deconvolution on OME-TIFF and OME-Zarr inputs.",
|
|
105
|
+
)
|
|
106
|
+
parser.add_argument("input_path", nargs="?", help="Input file/folder. Alias for --input.")
|
|
107
|
+
parser.add_argument("--input", dest="input_option", help="OME-TIFF, OME-Zarr, or folder.")
|
|
108
|
+
parser.add_argument("--output", required=True, help="Output folder.")
|
|
109
|
+
parser.add_argument(
|
|
110
|
+
"--output-format",
|
|
111
|
+
choices=("ome-tiff", "ome-zarr"),
|
|
112
|
+
default="ome-tiff",
|
|
113
|
+
help="Output format. Default: ome-tiff.",
|
|
114
|
+
)
|
|
115
|
+
parser.add_argument("--iterations", type=_parse_int_list, default=[40])
|
|
116
|
+
parser.add_argument("--device", choices=("auto", "cpu", "cuda"), default="auto")
|
|
117
|
+
parser.add_argument("--background", default="auto")
|
|
118
|
+
parser.add_argument("--offset", default="auto")
|
|
119
|
+
parser.add_argument("--damping", default="none")
|
|
120
|
+
parser.add_argument("--prefilter-sigma", type=float, default=0.0)
|
|
121
|
+
parser.add_argument(
|
|
122
|
+
"--start",
|
|
123
|
+
choices=(
|
|
124
|
+
"auto",
|
|
125
|
+
"flat",
|
|
126
|
+
"percentile_flat",
|
|
127
|
+
"observed",
|
|
128
|
+
"observed_bgsub",
|
|
129
|
+
"lowpass",
|
|
130
|
+
"lowpass_bgsub",
|
|
131
|
+
"hybrid",
|
|
132
|
+
),
|
|
133
|
+
default="auto",
|
|
134
|
+
)
|
|
135
|
+
parser.add_argument("--convergence", choices=("auto", "fixed", "none"), default="auto")
|
|
136
|
+
parser.add_argument("--rel-threshold", type=float, default=0.005)
|
|
137
|
+
parser.add_argument("--check-every", type=int, default=5)
|
|
138
|
+
parser.add_argument("--na", type=float, default=1.4)
|
|
139
|
+
parser.add_argument("--refractive-index", type=float, default=1.515)
|
|
140
|
+
parser.add_argument("--sample-ri", type=float, default=1.47)
|
|
141
|
+
parser.add_argument("--microscope-type", choices=("widefield", "confocal"), default="confocal")
|
|
142
|
+
parser.add_argument("--emission-wl", default="520")
|
|
143
|
+
parser.add_argument("--excitation-wl", default="488")
|
|
144
|
+
parser.add_argument("--pinhole-airy", default="1.0")
|
|
145
|
+
parser.add_argument("--pixel-size-xy", type=float, default=0.065, help="Micrometers.")
|
|
146
|
+
parser.add_argument("--pixel-size-z", type=float, default=0.2, help="Micrometers.")
|
|
147
|
+
parser.add_argument(
|
|
148
|
+
"--overrule-metadata",
|
|
149
|
+
action="store_true",
|
|
150
|
+
help="Use CLI metadata values even when image metadata is present.",
|
|
151
|
+
)
|
|
152
|
+
parser.add_argument(
|
|
153
|
+
"--two-d-mode",
|
|
154
|
+
choices=("auto", "legacy_2d"),
|
|
155
|
+
default="auto",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument(
|
|
158
|
+
"--two-d-wf-aggressiveness",
|
|
159
|
+
choices=("conservative", "balanced", "strong"),
|
|
160
|
+
default="balanced",
|
|
161
|
+
)
|
|
162
|
+
parser.add_argument("--two-d-wf-bg-radius-um", type=float, default=0.5)
|
|
163
|
+
parser.add_argument("--two-d-wf-bg-scale", type=float, default=1.0)
|
|
164
|
+
parser.add_argument("-v", "--verbose", action="store_true")
|
|
165
|
+
return parser
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _normalise_convergence(value: str) -> str:
|
|
169
|
+
return "fixed" if value == "none" else value
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _output_path(input_path: Path, output_dir: Path, output_format: str) -> Path:
|
|
173
|
+
if output_format == "ome-zarr":
|
|
174
|
+
return output_dir / f"{_stem(input_path)}_decon.ome.zarr"
|
|
175
|
+
return output_dir / f"{_stem(input_path)}_decon.ome.tiff"
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _run_one(input_path: Path, output_dir: Path, args: argparse.Namespace) -> Path:
|
|
179
|
+
from core.deconvolve import deconvolve_image, save_result
|
|
180
|
+
from core.ome_zarr_io import is_hcs_plate, save_result_ome_zarr
|
|
181
|
+
|
|
182
|
+
if _is_ome_zarr(input_path) and is_hcs_plate(input_path):
|
|
183
|
+
if args.output_format == "ome-tiff":
|
|
184
|
+
raise ValueError("HCS plate OME-Zarr input cannot be written as one OME-TIFF output.")
|
|
185
|
+
raise ValueError("HCS plate OME-Zarr output is not supported by the focused CLI yet.")
|
|
186
|
+
|
|
187
|
+
device = None if args.device == "auto" else args.device
|
|
188
|
+
result = deconvolve_image(
|
|
189
|
+
input_path,
|
|
190
|
+
method="ci_rl",
|
|
191
|
+
niter=args.iterations,
|
|
192
|
+
na=args.na,
|
|
193
|
+
refractive_index=args.refractive_index,
|
|
194
|
+
sample_refractive_index=args.sample_ri,
|
|
195
|
+
microscope_type=args.microscope_type,
|
|
196
|
+
emission_wavelengths=_parse_float_list(args.emission_wl),
|
|
197
|
+
excitation_wavelengths=_parse_float_list(args.excitation_wl),
|
|
198
|
+
pinhole_airy_units=_parse_float_list(args.pinhole_airy),
|
|
199
|
+
overrule_metadata=bool(args.overrule_metadata),
|
|
200
|
+
pixel_size_xy=args.pixel_size_xy,
|
|
201
|
+
pixel_size_z=args.pixel_size_z,
|
|
202
|
+
background=_parse_float_or_auto(args.background),
|
|
203
|
+
damping=_parse_float_or_auto(args.damping),
|
|
204
|
+
offset=_parse_float_or_auto(args.offset),
|
|
205
|
+
prefilter_sigma=max(0.0, float(args.prefilter_sigma)),
|
|
206
|
+
start=args.start,
|
|
207
|
+
convergence=_normalise_convergence(args.convergence),
|
|
208
|
+
rel_threshold=max(1e-8, float(args.rel_threshold)),
|
|
209
|
+
check_every=max(1, int(args.check_every)),
|
|
210
|
+
two_d_mode=args.two_d_mode,
|
|
211
|
+
two_d_wf_aggressiveness=args.two_d_wf_aggressiveness,
|
|
212
|
+
two_d_wf_bg_radius_um=max(0.1, float(args.two_d_wf_bg_radius_um)),
|
|
213
|
+
two_d_wf_bg_scale=max(0.1, float(args.two_d_wf_bg_scale)),
|
|
214
|
+
device=device,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
out_path = _output_path(input_path, output_dir, args.output_format)
|
|
218
|
+
if args.output_format == "ome-zarr":
|
|
219
|
+
return save_result_ome_zarr(result, out_path)
|
|
220
|
+
|
|
221
|
+
tmp_dir = output_dir / ".ci_deconvolve_tmp"
|
|
222
|
+
tmp_dir.mkdir(parents=True, exist_ok=True)
|
|
223
|
+
tmp_path = tmp_dir / out_path.name
|
|
224
|
+
try:
|
|
225
|
+
save_result(result, tmp_path)
|
|
226
|
+
if out_path.exists():
|
|
227
|
+
out_path.unlink()
|
|
228
|
+
shutil.move(str(tmp_path), str(out_path))
|
|
229
|
+
finally:
|
|
230
|
+
shutil.rmtree(tmp_dir, ignore_errors=True)
|
|
231
|
+
return out_path
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def run(argv: Iterable[str] | None = None) -> int:
|
|
235
|
+
parser = _build_parser()
|
|
236
|
+
args = parser.parse_args(list(argv) if argv is not None else None)
|
|
237
|
+
logging.basicConfig(
|
|
238
|
+
level=logging.INFO if args.verbose else logging.WARNING,
|
|
239
|
+
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
input_value = args.input_option or args.input_path
|
|
243
|
+
if not input_value:
|
|
244
|
+
parser.error("an input path is required via --input or positional input_path")
|
|
245
|
+
input_path = Path(input_value)
|
|
246
|
+
output_dir = Path(args.output)
|
|
247
|
+
|
|
248
|
+
_require_torch()
|
|
249
|
+
inputs = _discover_inputs(input_path)
|
|
250
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
251
|
+
|
|
252
|
+
print("ci_deconvolve")
|
|
253
|
+
print(f" method : ci_rl")
|
|
254
|
+
print(f" input count : {len(inputs)}")
|
|
255
|
+
print(f" output : {output_dir}")
|
|
256
|
+
print(f" output format: {args.output_format}")
|
|
257
|
+
print(f" iterations : {', '.join(str(v) for v in args.iterations)}")
|
|
258
|
+
|
|
259
|
+
failures: list[tuple[Path, Exception]] = []
|
|
260
|
+
for index, item in enumerate(inputs, start=1):
|
|
261
|
+
print(f"\n[{index}/{len(inputs)}] {item}")
|
|
262
|
+
start = time.time()
|
|
263
|
+
try:
|
|
264
|
+
out_path = _run_one(item, output_dir, args)
|
|
265
|
+
print(f" saved: {out_path}")
|
|
266
|
+
print(f" time : {time.time() - start:.1f}s")
|
|
267
|
+
except Exception as exc:
|
|
268
|
+
failures.append((item, exc))
|
|
269
|
+
print(f" ERROR: {exc}", file=sys.stderr)
|
|
270
|
+
|
|
271
|
+
if failures:
|
|
272
|
+
print(f"\nFailed inputs: {len(failures)}", file=sys.stderr)
|
|
273
|
+
for item, exc in failures:
|
|
274
|
+
print(f" {item}: {exc}", file=sys.stderr)
|
|
275
|
+
return 1
|
|
276
|
+
print("\nci_deconvolve complete.")
|
|
277
|
+
return 0
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def main(argv: Iterable[str] | None = None) -> int:
|
|
281
|
+
try:
|
|
282
|
+
return run(argv)
|
|
283
|
+
except KeyboardInterrupt:
|
|
284
|
+
print("Interrupted.", file=sys.stderr)
|
|
285
|
+
return 130
|
|
286
|
+
except Exception as exc:
|
|
287
|
+
print(f"ci_deconvolve failed: {exc}", file=sys.stderr)
|
|
288
|
+
return 1
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
if __name__ == "__main__":
|
|
292
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ci-deconvolve
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Focused command-line CI-RL deconvolution for OME-TIFF and OME-Zarr images.
|
|
5
|
+
Author: NL-BioImaging contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Keywords: microscopy,deconvolution,ome-tiff,ome-zarr,richardson-lucy
|
|
8
|
+
Classifier: Development Status :: 3 - Alpha
|
|
9
|
+
Classifier: Intended Audience :: Science/Research
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
13
|
+
Requires-Python: >=3.12
|
|
14
|
+
Description-Content-Type: text/markdown
|
|
15
|
+
Requires-Dist: numpy<3,>=1.26
|
|
16
|
+
Requires-Dist: Pillow>=10.0
|
|
17
|
+
Requires-Dist: tifffile>=2024.1.30
|
|
18
|
+
Requires-Dist: imagecodecs>=2024.1.1
|
|
19
|
+
Requires-Dist: bioio==3.2.0
|
|
20
|
+
Requires-Dist: bioio-ome-tiff==1.4.0
|
|
21
|
+
Requires-Dist: ome-zarr<0.19,>=0.18
|
|
22
|
+
Requires-Dist: numcodecs>=0.14
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: pytest; extra == "dev"
|
|
25
|
+
Requires-Dist: build; extra == "dev"
|
|
26
|
+
Requires-Dist: twine; extra == "dev"
|
|
27
|
+
|
|
28
|
+
# ci-deconvolve
|
|
29
|
+
|
|
30
|
+
Focused command-line CI-RL deconvolution for OME-TIFF and OME-Zarr images.
|
|
31
|
+
|
|
32
|
+
This package installs the `ci_deconvolve` command. On Windows, `pip` also
|
|
33
|
+
creates a `ci_deconvolve.exe` console launcher in the active environment.
|
|
34
|
+
|
|
35
|
+
## Prerequisite: PyTorch
|
|
36
|
+
|
|
37
|
+
PyTorch is required but is not bundled in this package. Install the CPU or CUDA
|
|
38
|
+
build that matches your system before running `ci_deconvolve`.
|
|
39
|
+
|
|
40
|
+
Examples:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pip install torch
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
or install a CUDA build using the command from:
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
https://pytorch.org/get-started/locally/
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Install
|
|
53
|
+
|
|
54
|
+
From this folder during development:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
pip install -e .
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
From PyPI after publishing:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install ci-deconvolve
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The package depends on `ome-zarr` for OME-Zarr reading and writing. `zarr` is
|
|
67
|
+
installed transitively by `ome-zarr`; it is not declared as a direct dependency.
|
|
68
|
+
|
|
69
|
+
## Usage
|
|
70
|
+
|
|
71
|
+
Run one OME-TIFF file:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ci_deconvolve --input image.ome.tiff --output out
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Run one OME-Zarr image:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ci_deconvolve --input image.ome.zarr --output out --output-format ome-zarr
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Run every supported immediate child in a folder:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ci_deconvolve --input in_folder --output out_folder --iterations 40
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Only these inputs are accepted:
|
|
90
|
+
|
|
91
|
+
- `.ome.tif`
|
|
92
|
+
- `.ome.tiff`
|
|
93
|
+
- `.zarr` / `.ome.zarr` directories
|
|
94
|
+
|
|
95
|
+
The CLI always runs `ci_rl`. It intentionally has no `--method` option.
|
|
96
|
+
|
|
97
|
+
## Examples
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
ci_deconvolve --input image.ome.tiff --output out ^
|
|
101
|
+
--iterations 40 ^
|
|
102
|
+
--device auto ^
|
|
103
|
+
--output-format ome-tiff ^
|
|
104
|
+
--emission-wl 520 ^
|
|
105
|
+
--excitation-wl 488 ^
|
|
106
|
+
--pixel-size-xy 0.065 ^
|
|
107
|
+
--pixel-size-z 0.2
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Use `--overrule-metadata` when CLI metadata values should replace image
|
|
111
|
+
metadata. Without it, CLI metadata values are used as fallbacks.
|
|
112
|
+
|
|
113
|
+
Per-channel values can be comma-separated. If there are more channels than
|
|
114
|
+
values, the last value is reused for the remaining channels.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
ci_deconvolve --input image.ome.tiff --output out ^
|
|
118
|
+
--iterations 40,60,80 ^
|
|
119
|
+
--emission-wl 520,610 ^
|
|
120
|
+
--excitation-wl 488,561 ^
|
|
121
|
+
--pinhole-airy 1.0,1.2
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Parameters
|
|
125
|
+
|
|
126
|
+
### Required Paths
|
|
127
|
+
|
|
128
|
+
| Option | Default | Description |
|
|
129
|
+
| --- | --- | --- |
|
|
130
|
+
| `input_path` | none | Optional positional input path. Use this or `--input`. |
|
|
131
|
+
| `--input PATH` | none | Input OME-TIFF file, OME-Zarr folder, or folder containing supported inputs. |
|
|
132
|
+
| `--output DIR` | required | Output folder. Created if it does not exist. |
|
|
133
|
+
|
|
134
|
+
Supported inputs are immediate files/folders only:
|
|
135
|
+
|
|
136
|
+
- `.ome.tif`
|
|
137
|
+
- `.ome.tiff`
|
|
138
|
+
- `.zarr` / `.ome.zarr` directories
|
|
139
|
+
|
|
140
|
+
HCS plate OME-Zarr inputs are detected and rejected clearly; this focused CLI
|
|
141
|
+
does not process plate fields.
|
|
142
|
+
|
|
143
|
+
### Output And Compute
|
|
144
|
+
|
|
145
|
+
| Option | Default | Description |
|
|
146
|
+
| --- | --- | --- |
|
|
147
|
+
| `--output-format ome-tiff\|ome-zarr` | `ome-tiff` | Writes `<stem>_decon.ome.tiff` or `<stem>_decon.ome.zarr`. |
|
|
148
|
+
| `--iterations N[,N...]` | `40` | CI-RL iteration count. A comma- or semicolon-separated list applies per channel, with the last value reused for extra channels. |
|
|
149
|
+
| `--device auto\|cpu\|cuda` | `auto` | Compute device. `auto` lets PyTorch choose CUDA when available, otherwise CPU. |
|
|
150
|
+
| `-v`, `--verbose` | off | Enable INFO logging from the CLI and core deconvolution code. |
|
|
151
|
+
|
|
152
|
+
### CI-RL Solver
|
|
153
|
+
|
|
154
|
+
| Option | Default | Description |
|
|
155
|
+
| --- | --- | --- |
|
|
156
|
+
| `--background VALUE\|auto` | `auto` | Background level used by the solver. `auto` estimates it from the image. Numeric values are accepted. |
|
|
157
|
+
| `--offset VALUE\|auto\|none` | `auto` | Positive offset added before iteration and removed afterwards. Use `none`, `0`, or `0.0` to disable. |
|
|
158
|
+
| `--damping VALUE\|auto\|none` | `none` | Noise-gated damping strength. Use `auto` for automatic damping, numeric values for manual damping, or `none`/`0` to disable. |
|
|
159
|
+
| `--prefilter-sigma VALUE` | `0.0` | Optional Anscombe/low-pass prefilter sigma. `0.0` disables prefiltering. |
|
|
160
|
+
| `--start MODE` | `auto` | Initial estimate mode. Choices: `auto`, `flat`, `percentile_flat`, `observed`, `observed_bgsub`, `lowpass`, `lowpass_bgsub`, `hybrid`. |
|
|
161
|
+
| `--convergence auto\|fixed\|none` | `auto` | `auto` enables early stopping based on convergence. `fixed` and `none` both run the requested iteration count. |
|
|
162
|
+
| `--rel-threshold VALUE` | `0.005` | Relative convergence threshold used when `--convergence auto`. Values are clamped to at least `1e-8`. |
|
|
163
|
+
| `--check-every N` | `5` | Check convergence every N iterations. Values below 1 are clamped to 1. |
|
|
164
|
+
|
|
165
|
+
### Metadata And PSF
|
|
166
|
+
|
|
167
|
+
These values are used to generate the point spread function. By default, image
|
|
168
|
+
metadata wins and CLI values fill in missing metadata. With
|
|
169
|
+
`--overrule-metadata`, CLI values replace image metadata.
|
|
170
|
+
|
|
171
|
+
| Option | Default | Description |
|
|
172
|
+
| --- | --- | --- |
|
|
173
|
+
| `--na VALUE` | `1.4` | Numerical aperture. |
|
|
174
|
+
| `--refractive-index VALUE` | `1.515` | Immersion medium refractive index, typically oil. |
|
|
175
|
+
| `--sample-ri VALUE` | `1.47` | Sample medium refractive index. |
|
|
176
|
+
| `--microscope-type widefield\|confocal` | `confocal` | Microscope model used for PSF generation. |
|
|
177
|
+
| `--emission-wl VALUE[,VALUE...]` | `520` | Emission wavelength(s), in nm. Per-channel list supported. |
|
|
178
|
+
| `--excitation-wl VALUE[,VALUE...]` | `488` | Excitation wavelength(s), in nm. Per-channel list supported. |
|
|
179
|
+
| `--pinhole-airy VALUE[,VALUE...]` | `1.0` | Confocal pinhole diameter in Airy units. Per-channel list supported. |
|
|
180
|
+
| `--pixel-size-xy VALUE` | `0.065` | Lateral pixel size in micrometers. Applied to X and Y. |
|
|
181
|
+
| `--pixel-size-z VALUE` | `0.2` | Axial pixel size in micrometers. |
|
|
182
|
+
| `--overrule-metadata` | off | Replace image metadata with CLI metadata values instead of only using CLI values as fallbacks. |
|
|
183
|
+
|
|
184
|
+
List syntax:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
--emission-wl 520,610
|
|
188
|
+
--excitation-wl 488;561
|
|
189
|
+
--iterations 30,40,50
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Commas and semicolons are both accepted.
|
|
193
|
+
|
|
194
|
+
### 2D Widefield Options
|
|
195
|
+
|
|
196
|
+
These only affect 2D widefield data when `--microscope-type widefield` and
|
|
197
|
+
`--two-d-mode auto` are used.
|
|
198
|
+
|
|
199
|
+
| Option | Default | Description |
|
|
200
|
+
| --- | --- | --- |
|
|
201
|
+
| `--two-d-mode auto\|legacy_2d` | `auto` | `auto` enables the enhanced 2D widefield path. `legacy_2d` uses the historical pure-2D PSF behavior. |
|
|
202
|
+
| `--two-d-wf-aggressiveness conservative\|balanced\|strong` | `balanced` | Strength preset for the enhanced 2D widefield model. |
|
|
203
|
+
| `--two-d-wf-bg-radius-um VALUE` | `0.5` | Background-estimation radius in micrometers. Values below `0.1` are clamped to `0.1`. |
|
|
204
|
+
| `--two-d-wf-bg-scale VALUE` | `1.0` | Multiplier for the estimated 2D widefield background. Values below `0.1` are clamped to `0.1`. |
|
|
205
|
+
|
|
206
|
+
## Metadata Override Behavior
|
|
207
|
+
|
|
208
|
+
Without `--overrule-metadata`:
|
|
209
|
+
|
|
210
|
+
- Existing OME metadata is preserved.
|
|
211
|
+
- CLI metadata values are used only when a value is missing or invalid.
|
|
212
|
+
- This is the recommended mode for well-annotated OME-TIFF or OME-Zarr data.
|
|
213
|
+
|
|
214
|
+
With `--overrule-metadata`:
|
|
215
|
+
|
|
216
|
+
- CLI metadata values replace image metadata.
|
|
217
|
+
- Use this when source metadata is known to be wrong, incomplete, or converted
|
|
218
|
+
with incorrect physical pixel sizes or wavelengths.
|
|
219
|
+
|
|
220
|
+
Example:
|
|
221
|
+
|
|
222
|
+
```bash
|
|
223
|
+
ci_deconvolve --input image.ome.tiff --output out ^
|
|
224
|
+
--overrule-metadata ^
|
|
225
|
+
--microscope-type widefield ^
|
|
226
|
+
--na 1.40 ^
|
|
227
|
+
--refractive-index 1.515 ^
|
|
228
|
+
--sample-ri 1.47 ^
|
|
229
|
+
--pixel-size-xy 0.065 ^
|
|
230
|
+
--pixel-size-z 0.200 ^
|
|
231
|
+
--emission-wl 520,610
|
|
232
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ci_deconvolve/__init__.py,sha256=6KvpHMilH5eQK56n8a7npW11k1soiTXAUIsPaG-RAs4,83
|
|
2
|
+
ci_deconvolve/cli.py,sha256=b1jS5DoYKKLe419xAG1RTZrzFlEKbSe9IsW-y2mQlow,10536
|
|
3
|
+
core/__init__.py,sha256=-yMZ_O1WisEVjusjWyUbj3Q8o4q2C8EU8cdsvgAq-L8,460
|
|
4
|
+
core/_meta_helpers.py,sha256=2VndQriokjD_BVsy1MyE__DPbuOr96iL5GDHTc5PZmY,11223
|
|
5
|
+
core/deconvolve.py,sha256=2mgDsc3XlBu-WLYgfa2bs298tEtR8BUYpczQrCw8Zro,62327
|
|
6
|
+
core/deconvolve_ci.py,sha256=sX1M0aONkB_Zaf1WbUygnaTMdyfFHQwzYVfw5PsAYws,80379
|
|
7
|
+
core/ome_zarr_io.py,sha256=0zmHwJYKGdkgRkr_0KN9xK1f4dKFHO_GVHvsCoSyp8w,8968
|
|
8
|
+
ci_deconvolve-0.1.0.dist-info/METADATA,sha256=V3l4XRg-637uAggxqSVsiFxg3BQ6cZP1Gv3wwKyBvrM,8455
|
|
9
|
+
ci_deconvolve-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
10
|
+
ci_deconvolve-0.1.0.dist-info/entry_points.txt,sha256=CUy5vVJU6ZzAmnfjI-qduljcgZCMpdsMrA-Zi3g85qo,57
|
|
11
|
+
ci_deconvolve-0.1.0.dist-info/top_level.txt,sha256=76r0e8VMIoGrdcRgh-lsaWbI8bvBgRn64LkZgd70hIg,19
|
|
12
|
+
ci_deconvolve-0.1.0.dist-info/RECORD,,
|
core/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Focused core package for the ci_deconvolve CLI."""
|
|
2
|
+
|
|
3
|
+
from .deconvolve import (
|
|
4
|
+
deconvolve,
|
|
5
|
+
deconvolve_image,
|
|
6
|
+
generate_psf,
|
|
7
|
+
load_image,
|
|
8
|
+
save_mip_png,
|
|
9
|
+
save_result,
|
|
10
|
+
_DEFAULT_PINHOLE_AIRY_UNITS,
|
|
11
|
+
_apply_pinhole_airy_units,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"deconvolve",
|
|
16
|
+
"deconvolve_image",
|
|
17
|
+
"generate_psf",
|
|
18
|
+
"load_image",
|
|
19
|
+
"save_mip_png",
|
|
20
|
+
"save_result",
|
|
21
|
+
"_DEFAULT_PINHOLE_AIRY_UNITS",
|
|
22
|
+
"_apply_pinhole_airy_units",
|
|
23
|
+
]
|