fix: contrast formulation cause leak of high value fix: empty series_id cause fail to read chore: restructure folder chore!: change layer vars BREAKING CHANGE: not support 4.1 any more
132 lines
3.5 KiB
Python
132 lines
3.5 KiB
Python
import copy
|
|
import numpy as np
|
|
|
|
from . import scipy
|
|
from . import skimage as ski
|
|
|
|
# 3rd-party
|
|
import transforms3d
|
|
|
|
# TODO: turn to dataclasses
|
|
|
|
|
|
class Layer():
|
|
def __init__(self,
|
|
data: np.ndarray,
|
|
name: str,
|
|
kind="scalar",
|
|
affine=np.identity(4)) -> None:
|
|
if data.ndim != 5:
|
|
raise Exception("Data shape order should be TXYZC")
|
|
|
|
affine = np.array(affine)
|
|
if affine.shape != (4, 4):
|
|
raise Exception("affine shape should be (4,4)")
|
|
|
|
self.data = data
|
|
self.name = name
|
|
self.kind = kind
|
|
self.affine = affine
|
|
|
|
@property
|
|
def bioxel_size(self):
|
|
t, r, z, s = transforms3d.affines.decompose44(self.affine)
|
|
return z.tolist()
|
|
|
|
@property
|
|
def shape(self):
|
|
return self.data.shape[1:4]
|
|
|
|
@property
|
|
def dtype(self):
|
|
return self.data.dtype
|
|
|
|
@property
|
|
def origin(self):
|
|
t, r, z, s = transforms3d.affines.decompose44(self.affine)
|
|
return t.tolist()
|
|
|
|
@property
|
|
def euler(self):
|
|
t, r, z, s = transforms3d.affines.decompose44(self.affine)
|
|
return list(transforms3d.euler.mat2euler(r))
|
|
|
|
@property
|
|
def min(self):
|
|
return float(np.min(self.data))
|
|
|
|
@property
|
|
def frame_count(self):
|
|
return self.data.shape[0]
|
|
|
|
@property
|
|
def channel_count(self):
|
|
return self.data.shape[-1]
|
|
|
|
@property
|
|
def max(self):
|
|
return float(np.max(self.data))
|
|
|
|
def copy(self):
|
|
return copy.deepcopy(self)
|
|
|
|
def fill(self, value: float, mask: np.ndarray):
|
|
mask_frames = ()
|
|
if mask.ndim == 4:
|
|
if mask.shape[0] != self.frame_count:
|
|
raise Exception("Mask frame count is not same as ")
|
|
for f in range(self.frame_count):
|
|
mask_frame = mask[f, :, :, :]
|
|
mask_frame = scipy.median_filter(
|
|
mask_frame.astype(np.float32), size=2)
|
|
mask_frames += (mask_frame,)
|
|
elif mask.ndim == 3:
|
|
for f in range(self.frame_count):
|
|
mask_frame = mask[:, :, :]
|
|
mask_frame = scipy.median_filter(
|
|
mask_frame.astype(np.float32), size=2)
|
|
mask_frames += (mask_frame,)
|
|
else:
|
|
raise Exception("Mask shape order should be TXYZ or XYZ")
|
|
|
|
_mask = np.stack(mask_frames)
|
|
_mask = np.expand_dims(_mask, axis=-1)
|
|
self.data = _mask * value + (1-_mask) * self.data
|
|
|
|
def resize(self, shape:tuple, progress_callback=None):
|
|
if len(shape) != 3:
|
|
raise Exception("Shape must be 3 dim")
|
|
|
|
data = self.data
|
|
|
|
# TXYZC > TXYZ
|
|
if self.kind in ['label', 'scalar']:
|
|
data = np.amax(data, -1)
|
|
|
|
if self.kind in ['scalar']:
|
|
dtype = data.dtype
|
|
data = data.astype(np.float32)
|
|
|
|
data_frames = ()
|
|
for f in range(self.frame_count):
|
|
if progress_callback:
|
|
progress_callback(f, self.frame_count)
|
|
|
|
frame = ski.resize(data[f, :, :, :],
|
|
shape,
|
|
preserve_range=True,
|
|
anti_aliasing=data.dtype.kind != "b")
|
|
|
|
data_frames += (frame,)
|
|
|
|
data = np.stack(data_frames)
|
|
|
|
if self.kind in ['scalar']:
|
|
data = data.astype(dtype)
|
|
|
|
# TXYZ > TXYZC
|
|
if self.kind in ['label', 'scalar']:
|
|
data = np.expand_dims(data, axis=-1) # expend channel
|
|
|
|
self.data = data
|