Skip to content

Commit ce94ea8

Browse files
Add CLI for IMOD export
1 parent 3b4c6c4 commit ce94ea8

File tree

3 files changed

+110
-11
lines changed

3 files changed

+110
-11
lines changed

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
entry_points={
1515
"console_scripts": [
1616
"synapse_net.run_segmentation = synaptic_reconstruction.tools.cli:segmentation_cli",
17+
"synapse_net.export_to_imod_points = synaptic_reconstruction.tools.cli:imod_point_cli",
18+
"synapse_net.export_to_imod_objects = synaptic_reconstruction.tools.cli:imod_object_cli",
1719
],
1820
"napari.manifest": [
1921
"synaptic_reconstruction = synaptic_reconstruction:napari.yaml",

synaptic_reconstruction/imod/to_imod.py

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from subprocess import run
99
from typing import Optional, Tuple, Union
1010

11+
import h5py
1112
import imageio.v3 as imageio
1213
import mrcfile
1314
import numpy as np
@@ -16,6 +17,16 @@
1617
from tqdm import tqdm
1718

1819

20+
def _load_segmentation(segmentation_path, segmentation_key):
21+
assert os.path.exists(segmentation_path), segmentation_path
22+
if segmentation_key is None:
23+
seg = imageio.imread(segmentation_path)
24+
else:
25+
with h5py.File(segmentation_path, "r") as f:
26+
seg = f[segmentation_key][:]
27+
return seg
28+
29+
1930
# TODO: this has still some issues with some tomograms that has an offset info.
2031
# For now, this occurs for the inner ear data tomograms; it works for Fidi's STEM tomograms.
2132
# Ben's theory is that this might be due to data form JEOL vs. ThermoFischer microscopes.
@@ -25,22 +36,22 @@ def write_segmentation_to_imod(
2536
mrc_path: str,
2637
segmentation: Union[str, np.ndarray],
2738
output_path: str,
39+
segmentation_key: Optional[str] = None,
2840
) -> None:
2941
"""Write a segmentation to a mod file as closed contour objects.
3042
3143
Args:
3244
mrc_path: The filepath to the mrc file from which the segmentation was derived.
3345
segmentation: The segmentation (either as numpy array or filepath to a .tif file).
3446
output_path: The output path where the mod file will be saved.
47+
segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
3548
"""
3649
cmd = "imodauto"
3750
cmd_path = shutil.which(cmd)
3851
assert cmd_path is not None, f"Could not find the {cmd} imod command."
3952

40-
# Load the segmentation from a tif file in case a filepath was passed.
41-
if isinstance(segmentation, str):
42-
assert os.path.exists(segmentation)
43-
segmentation = imageio.imread(segmentation)
53+
# Load the segmentation case a filepath was passed.
54+
segmentation = _load_segmentation(segmentation, segmentation_key)
4455

4556
# Binarize the segmentation and flip its axes to match the IMOD axis convention.
4657
segmentation = (segmentation > 0).astype("uint8")
@@ -187,6 +198,7 @@ def write_segmentation_to_imod_as_points(
187198
min_radius: Union[int, float],
188199
radius_factor: float = 1.0,
189200
estimate_radius_2d: bool = True,
201+
segmentation_key: Optional[str] = None,
190202
) -> None:
191203
"""Write segmentation results to .mod file with imod point annotations.
192204
@@ -201,6 +213,7 @@ def write_segmentation_to_imod_as_points(
201213
estimate_radius_2d: If true the distance to boundary for determining the centroid and computing
202214
the radius will be computed only in 2d rather than in 3d. This can lead to better results
203215
in case of deformation across the depth axis.
216+
segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
204217
"""
205218

206219
# Read the resolution information from the mrcfile.
@@ -212,7 +225,7 @@ def write_segmentation_to_imod_as_points(
212225

213226
# Extract the center coordinates and radii from the segmentation.
214227
if isinstance(segmentation, str):
215-
segmentation = imageio.imread(segmentation)
228+
segmentation = _load_segmentation(segmentation, segmentation_key)
216229
coordinates, radii = convert_segmentation_to_spheres(
217230
segmentation, resolution=resolution, radius_factor=radius_factor, estimate_radius_2d=estimate_radius_2d
218231
)
@@ -221,16 +234,22 @@ def write_segmentation_to_imod_as_points(
221234
write_points_to_imod(coordinates, radii, segmentation.shape, min_radius, output_path)
222235

223236

224-
# TODO we also need to support .rec files ...
225-
def _get_file_paths(input_path, ext=".mrc"):
237+
def _get_file_paths(input_path, ext=(".mrc", ".rec")):
226238
if not os.path.exists(input_path):
227-
raise Exception(f"Input path not found {input_path}")
239+
raise Exception(f"Input path not found {input_path}.")
240+
241+
if isinstance(ext, str):
242+
ext = (ext,)
228243

229244
if os.path.isfile(input_path):
230245
input_files = [input_path]
231246
input_root = None
232247
else:
233-
input_files = sorted(glob(os.path.join(input_path, "**", f"*{ext}"), recursive=True))
248+
input_files = []
249+
for ex in ext:
250+
input_files.extend(
251+
sorted(glob(os.path.join(input_path, "**", f"*{ex}"), recursive=True))
252+
)
234253
input_root = input_path
235254

236255
return input_files, input_root
@@ -242,6 +261,7 @@ def export_helper(
242261
output_root: str,
243262
export_function: callable,
244263
force: bool = False,
264+
segmentation_key: Optional[str] = None,
245265
) -> None:
246266
"""
247267
Helper function to run imod export for files in a directory.
@@ -258,9 +278,10 @@ def export_helper(
258278
the path to the segmentation in a .tif file and the output path as only arguments.
259279
If you want to pass additional arguments to this function the use 'funtools.partial'
260280
force: Whether to rerun segmentation for output files that are already present.
281+
segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
261282
"""
262283
input_files, input_root = _get_file_paths(input_path)
263-
segmentation_files, _ = _get_file_paths(segmentation_path, ext=".tif")
284+
segmentation_files, _ = _get_file_paths(segmentation_path, ext=".tif" if segmentation_key is None else ".h5")
264285
assert len(input_files) == len(segmentation_files)
265286

266287
for input_path, seg_path in tqdm(zip(input_files, segmentation_files), total=len(input_files)):
@@ -279,4 +300,4 @@ def export_helper(
279300
continue
280301

281302
os.makedirs(os.path.split(output_path)[0], exist_ok=True)
282-
export_function(input_path, seg_path, output_path)
303+
export_function(input_path, seg_path, output_path, segmentation_key=segmentation_key)

synaptic_reconstruction/tools/cli.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,85 @@
22
from functools import partial
33

44
from .util import run_segmentation, get_model
5+
from ..imod.to_imod import export_helper, write_segmentation_to_imod_as_points, write_segmentation_to_imod
56
from ..inference.util import inference_helper, parse_tiling
67

78

9+
def imod_point_cli():
10+
parser = argparse.ArgumentParser(description="")
11+
parser.add_argument(
12+
"--input_path", "-i", required=True,
13+
help="The filepath to the mrc file or the directory containing the tomogram data."
14+
)
15+
parser.add_argument(
16+
"--segmentation_path", "-s", required=True,
17+
help="The filepath to the tif file or the directory containing the segmentations."
18+
)
19+
parser.add_argument(
20+
"--output_path", "-o", required=True,
21+
help="The filepath to directory where the segmentations will be saved."
22+
)
23+
parser.add_argument(
24+
"--segmentation_key", "-k", help=""
25+
)
26+
parser.add_argument(
27+
"--min_radius", type=float, default=10.0, help=""
28+
)
29+
parser.add_argument(
30+
"--radius_factor", type=float, default=1.0, help="",
31+
)
32+
parser.add_argument(
33+
"--force", action="store_true", help="",
34+
)
35+
args = parser.parse_args()
36+
37+
export_function = partial(
38+
write_segmentation_to_imod_as_points,
39+
min_radius=args.min_radius,
40+
radius_factor=args.radius_factor,
41+
)
42+
43+
export_helper(
44+
input_path=args.input_path,
45+
segmentation_path=args.segmentation_path,
46+
output_root=args.output_path,
47+
export_function=export_function,
48+
force=args.force,
49+
segmentation_key=args.segmentation_key,
50+
)
51+
52+
53+
def imod_object_cli():
54+
parser = argparse.ArgumentParser(description="")
55+
parser.add_argument(
56+
"--input_path", "-i", required=True,
57+
help="The filepath to the mrc file or the directory containing the tomogram data."
58+
)
59+
parser.add_argument(
60+
"--segmentation_path", "-s", required=True,
61+
help="The filepath to the tif file or the directory containing the segmentations."
62+
)
63+
parser.add_argument(
64+
"--output_path", "-o", required=True,
65+
help="The filepath to directory where the segmentations will be saved."
66+
)
67+
parser.add_argument(
68+
"--segmentation_key", "-k", help=""
69+
)
70+
parser.add_argument(
71+
"--force", action="store_true", help="",
72+
)
73+
args = parser.parse_args()
74+
export_helper(
75+
input_path=args.input_path,
76+
segmentation_path=args.segmentation_path,
77+
output_root=args.output_path,
78+
export_function=write_segmentation_to_imod,
79+
force=args.force,
80+
segmentation_key=args.segmentation_key,
81+
)
82+
83+
884
# TODO: handle kwargs
985
# TODO: add custom model path
1086
def segmentation_cli():

0 commit comments

Comments
 (0)