Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions cuvis/Async.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@

import asyncio as a

from typing import Tuple, Optional, Union
from typing import Optional, Union
from datetime import timedelta


def _to_ms(value: Union[int, timedelta]) -> int:
if isinstance(value, timedelta):
return int(value / timedelta(milliseconds=1))
Expand All @@ -23,14 +24,15 @@ def __init__(self, handle):

pass

def get(self, timeout_ms: Union[int, timedelta] ) -> Tuple[Optional[Measurement], AsyncResult]:
def get(self, timeout_ms: Union[int, timedelta]) -> tuple[Optional[Measurement], AsyncResult]:
"""

"""
_ptr = cuvis_il.new_p_int()
_pmesu = cuvis_il.new_p_int()
cuvis_il.p_int_assign(_ptr, self._handle)
res = cuvis_il.cuvis_async_capture_get(_ptr, _to_ms(timeout_ms), _pmesu)
res = cuvis_il.cuvis_async_capture_get(
_ptr, _to_ms(timeout_ms), _pmesu)

if res == cuvis_il.status_ok:
return Measurement(cuvis_il.p_int_value(_pmesu)), AsyncResult.done
Expand All @@ -42,22 +44,22 @@ def get(self, timeout_ms: Union[int, timedelta] ) -> Tuple[Optional[Measurement]
return None, AsyncResult.timeout
else:
raise SDKException()

# Python Magic Methods

def __await__(self) -> Optional[Measurement]:
async def _wait_for_return():
_status_ptr = cuvis_il.new_p_cuvis_status_t()
while True:
if cuvis_il.status_ok != cuvis_il.cuvis_async_capture_status(self._handle,_status_ptr):
if cuvis_il.status_ok != cuvis_il.cuvis_async_capture_status(self._handle, _status_ptr):
raise SDKException()
status = cuvis_il.p_cuvis_status_t_value(_status_ptr)
if status == cuvis_il.status_ok:
return self.get(0)[0]
else:
await a.sleep(10.0 / 1000)
return _wait_for_return().__await__()

def __del__(self):
_ptr = cuvis_il.new_p_int()
cuvis_il.p_int_assign(_ptr, self._handle)
Expand Down Expand Up @@ -89,22 +91,21 @@ def get(self, timeout_ms: Union[int, timedelta]) -> AsyncResult:
raise SDKException()
pass


# Python Magic Methods

def __await__(self) -> AsyncResult:
async def _wait_for_return():
_status_ptr = cuvis_il.new_p_cuvis_status_t()
while True:
if cuvis_il.status_ok != cuvis_il.cuvis_async_call_status(self._handle,_status_ptr):
if cuvis_il.status_ok != cuvis_il.cuvis_async_call_status(self._handle, _status_ptr):
raise SDKException()
status = cuvis_il.p_cuvis_status_t_value(_status_ptr)
if status == cuvis_il.status_ok:
return self.get(0)
else:
await a.sleep(10.0 / 1000)
return _wait_for_return().__await__()

def __del__(self):
_ptr = cuvis_il.new_p_int()
cuvis_il.p_int_assign(_ptr, self._handle)
Expand Down
87 changes: 41 additions & 46 deletions cuvis/Measurement.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from typing import Union, List
from typing import Union
from .FileWriteSettings import SaveArgs
import datetime
import os
import numpy as np

from ._cuvis_il import cuvis_il
from .cuvis_aux import SDKException, SessionData, Capabilities, MeasurementFlags, SensorInfo, GPSData
from .cuvis_types import DataFormat, ProcessingMode, ReferenceType
from .cube_utils import ImageData


import cuvis.cuvis_types as internal
base_datetime = datetime.datetime(1970, 1, 1)
Expand All @@ -30,6 +33,7 @@ class Measurement(object):

def __init__(self, base: Union[int, str]):
self._handle = None
self._session = None

if isinstance(base, int):
self._handle = base
Expand Down Expand Up @@ -129,6 +133,42 @@ def set_name(self, name: str) -> None:
self._refresh_metadata()
pass

@property
def cube(self) -> ImageData:
"""
Retrieves or processes the 'cube' data for this Measurement.

This property prioritizes convenience over strict design principles:
- Attempts to retrieve the 'cube' from `self.data`.
- Lazily initializes a `ProcessingContext` if a session is available but uninitialized.
- May trigger expensive processing and modify internal state during property access.

While functional, this approach introduces side effects and tight coupling, making it less
predictable and not the cleanest solution. Suitable for specific workflows where these
trade-offs are acceptable.

Raises
------
ValueError
If the 'cube' is not available and processing is not possible.

Returns
-------
ImageData
The 'cube' data, either retrieved from `self.data` or generated through processing.
"""
if 'cube' in self.data:
return self.data.get('cube')
if self._session is not None:
# try fallback if session is known
if self._session._pc is None:
from .ProcessingContext import ProcessingContext
self._session._pc = ProcessingContext(self._session)
self._session._pc.apply(self)
return self.data.get('cube', None)
raise ValueError(
"This Measurement does not have a cube saved. Consider reprocessing with a Processing Context.")

@property
def thumbnail(self):
thumb = [val for key, val in self.data.items() if "view" in key]
Expand Down Expand Up @@ -194,48 +234,3 @@ def __del__(self):
cuvis_il.cuvis_measurement_free(_ptr)
self._handle = cuvis_il.p_int_value(_ptr)
pass


class ImageData(object):
def __init__(self, img_buf=None, dformat=None):

if img_buf is None:

self.width = None
self.height = None
self.channels = None
self.array = None
self.wavelength = None

elif isinstance(img_buf, cuvis_il.cuvis_imbuffer_t):

if dformat is None:
raise TypeError("Missing format for reading image buffer")

if img_buf.format == 1:
self.array = cuvis_il.cuvis_read_imbuf_uint8(img_buf)
elif img_buf.format == 2:
self.array = cuvis_il.cuvis_read_imbuf_uint16(img_buf)
elif img_buf.format == 3:
self.array = cuvis_il.cuvis_read_imbuf_uint32(img_buf)
elif img_buf.format == 4:
self.array = cuvis_il.cuvis_read_imbuf_float32(img_buf)
else:
raise SDKException()

self.width = img_buf.width
self.height = img_buf.height
self.channels = img_buf.channels

if img_buf.wavelength is not None:
self.wavelength = [
cuvis_il.p_unsigned_int_getitem(
img_buf.wavelength, z) for z
in
range(self.channels)]

# print("got image of size {}.".format(self.array.shape))

else:
raise TypeError(
"Wrong data type for image buffer: {}".format(type(img_buf)))
43 changes: 23 additions & 20 deletions cuvis/SessionFile.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@

from typing import Union, Optional


class SessionFile(object):
def __init__(self, base: Union[Path,str]):
def __init__(self, base: Union[Path, str]):
self._handle = None
self._pc = None
if isinstance(Path(base), Path) and os.path.exists(base):
_ptr = cuvis_il.new_p_int()
if cuvis_il.status_ok != cuvis_il.cuvis_session_file_load(base,
Expand All @@ -23,40 +25,41 @@ def __init__(self, base: Union[Path,str]):
raise SDKException(
"Could not open SessionFile File! File not found!")

pass

def get_measurement(self, frameNo: int, itemtype: SessionItemType = SessionItemType.no_gaps) -> Optional[Measurement]:
def get_measurement(self, frameNo: int = 0, itemtype: SessionItemType = SessionItemType.no_gaps) -> Optional[Measurement]:
_ptr = cuvis_il.new_p_int()
ret = cuvis_il.cuvis_session_file_get_mesu(self._handle, frameNo, internal.__CuvisSessionItemType__[itemtype],
_ptr)
ret = cuvis_il.cuvis_session_file_get_mesu(self._handle, frameNo, internal.__CuvisSessionItemType__[itemtype],
_ptr)
if cuvis_il.status_no_measurement == ret:
return None
if cuvis_il.status_ok != ret:
raise SDKException()
return Measurement(cuvis_il.p_int_value(_ptr))

def get_reference(self, frameNo: int, reftype: ReferenceType) -> Optional[Measurement]:
mesu = Measurement(cuvis_il.p_int_value(_ptr))
mesu._session = self
return mesu

def get_reference(self, frameNo: int, reftype: ReferenceType) -> Optional[Measurement]:
_ptr = cuvis_il.new_p_int()
ret = cuvis_il.cuvis_session_file_get_reference_mesu(
self._handle, frameNo, internal.__CuvisReferenceType__[reftype],
_ptr)
self._handle, frameNo, internal.__CuvisReferenceType__[reftype],
_ptr)
if cuvis_il.status_no_measurement == ret:
return None
if cuvis_il.status_ok != ret:
raise SDKException()
return Measurement(cuvis_il.p_int_value(_ptr))

def get_thumbnail(self) -> ImageData:

@property
def thumbnail(self) -> ImageData:
thumbnail_data = cuvis_il.cuvis_view_data_t()
if cuvis_il.status_ok != cuvis_il.cuvis_session_file_get_thumbnail(self, thumbnail_data):
raise SDKException()

if thumbnail_data.data.format == CUVIS_imbuffer_format["imbuffer_format_uint8"]:
return ImageData(img_buf=thumbnail_data.data,
dformat=thumbnail_data.data.format)
dformat=thumbnail_data.data.format)
else:
raise SDKException("Unsupported viewer bit depth!")

def get_size(self, itemtype: SessionItemType = SessionItemType.no_gaps) -> int:
val = cuvis_il.new_p_int()
if cuvis_il.status_ok != cuvis_il.cuvis_session_file_get_size(
Expand All @@ -79,20 +82,20 @@ def operation_mode(self) -> OperationMode:
self._handle, val):
raise SDKException()
return internal.__OperationMode__[cuvis_il.p_cuvis_operation_mode_t_value(val)]

@property
def hash(self) -> str:
return cuvis_il.cuvis_session_file_get_hash_swig(self._handle)

# Python Magic Methods
def __iter__(self):
for i in range(len(self)):
yield self[i]
pass

def __len__(self):
return self.get_size()

def __getitem__(self, key: int) -> Measurement:
return self.get_measurement(key)

Expand Down
14 changes: 8 additions & 6 deletions cuvis/Viewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from .FileWriteSettings import ViewerSettings

from typing import Union, Dict
from typing import Union


class Viewer(object):
Expand All @@ -25,7 +25,7 @@ def __init__(self, settings: Union[int, ViewerSettings]):
type(settings)))
pass

def _create_view_data(self, new_handle: int) -> Dict[str, ImageData]:
def _create_view_data(self, new_handle: int) -> Union[dict[str, ImageData], ImageData]:

_ptr = cuvis_il.new_p_int()
if cuvis_il.status_ok != cuvis_il.cuvis_view_get_data_count(
Expand All @@ -47,11 +47,13 @@ def _create_view_data(self, new_handle: int) -> Dict[str, ImageData]:
dformat=view_data.data.format)
else:
raise SDKException("Unsupported viewer bit depth!")
# TODO when is a good point to release the view
# cuvis_il.cuvis_view_free(_ptr)
return view_array
if len(view_array.keys()) == 1:
# if only one value is available, do not wrap in dictionary
return list(view_array.values())[0]
else:
return view_array

def apply(self, mesu: Measurement) -> Dict[str, ImageData]:
def apply(self, mesu: Measurement) -> dict[str, ImageData]:
_ptr = cuvis_il.new_p_int()
if cuvis_il.status_ok != cuvis_il.cuvis_viewer_apply(self._handle,
mesu._handle, _ptr):
Expand Down
2 changes: 1 addition & 1 deletion cuvis/Worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from .doc import copydoc

from dataclasses import dataclass
from typing import Callable, Awaitable, Tuple
from typing import Callable, Awaitable


@dataclass
Expand Down
1 change: 1 addition & 0 deletions cuvis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from .Export import CubeExporter, EnviExporter, TiffExporter, ViewExporter
from .Calibration import Calibration
from .AcquisitionContext import AcquisitionContext
from .cube_utils import ImageData
import os
import platform
import sys
Expand Down
Loading