synapse_net.imod.to_imod

  1import multiprocessing
  2import os
  3import shutil
  4import tempfile
  5
  6from concurrent import futures
  7from glob import glob
  8from subprocess import run
  9from typing import Optional, Tuple, Union
 10
 11import h5py
 12import imageio.v3 as imageio
 13import mrcfile
 14import numpy as np
 15from scipy.ndimage import distance_transform_edt
 16from skimage.measure import regionprops
 17from tqdm import tqdm
 18
 19
 20def _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
 30# TODO: this has still some issues with some tomograms that has an offset info.
 31# For now, this occurs for the inner ear data tomograms; it works for Fidi's STEM tomograms.
 32# Ben's theory is that this might be due to data form JEOL vs. ThermoFischer microscopes.
 33# To test this I can check how it works for data from Maus et al. / Imig et al., which were taken on a JEOL.
 34# Can also check out the mrc documentation here: https://www.ccpem.ac.uk/mrc_format/mrc2014.php
 35def write_segmentation_to_imod(
 36    mrc_path: str,
 37    segmentation: Union[str, np.ndarray],
 38    output_path: str,
 39    segmentation_key: Optional[str] = None,
 40) -> None:
 41    """Write a segmentation to a mod file as closed contour object(s).
 42
 43    Args:
 44        mrc_path: The filepath to the mrc file from which the segmentation was derived.
 45        segmentation: The segmentation (either as numpy array or filepath to a .tif file).
 46        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.
 48    """
 49    cmd = "imodauto"
 50    cmd_path = shutil.which(cmd)
 51    assert cmd_path is not None, f"Could not find the {cmd} imod command."
 52
 53    # Load the segmentation case a filepath was passed.
 54    if isinstance(segmentation, str):
 55        segmentation = _load_segmentation(segmentation, segmentation_key)
 56
 57    # Binarize the segmentation and flip its axes to match the IMOD axis convention.
 58    segmentation = (segmentation > 0).astype("uint8")
 59    segmentation = np.flip(segmentation, axis=1)
 60
 61    # Read the voxel size and origin information from the mrc file.
 62    assert os.path.exists(mrc_path)
 63    with mrcfile.open(mrc_path, mode="r") as f:
 64        voxel_size = f.voxel_size
 65        nx, ny, nz = f.header.nxstart, f.header.nystart, f.header.nzstart
 66        origin = f.header.origin
 67
 68    # Write the input for imodauto to a temporary mrc file.
 69    with tempfile.NamedTemporaryFile(suffix=".mrc") as f:
 70        tmp_path = f.name
 71
 72        mrcfile.new(tmp_path, data=segmentation, overwrite=True)
 73        # Write the voxel_size and origin infomration.
 74        with mrcfile.open(tmp_path, mode="r+") as f:
 75            f.voxel_size = voxel_size
 76
 77            f.header.nxstart = nx
 78            f.header.nystart = ny
 79            f.header.nzstart = nz
 80            f.header.origin = (0.0, 0.0, 0.0) * 3 if origin is None else origin
 81
 82            f.update_header_from_data()
 83
 84        # Run the command.
 85        cmd_list = [cmd, "-E", "1", "-u", tmp_path, output_path]
 86        run(cmd_list)
 87
 88
 89def convert_segmentation_to_spheres(
 90    segmentation: np.ndarray,
 91    verbose: bool = False,
 92    num_workers: Optional[int] = None,
 93    resolution: Optional[Tuple[float, float, float]] = None,
 94    radius_factor: float = 1.0,
 95    estimate_radius_2d: bool = True,
 96    props: Optional[list] = None,
 97) -> Tuple[np.ndarray, np.ndarray]:
 98    """Extract spheres parameterized by center and radius from a segmentation.
 99
100    Args:
101        segmentation: The segmentation.
102        verbose: Whether to print a progress bar.
103        num_workers: Number of workers to use for parallelization.
104        resolution: The physical resolution of the data.
105        radius_factor: Factor for increasing the radius to account for too small exported spheres.
106        estimate_radius_2d: If true the distance to boundary for determining the centroid and computing
107            the radius will be computed only in 2d rather than in 3d. This can lead to better results
108            in case of deformation across the depth axis.
109        props: Optional list of regionprops
110
111    Returns:
112        The center coordinates.
113        The radii.
114    """
115    num_workers = multiprocessing.cpu_count() if num_workers is None else num_workers
116    if props is None:
117        props = regionprops(segmentation)
118
119    def coords_and_rads(prop):
120        seg_id = prop.label
121        bbox = prop.bbox
122        ndim = len(bbox) // 2
123
124        # Handle 2D bounding box
125        if len(bbox) == 4:
126            bb = np.s_[bbox[0]:bbox[2], bbox[1]:bbox[3]]
127            mask = segmentation[bb] == seg_id
128            if resolution:
129                dists = distance_transform_edt(mask, sampling=resolution)
130            else:
131                dists = distance_transform_edt(mask)
132
133        # Handle 3D bounding box
134        elif len(bbox) == 6:
135            bb = np.s_[bbox[0]:bbox[3], bbox[1]:bbox[4], bbox[2]:bbox[5]]
136            mask = segmentation[bb] == seg_id
137
138            if estimate_radius_2d:
139                if resolution:
140                    dists = np.array([distance_transform_edt(ma, sampling=resolution[1:]) for ma in mask])
141                else:
142                    dists = np.array([distance_transform_edt(ma) for ma in mask])
143            else:
144                dists = distance_transform_edt(mask, sampling=resolution)
145        else:
146            raise ValueError(f"Unsupported bounding box dimensions: {len(bbox)}")
147
148        max_coord = np.unravel_index(np.argmax(dists), mask.shape)
149        radius = dists[max_coord] * radius_factor
150
151        offset = np.array(bbox[:ndim])
152
153        coord = np.array(max_coord) + offset
154
155        return coord, radius
156
157    with futures.ThreadPoolExecutor(num_workers) as tp:
158        res = list(tqdm(
159            tp.map(coords_and_rads, props), disable=not verbose, total=len(props),
160            desc="Compute coordinates and radii"
161        ))
162
163    coords = [re[0] for re in res]
164    rads = [re[1] for re in res]
165    return np.array(coords), np.array(rads)
166
167
168def write_points_to_imod(
169    coordinates: np.ndarray,
170    radii: np.ndarray,
171    shape: Tuple[int, int, int],
172    min_radius: Union[float, int],
173    output_path: str,
174    color: Optional[Tuple[int, int, int]] = None,
175) -> None:
176    """Write point annotations to a .mod file for IMOD.
177
178    Args:
179        coordinates: Array with the point coordinates. #2D or 3D
180        radii: Array with the point radii.
181        shape: Shape of the volume to be exported.
182        min_radius: Minimum radius for export.
183        output_path: Where to save the .mod file.
184        color: Optional color for writing out the points.
185    """
186    cmd = "point2model"
187    cmd_path = shutil.which(cmd)
188    assert cmd_path is not None, f"Could not find the {cmd} imod command."
189
190    def _pad(inp, n=3):
191        inp = int(round(inp))
192        plen = 6 + (n - len(str(inp)))
193        pw = plen * " "
194        return f"{pw}{inp}.00"
195
196    is_3d = coordinates.shape[1] == 3
197    if not is_3d and coordinates.shape[1] != 2:
198        raise ValueError("Coordinates must have shape (N, 2) for 2D or (N, 3) for 3D.")
199
200    with tempfile.NamedTemporaryFile() as tmp_file:
201        fname = tmp_file.name
202        with open(fname, "w") as f:
203            for coord, radius in zip(coordinates, radii):
204                if radius < min_radius:
205                    continue
206
207                if is_3d:
208                    # (z, y, x) indexing
209                    x = _pad(coord[2])
210                    y = _pad(shape[1] - coord[1])
211                    z = _pad(coord[0])
212                    
213                else:
214                    # (y, x) indexing, single z-plane
215                    x = _pad(coord[1])
216                    y = _pad(shape[0] - coord[0])
217                    z=_pad(0)
218                    
219                f.write(f"{x}{y}{z}{_pad(radius, 2)}\n")
220        cmd = [cmd, "-si", "-scat", fname, output_path]
221
222        if color is not None:
223            assert len(color) == 3
224            r, g, b = [str(co) for co in color]
225            cmd += ["-co", f"{r} {g} {b}"]
226
227        run(cmd)
228
229
230
231def write_segmentation_to_imod_as_points(
232    mrc_path: str,
233    segmentation: Union[str, np.ndarray],
234    output_path: str,
235    min_radius: Union[int, float],
236    radius_factor: float = 1.0,
237    estimate_radius_2d: bool = True,
238    segmentation_key: Optional[str] = None,
239) -> None:
240    """Write segmentation results to .mod file with imod point annotations.
241
242    This approximates each segmented object as a sphere.
243
244    Args:
245        mrc_path: Filepath to the mrc volume that was segmented.
246        segmentation: The segmentation (either as numpy array or filepath to a .tif file).
247        output_path: Where to save the .mod file.
248        min_radius: Minimum radius for export.
249        radius_factor: Factor for increasing the radius to account for too small exported spheres.
250        estimate_radius_2d: If true the distance to boundary for determining the centroid and computing
251            the radius will be computed only in 2d rather than in 3d. This can lead to better results
252            in case of deformation across the depth axis.
253        segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
254    """
255
256    # Extract the center coordinates and radii from the segmentation.
257    if isinstance(segmentation, str):
258        segmentation = _load_segmentation(segmentation, segmentation_key)
259    coordinates, radii = convert_segmentation_to_spheres(
260        segmentation, radius_factor=radius_factor, estimate_radius_2d=estimate_radius_2d
261    )
262
263    # Write the point annotations to imod.
264    write_points_to_imod(coordinates, radii, segmentation.shape, min_radius, output_path)
265
266
267def _get_file_paths(input_path, ext=(".mrc", ".rec")):
268    if not os.path.exists(input_path):
269        raise Exception(f"Input path not found {input_path}.")
270
271    if isinstance(ext, str):
272        ext = (ext,)
273
274    if os.path.isfile(input_path):
275        input_files = [input_path]
276        input_root = None
277    else:
278        input_files = []
279        for ex in ext:
280            input_files.extend(
281                sorted(glob(os.path.join(input_path, "**", f"*{ex}"), recursive=True))
282            )
283        input_root = input_path
284
285    return input_files, input_root
286
287
288def export_helper(
289    input_path: str,
290    segmentation_path: str,
291    output_root: str,
292    export_function: callable,
293    force: bool = False,
294    segmentation_key: Optional[str] = None,
295) -> None:
296    """
297    Helper function to run imod export for files in a directory.
298
299    Args:
300        input_path: The path to the input data.
301            Can either be a folder. In this case all mrc files below the folder will be exported.
302            Or can be a single mrc file. In this case only this mrc file will be segmented.
303        segmentation_path: Filepath to the segmentation results.
304            The filestructure must exactly match `input_path`.
305        output_root: The path to the output directory where the export results will be saved.
306        export_function: The function performing the export to the desired imod format.
307            This function must take the path to the input in a .mrc file,
308            the path to the segmentation in a .tif file and the output path as only arguments.
309            If you want to pass additional arguments to this function the use 'funtools.partial'
310        force: Whether to rerun segmentation for output files that are already present.
311        segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
312    """
313    input_files, input_root = _get_file_paths(input_path)
314    segmentation_files, _ = _get_file_paths(segmentation_path, ext=".tif" if segmentation_key is None else ".h5")
315    assert len(input_files) == len(segmentation_files)
316
317    for input_path, seg_path in tqdm(zip(input_files, segmentation_files), total=len(input_files)):
318        # Determine the output file name.
319        input_folder, input_name = os.path.split(input_path)
320        fname = os.path.splitext(input_name)[0] + ".mod"
321        if input_root is None:
322            output_path = os.path.join(output_root, fname)
323        else:  # If we have nested input folders then we preserve the folder structure in the output.
324            rel_folder = os.path.relpath(input_folder, input_root)
325            output_path = os.path.join(output_root, rel_folder, fname)
326
327        # Check if the output path is already present.
328        # If it is we skip the prediction, unless force was set to true.
329        if os.path.exists(output_path) and not force:
330            continue
331
332        os.makedirs(os.path.split(output_path)[0], exist_ok=True)
333        export_function(input_path, seg_path, output_path, segmentation_key=segmentation_key)
def write_segmentation_to_imod( mrc_path: str, segmentation: Union[str, numpy.ndarray], output_path: str, segmentation_key: Optional[str] = None) -> None:
36def write_segmentation_to_imod(
37    mrc_path: str,
38    segmentation: Union[str, np.ndarray],
39    output_path: str,
40    segmentation_key: Optional[str] = None,
41) -> None:
42    """Write a segmentation to a mod file as closed contour object(s).
43
44    Args:
45        mrc_path: The filepath to the mrc file from which the segmentation was derived.
46        segmentation: The segmentation (either as numpy array or filepath to a .tif file).
47        output_path: The output path where the mod file will be saved.
48        segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
49    """
50    cmd = "imodauto"
51    cmd_path = shutil.which(cmd)
52    assert cmd_path is not None, f"Could not find the {cmd} imod command."
53
54    # Load the segmentation case a filepath was passed.
55    if isinstance(segmentation, str):
56        segmentation = _load_segmentation(segmentation, segmentation_key)
57
58    # Binarize the segmentation and flip its axes to match the IMOD axis convention.
59    segmentation = (segmentation > 0).astype("uint8")
60    segmentation = np.flip(segmentation, axis=1)
61
62    # Read the voxel size and origin information from the mrc file.
63    assert os.path.exists(mrc_path)
64    with mrcfile.open(mrc_path, mode="r") as f:
65        voxel_size = f.voxel_size
66        nx, ny, nz = f.header.nxstart, f.header.nystart, f.header.nzstart
67        origin = f.header.origin
68
69    # Write the input for imodauto to a temporary mrc file.
70    with tempfile.NamedTemporaryFile(suffix=".mrc") as f:
71        tmp_path = f.name
72
73        mrcfile.new(tmp_path, data=segmentation, overwrite=True)
74        # Write the voxel_size and origin infomration.
75        with mrcfile.open(tmp_path, mode="r+") as f:
76            f.voxel_size = voxel_size
77
78            f.header.nxstart = nx
79            f.header.nystart = ny
80            f.header.nzstart = nz
81            f.header.origin = (0.0, 0.0, 0.0) * 3 if origin is None else origin
82
83            f.update_header_from_data()
84
85        # Run the command.
86        cmd_list = [cmd, "-E", "1", "-u", tmp_path, output_path]
87        run(cmd_list)

Write a segmentation to a mod file as closed contour object(s).

Arguments:
  • mrc_path: The filepath to the mrc file from which the segmentation was derived.
  • segmentation: The segmentation (either as numpy array or filepath to a .tif file).
  • output_path: The output path where the mod file will be saved.
  • segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
def convert_segmentation_to_spheres( segmentation: numpy.ndarray, verbose: bool = False, num_workers: Optional[int] = None, resolution: Optional[Tuple[float, float, float]] = None, radius_factor: float = 1.0, estimate_radius_2d: bool = True, props: Optional[list] = None) -> Tuple[numpy.ndarray, numpy.ndarray]:
 90def convert_segmentation_to_spheres(
 91    segmentation: np.ndarray,
 92    verbose: bool = False,
 93    num_workers: Optional[int] = None,
 94    resolution: Optional[Tuple[float, float, float]] = None,
 95    radius_factor: float = 1.0,
 96    estimate_radius_2d: bool = True,
 97    props: Optional[list] = None,
 98) -> Tuple[np.ndarray, np.ndarray]:
 99    """Extract spheres parameterized by center and radius from a segmentation.
100
101    Args:
102        segmentation: The segmentation.
103        verbose: Whether to print a progress bar.
104        num_workers: Number of workers to use for parallelization.
105        resolution: The physical resolution of the data.
106        radius_factor: Factor for increasing the radius to account for too small exported spheres.
107        estimate_radius_2d: If true the distance to boundary for determining the centroid and computing
108            the radius will be computed only in 2d rather than in 3d. This can lead to better results
109            in case of deformation across the depth axis.
110        props: Optional list of regionprops
111
112    Returns:
113        The center coordinates.
114        The radii.
115    """
116    num_workers = multiprocessing.cpu_count() if num_workers is None else num_workers
117    if props is None:
118        props = regionprops(segmentation)
119
120    def coords_and_rads(prop):
121        seg_id = prop.label
122        bbox = prop.bbox
123        ndim = len(bbox) // 2
124
125        # Handle 2D bounding box
126        if len(bbox) == 4:
127            bb = np.s_[bbox[0]:bbox[2], bbox[1]:bbox[3]]
128            mask = segmentation[bb] == seg_id
129            if resolution:
130                dists = distance_transform_edt(mask, sampling=resolution)
131            else:
132                dists = distance_transform_edt(mask)
133
134        # Handle 3D bounding box
135        elif len(bbox) == 6:
136            bb = np.s_[bbox[0]:bbox[3], bbox[1]:bbox[4], bbox[2]:bbox[5]]
137            mask = segmentation[bb] == seg_id
138
139            if estimate_radius_2d:
140                if resolution:
141                    dists = np.array([distance_transform_edt(ma, sampling=resolution[1:]) for ma in mask])
142                else:
143                    dists = np.array([distance_transform_edt(ma) for ma in mask])
144            else:
145                dists = distance_transform_edt(mask, sampling=resolution)
146        else:
147            raise ValueError(f"Unsupported bounding box dimensions: {len(bbox)}")
148
149        max_coord = np.unravel_index(np.argmax(dists), mask.shape)
150        radius = dists[max_coord] * radius_factor
151
152        offset = np.array(bbox[:ndim])
153
154        coord = np.array(max_coord) + offset
155
156        return coord, radius
157
158    with futures.ThreadPoolExecutor(num_workers) as tp:
159        res = list(tqdm(
160            tp.map(coords_and_rads, props), disable=not verbose, total=len(props),
161            desc="Compute coordinates and radii"
162        ))
163
164    coords = [re[0] for re in res]
165    rads = [re[1] for re in res]
166    return np.array(coords), np.array(rads)

Extract spheres parameterized by center and radius from a segmentation.

Arguments:
  • segmentation: The segmentation.
  • verbose: Whether to print a progress bar.
  • num_workers: Number of workers to use for parallelization.
  • resolution: The physical resolution of the data.
  • radius_factor: Factor for increasing the radius to account for too small exported spheres.
  • estimate_radius_2d: If true the distance to boundary for determining the centroid and computing the radius will be computed only in 2d rather than in 3d. This can lead to better results in case of deformation across the depth axis.
  • props: Optional list of regionprops
Returns:

The center coordinates. The radii.

def write_points_to_imod( coordinates: numpy.ndarray, radii: numpy.ndarray, shape: Tuple[int, int, int], min_radius: Union[float, int], output_path: str, color: Optional[Tuple[int, int, int]] = None) -> None:
169def write_points_to_imod(
170    coordinates: np.ndarray,
171    radii: np.ndarray,
172    shape: Tuple[int, int, int],
173    min_radius: Union[float, int],
174    output_path: str,
175    color: Optional[Tuple[int, int, int]] = None,
176) -> None:
177    """Write point annotations to a .mod file for IMOD.
178
179    Args:
180        coordinates: Array with the point coordinates. #2D or 3D
181        radii: Array with the point radii.
182        shape: Shape of the volume to be exported.
183        min_radius: Minimum radius for export.
184        output_path: Where to save the .mod file.
185        color: Optional color for writing out the points.
186    """
187    cmd = "point2model"
188    cmd_path = shutil.which(cmd)
189    assert cmd_path is not None, f"Could not find the {cmd} imod command."
190
191    def _pad(inp, n=3):
192        inp = int(round(inp))
193        plen = 6 + (n - len(str(inp)))
194        pw = plen * " "
195        return f"{pw}{inp}.00"
196
197    is_3d = coordinates.shape[1] == 3
198    if not is_3d and coordinates.shape[1] != 2:
199        raise ValueError("Coordinates must have shape (N, 2) for 2D or (N, 3) for 3D.")
200
201    with tempfile.NamedTemporaryFile() as tmp_file:
202        fname = tmp_file.name
203        with open(fname, "w") as f:
204            for coord, radius in zip(coordinates, radii):
205                if radius < min_radius:
206                    continue
207
208                if is_3d:
209                    # (z, y, x) indexing
210                    x = _pad(coord[2])
211                    y = _pad(shape[1] - coord[1])
212                    z = _pad(coord[0])
213                    
214                else:
215                    # (y, x) indexing, single z-plane
216                    x = _pad(coord[1])
217                    y = _pad(shape[0] - coord[0])
218                    z=_pad(0)
219                    
220                f.write(f"{x}{y}{z}{_pad(radius, 2)}\n")
221        cmd = [cmd, "-si", "-scat", fname, output_path]
222
223        if color is not None:
224            assert len(color) == 3
225            r, g, b = [str(co) for co in color]
226            cmd += ["-co", f"{r} {g} {b}"]
227
228        run(cmd)

Write point annotations to a .mod file for IMOD.

Arguments:
  • coordinates: Array with the point coordinates. #2D or 3D
  • radii: Array with the point radii.
  • shape: Shape of the volume to be exported.
  • min_radius: Minimum radius for export.
  • output_path: Where to save the .mod file.
  • color: Optional color for writing out the points.
def write_segmentation_to_imod_as_points( mrc_path: str, segmentation: Union[str, numpy.ndarray], output_path: str, min_radius: Union[int, float], radius_factor: float = 1.0, estimate_radius_2d: bool = True, segmentation_key: Optional[str] = None) -> None:
232def write_segmentation_to_imod_as_points(
233    mrc_path: str,
234    segmentation: Union[str, np.ndarray],
235    output_path: str,
236    min_radius: Union[int, float],
237    radius_factor: float = 1.0,
238    estimate_radius_2d: bool = True,
239    segmentation_key: Optional[str] = None,
240) -> None:
241    """Write segmentation results to .mod file with imod point annotations.
242
243    This approximates each segmented object as a sphere.
244
245    Args:
246        mrc_path: Filepath to the mrc volume that was segmented.
247        segmentation: The segmentation (either as numpy array or filepath to a .tif file).
248        output_path: Where to save the .mod file.
249        min_radius: Minimum radius for export.
250        radius_factor: Factor for increasing the radius to account for too small exported spheres.
251        estimate_radius_2d: If true the distance to boundary for determining the centroid and computing
252            the radius will be computed only in 2d rather than in 3d. This can lead to better results
253            in case of deformation across the depth axis.
254        segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
255    """
256
257    # Extract the center coordinates and radii from the segmentation.
258    if isinstance(segmentation, str):
259        segmentation = _load_segmentation(segmentation, segmentation_key)
260    coordinates, radii = convert_segmentation_to_spheres(
261        segmentation, radius_factor=radius_factor, estimate_radius_2d=estimate_radius_2d
262    )
263
264    # Write the point annotations to imod.
265    write_points_to_imod(coordinates, radii, segmentation.shape, min_radius, output_path)

Write segmentation results to .mod file with imod point annotations.

This approximates each segmented object as a sphere.

Arguments:
  • mrc_path: Filepath to the mrc volume that was segmented.
  • segmentation: The segmentation (either as numpy array or filepath to a .tif file).
  • output_path: Where to save the .mod file.
  • min_radius: Minimum radius for export.
  • radius_factor: Factor for increasing the radius to account for too small exported spheres.
  • estimate_radius_2d: If true the distance to boundary for determining the centroid and computing the radius will be computed only in 2d rather than in 3d. This can lead to better results in case of deformation across the depth axis.
  • segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
def export_helper( input_path: str, segmentation_path: str, output_root: str, export_function: <built-in function callable>, force: bool = False, segmentation_key: Optional[str] = None) -> None:
289def export_helper(
290    input_path: str,
291    segmentation_path: str,
292    output_root: str,
293    export_function: callable,
294    force: bool = False,
295    segmentation_key: Optional[str] = None,
296) -> None:
297    """
298    Helper function to run imod export for files in a directory.
299
300    Args:
301        input_path: The path to the input data.
302            Can either be a folder. In this case all mrc files below the folder will be exported.
303            Or can be a single mrc file. In this case only this mrc file will be segmented.
304        segmentation_path: Filepath to the segmentation results.
305            The filestructure must exactly match `input_path`.
306        output_root: The path to the output directory where the export results will be saved.
307        export_function: The function performing the export to the desired imod format.
308            This function must take the path to the input in a .mrc file,
309            the path to the segmentation in a .tif file and the output path as only arguments.
310            If you want to pass additional arguments to this function the use 'funtools.partial'
311        force: Whether to rerun segmentation for output files that are already present.
312        segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.
313    """
314    input_files, input_root = _get_file_paths(input_path)
315    segmentation_files, _ = _get_file_paths(segmentation_path, ext=".tif" if segmentation_key is None else ".h5")
316    assert len(input_files) == len(segmentation_files)
317
318    for input_path, seg_path in tqdm(zip(input_files, segmentation_files), total=len(input_files)):
319        # Determine the output file name.
320        input_folder, input_name = os.path.split(input_path)
321        fname = os.path.splitext(input_name)[0] + ".mod"
322        if input_root is None:
323            output_path = os.path.join(output_root, fname)
324        else:  # If we have nested input folders then we preserve the folder structure in the output.
325            rel_folder = os.path.relpath(input_folder, input_root)
326            output_path = os.path.join(output_root, rel_folder, fname)
327
328        # Check if the output path is already present.
329        # If it is we skip the prediction, unless force was set to true.
330        if os.path.exists(output_path) and not force:
331            continue
332
333        os.makedirs(os.path.split(output_path)[0], exist_ok=True)
334        export_function(input_path, seg_path, output_path, segmentation_key=segmentation_key)

Helper function to run imod export for files in a directory.

Arguments:
  • input_path: The path to the input data. Can either be a folder. In this case all mrc files below the folder will be exported. Or can be a single mrc file. In this case only this mrc file will be segmented.
  • segmentation_path: Filepath to the segmentation results. The filestructure must exactly match input_path.
  • output_root: The path to the output directory where the export results will be saved.
  • export_function: The function performing the export to the desired imod format. This function must take the path to the input in a .mrc file, the path to the segmentation in a .tif file and the output path as only arguments. If you want to pass additional arguments to this function the use 'funtools.partial'
  • force: Whether to rerun segmentation for output files that are already present.
  • segmentation_key: The key to the segmentation data in case the segmentation is stored in hdf5 files.