Skip to content

Commit

Permalink
Merge pull request #283 from arayabrain/feature/microscope/main
Browse files Browse the repository at this point in the history
Merge microscope format modules
  • Loading branch information
itutu-tienday authored Feb 2, 2024
2 parents 096e53e + 8a61d52 commit a781d24
Show file tree
Hide file tree
Showing 22 changed files with 2,614 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .codespellignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ afterAll
xdescribe
optinist
Optinist
nnumber
nNumber
NNUMBER
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ repos:
rev: v2.2.4
hooks:
- id: codespell
args: [-I, .codespellignore]
additional_dependencies:
- tomli
7 changes: 7 additions & 0 deletions studio/app/optinist/microscopes/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
dll/
testdata/

/*.out.json
/*.out.tiff
/*.out.*.json
/*.out.*.tiff
99 changes: 99 additions & 0 deletions studio/app/optinist/microscopes/IsxdReader.py
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 studio/app/optinist/microscopes/MicroscopeDataReaderBase.py
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
Loading

0 comments on commit a781d24

Please sign in to comment.