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