From 9bd60d4552d6fdae165e65c25994798f6840bfb7 Mon Sep 17 00:00:00 2001 From: Kaidong Chai Date: Wed, 13 Nov 2024 11:28:49 -0500 Subject: [PATCH] Move PSOCT conversion script into modalities --- linc_convert/modalities/psoct/__init__.py | 5 ++ linc_convert/modalities/psoct/cli.py | 9 +++ .../modalities/psoct/placeholder.py | 32 +++----- .../utils.py => linc_convert/utils/unit.py | 76 ------------------- 4 files changed, 25 insertions(+), 97 deletions(-) create mode 100644 linc_convert/modalities/psoct/__init__.py create mode 100644 linc_convert/modalities/psoct/cli.py rename scripts/oct_mat_to_zarr.py => linc_convert/modalities/psoct/placeholder.py (96%) rename scripts/utils.py => linc_convert/utils/unit.py (71%) diff --git a/linc_convert/modalities/psoct/__init__.py b/linc_convert/modalities/psoct/__init__.py new file mode 100644 index 00000000..a6d9d6f2 --- /dev/null +++ b/linc_convert/modalities/psoct/__init__.py @@ -0,0 +1,5 @@ +"""Dark Field microscopy converters.""" + +__all__ = ["cli", "multi_slice", "single_slice"] + +from . import cli, multi_slice, single_slice diff --git a/linc_convert/modalities/psoct/cli.py b/linc_convert/modalities/psoct/cli.py new file mode 100644 index 00000000..12d4c065 --- /dev/null +++ b/linc_convert/modalities/psoct/cli.py @@ -0,0 +1,9 @@ +"""Entry-points for Dark Field microscopy converter.""" + +from cyclopts import App + +from linc_convert.cli import main + +help = "Converters for PS-OCT .mat files" +psoct = App(name="psoct", help=help) +main.command(psoct) diff --git a/scripts/oct_mat_to_zarr.py b/linc_convert/modalities/psoct/placeholder.py similarity index 96% rename from scripts/oct_mat_to_zarr.py rename to linc_convert/modalities/psoct/placeholder.py index a2ff53e5..964b4e9f 100644 --- a/scripts/oct_mat_to_zarr.py +++ b/linc_convert/modalities/psoct/placeholder.py @@ -1,17 +1,8 @@ """ -(OCT) Matlab to OME-ZARR -======================== - -This script converts Matlab files generated by the MGH in-house OCT pipeline -into a pyramidal OME-ZARR hierarchy. - -dependencies: - numpy - scipy - zarr - nibabel - cyclopts +Converts Matlab files generated by the MGH in-house OCT pipeline +into a OME-ZARR pyramid. """ + import ast import json import math @@ -30,12 +21,14 @@ import zarr from scipy.io import loadmat -from utils import ( - ceildiv, make_compressor, convert_unit, to_ome_unit, to_nifti_unit, - orientation_to_affine, center_affine -) +from linc_convert.modalities.psoct.cli import psoct +from linc_convert.utils.orientation import orientation_to_affine, center_affine +from linc_convert.utils.math import ceildiv +from linc_convert.utils.zarr import make_compressor +from linc_convert.utils.unit import (convert_unit, to_ome_unit, to_nifti_unit) -app = cyclopts.App(help_format="markdown") +placeholder = cyclopts.App(name="placeholder", help_format="markdown") +psoct.command(placeholder) def automap(func): @@ -53,7 +46,7 @@ def wrapper(inp, out=None, **kwargs): return wrapper -@app.default +@placeholder.default @automap def convert( inp: List[str], @@ -435,6 +428,3 @@ def parse_value_unit(string, n=None): return meta - -if __name__ == "__main__": - app() diff --git a/scripts/utils.py b/linc_convert/utils/unit.py similarity index 71% rename from scripts/utils.py rename to linc_convert/utils/unit.py index 88d6ff55..fc276957 100644 --- a/scripts/utils.py +++ b/linc_convert/utils/unit.py @@ -1,78 +1,3 @@ -import math -import numcodecs -import numpy as np - - -def orientation_ensure_3d(orientation): - """ - Parameters - ---------- - orientation : str - Either one of {'coronal', 'axial', 'sagittal'}, or a two- or - three-letter permutation of {('R', 'L'), ('A', 'P'), ('S', 'I')} - - Returns - ------- - orientation : str - A three-letter permutation of {('R', 'L'), ('A', 'P'), ('S', 'I')} - """ - orientation = { - 'coronal': 'LI', - 'axial': 'LP', - 'sagittal': 'PI', - }.get(orientation.lower(), orientation).upper() - if len(orientation) == 2: - if 'L' not in orientation and 'R' not in orientation: - orientation += 'R' - if 'P' not in orientation and 'A' not in orientation: - orientation += 'A' - if 'I' not in orientation and 'S' not in orientation: - orientation += 'S' - return orientation - - -def orientation_to_affine(orientation, vxw=1, vxh=1, vxd=1): - orientation = orientation_ensure_3d(orientation) - affine = np.zeros([4, 4]) - vx = np.asarray([vxw, vxh, vxd]) - for i in range(3): - letter = orientation[i] - sign = -1 if letter in 'LPI' else 1 - letter = {'L': 'R', 'P': 'A', 'I': 'S'}.get(letter, letter) - index = list('RAS').index(letter) - affine[index, i] = sign * vx[i] - return affine - - -def center_affine(affine, shape): - if len(shape) == 2: - shape = [*shape, 1] - shape = np.asarray(shape) - affine[:3, -1] = -0.5 * affine[:3, :3] @ (shape - 1) - return affine - - -def ceildiv(x, y): - return int(math.ceil(x / y)) - - -def floordiv(x, y): - return int(math.floor(x / y)) - - -def make_compressor(name, **prm): - if not isinstance(name, str): - return name - name = name.lower() - if name == 'blosc': - Compressor = numcodecs.Blosc - elif name == 'zlib': - Compressor = numcodecs.Zlib - else: - raise ValueError('Unknown compressor', name) - return Compressor(**prm) - - ome_valid_units = { 'space': [ 'angstrom', @@ -178,7 +103,6 @@ def make_compressor(name, **prm): for short, long in si_prefix_short2long.items() } - si_prefix_exponent = { 'Q': 30, 'R': 27,