forked from oist/optinist
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #283 from arayabrain/feature/microscope/main
Merge microscope format modules
- Loading branch information
Showing
22 changed files
with
2,614 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,3 +3,6 @@ afterAll | |
xdescribe | ||
optinist | ||
Optinist | ||
nnumber | ||
nNumber | ||
NNUMBER |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,5 +28,6 @@ repos: | |
rev: v2.2.4 | ||
hooks: | ||
- id: codespell | ||
args: [-I, .codespellignore] | ||
additional_dependencies: | ||
- tomli |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
dll/ | ||
testdata/ | ||
|
||
/*.out.json | ||
/*.out.tiff | ||
/*.out.*.json | ||
/*.out.*.tiff |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import sys | ||
|
||
try: | ||
import isx | ||
except ModuleNotFoundError: | ||
pass | ||
|
||
from MicroscopeDataReaderBase import MicroscopeDataReaderBase, OMEDataModel | ||
|
||
|
||
class IsxdReader(MicroscopeDataReaderBase): | ||
"""Inscopix isxd data reader""" | ||
|
||
SDK_MODULE_NAME = "isx" | ||
|
||
@staticmethod | ||
def get_library_path() -> str: | ||
"""Returns the path of the library (dll) file""" | ||
# Note: In inscopix SDK, library (ddl) files are not used directly. | ||
return None # do nothing. | ||
|
||
@staticmethod | ||
def is_available() -> bool: | ||
"""Determine if library is available""" | ||
return __class__.SDK_MODULE_NAME in sys.modules | ||
|
||
def _init_library(self): | ||
# Note: in inscopix sdk, there is no library (ddl) | ||
# initialization process. (using pip module) | ||
pass # do nothing. | ||
|
||
def _load_file(self, data_file_path: str) -> object: | ||
handle = isx.Movie.read(data_file_path) | ||
return (handle,) | ||
|
||
def _build_original_metadata(self, data_name: str) -> dict: | ||
movie: isx.Movie = None | ||
(movie,) = self.resource_handles | ||
|
||
spacing: isx.Spacing = movie.spacing | ||
timing: isx.Timing = movie.timing | ||
|
||
original_metadata = { | ||
"data_name": data_name, | ||
"spacing": { | ||
"width": spacing.num_pixels[0], | ||
"height": spacing.num_pixels[1], | ||
}, | ||
"timing": { | ||
"start": timing.start.to_datetime().strftime("%Y-%m-%d %H:%M:%S"), | ||
"period_msec": timing.period.to_msecs(), | ||
"num_samples": timing.num_samples, | ||
"dropped": timing.dropped, | ||
"cropped": timing.cropped, | ||
"blank": timing.blank, | ||
}, | ||
} | ||
|
||
return original_metadata | ||
|
||
def _build_ome_metadata(self, original_metadata: dict) -> OMEDataModel: | ||
""" | ||
@link OME/NativeND2Reader | ||
""" | ||
|
||
spacing = original_metadata["spacing"] | ||
timing = original_metadata["timing"] | ||
|
||
omeData = OMEDataModel( | ||
image_name=original_metadata["data_name"], | ||
size_x=spacing["width"], | ||
size_y=spacing["height"], | ||
size_t=timing["num_samples"], | ||
size_z=0, # Note: currently unsettled | ||
size_c=0, # Note: currently unsettled | ||
acquisition_date=timing["start"], | ||
objective_model=None, # Note: currently unsettled | ||
fps=(1000 / timing["period_msec"]), | ||
) | ||
|
||
return omeData | ||
|
||
def _build_lab_specific_metadata(self, original_metadata: dict) -> dict: | ||
# Note: Not currently supported | ||
return None | ||
|
||
def _release_resources(self) -> None: | ||
# Note: in inscopix sdk, there is no library (ddl) release process. | ||
pass # do nothing. | ||
|
||
def _get_image_stacks(self) -> list: | ||
movie: isx.Movie = None | ||
(movie,) = self.resource_handles | ||
|
||
image_frames = [ | ||
movie.get_frame_data(i) for i in range(movie.timing.num_samples) | ||
] | ||
|
||
return image_frames |
137 changes: 137 additions & 0 deletions
137
studio/app/optinist/microscopes/MicroscopeDataReaderBase.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
import os | ||
from abc import ABCMeta, abstractmethod | ||
from dataclasses import dataclass | ||
|
||
|
||
@dataclass | ||
class OMEDataModel: | ||
"""OME(Open Microscopy Environment) aware metadata | ||
@link https://ome-model.readthedocs.io/en/stable/ome-xml/ | ||
""" | ||
|
||
image_name: str | ||
size_x: int # width | ||
size_y: int # height | ||
size_t: int # time frames | ||
size_z: int # axis z frames | ||
size_c: int # channels | ||
acquisition_date: str | ||
objective_model: str # objective lens model | ||
fps: int # frames_per_second # Note: extended from OME format | ||
|
||
|
||
class MicroscopeDataReaderBase(metaclass=ABCMeta): | ||
"""Microscope data reader base class""" | ||
|
||
LIBRARY_DIR_KEY = "MICROSCOPES_LIBRARY_DIR" | ||
|
||
def __init__(self): | ||
""" | ||
Initialization | ||
""" | ||
self._init_library() | ||
|
||
# init members | ||
self.__data_file_path = None | ||
self.__resource_handles = None | ||
self.__original_metadata = None | ||
self.__ome_metadata = None | ||
self.__lab_specific_metadata = None | ||
|
||
def __del__(self): | ||
""" | ||
Destructor | ||
""" | ||
if self.__resource_handles is not None: | ||
self._release_resources() | ||
self.__resource_handles = None | ||
|
||
def load(self, data_file_path: str): | ||
""" | ||
Release resources | ||
""" | ||
if self.__resource_handles is not None: | ||
raise Exception("Reader module already initialized.") | ||
|
||
""" | ||
Reset data | ||
""" | ||
self.__original_metadata = None | ||
self.__ome_metadata = None | ||
self.__lab_specific_metadata = None | ||
|
||
""" | ||
Load data file | ||
""" | ||
handles = self._load_file(data_file_path) | ||
self.__resource_handles = handles | ||
self.__data_file_path = data_file_path | ||
data_name = os.path.basename(data_file_path) | ||
|
||
""" | ||
Read metadata | ||
""" | ||
self.__original_metadata = self._build_original_metadata(data_name) | ||
self.__ome_metadata = self._build_ome_metadata(self.__original_metadata) | ||
self.__lab_specific_metadata = self._build_lab_specific_metadata( | ||
self.__original_metadata | ||
) | ||
|
||
def get_image_stacks(self) -> list: | ||
"""Return microscope image stacks""" | ||
return self._get_image_stacks() | ||
|
||
@abstractmethod | ||
def _init_library(self) -> dict: | ||
"""Initialize microscope library""" | ||
pass | ||
|
||
@abstractmethod | ||
def _load_file(self, data_file_path: str) -> object: | ||
"""Return metadata specific to microscope instruments""" | ||
pass | ||
|
||
@abstractmethod | ||
def _build_original_metadata(self, data_name: str) -> dict: | ||
"""Build metadata specific to microscope instruments""" | ||
pass | ||
|
||
@abstractmethod | ||
def _build_ome_metadata(self, original_metadata: dict) -> OMEDataModel: | ||
"""Build OME(Open Microscopy Environment) aware metadata""" | ||
pass | ||
|
||
@abstractmethod | ||
def _build_lab_specific_metadata(self, original_metadata: dict) -> dict: | ||
"""Build metadata in lab-specific format""" | ||
pass | ||
|
||
@abstractmethod | ||
def _release_resources() -> None: | ||
"""Release microscope library resources""" | ||
pass | ||
|
||
@abstractmethod | ||
def _get_image_stacks(self) -> list: | ||
"""Return microscope image stacks""" | ||
pass | ||
|
||
@property | ||
def data_file_path(self) -> str: | ||
return self.__data_file_path | ||
|
||
@property | ||
def resource_handles(self) -> list: | ||
return self.__resource_handles | ||
|
||
@property | ||
def original_metadata(self) -> dict: | ||
return self.__original_metadata | ||
|
||
@property | ||
def ome_metadata(self) -> dict: | ||
return self.__ome_metadata | ||
|
||
@property | ||
def lab_specific_metadata(self) -> dict: | ||
return self.__lab_specific_metadata |
Oops, something went wrong.