diff --git a/model_server/base/accessors.py b/model_server/base/accessors.py index 712bbacf19296201775e1d7d49ef45b356ac1d1d..c6a787bc4c12918b64d06e95c97c50cf4ad2fc53 100644 --- a/model_server/base/accessors.py +++ b/model_server/base/accessors.py @@ -8,6 +8,7 @@ from skimage.io import imread import czifile import tifffile +from extensions.chaeo.accessors import InvalidDataForPatchStackError from model_server.base.process import is_mask class GenericImageDataAccessor(ABC): @@ -234,4 +235,68 @@ class InvalidAxisKey(Error): pass class InvalidDataShape(Error): - pass \ No newline at end of file + pass + + +class PatchStack(InMemoryDataAccessor): + + def __init__(self, data): + """ + A sequence of n (generally) color 3D images of the same size + :param data: either a list of np.ndarrays of size YXCZ, or np.ndarray of size PYXCZ + """ + + if isinstance(data, list): # list of YXCZ patches + n = len(data) + yxcz_shape = np.array([e.shape for e in data]).max(axis=0) + nda = np.zeros( + (n, *yxcz_shape), dtype=data[0].dtype + ) + for i in range(0, len(data)): + s = tuple([slice(0, c) for c in data[i].shape]) + nda[i][s] = data[i] + + elif isinstance(data, np.ndarray) and len(data.shape) == 5: # interpret as PYXCZ + nda = data + else: + raise InvalidDataForPatchStackError(f'Cannot create accessor from {type(data)}') + + assert nda.ndim == 5 + self._data = nda + + def iat(self, i): + return self.data[i, :, :, :, :] + + def iat_yxcz(self, i): + return self.iat(i) + + @property + def count(self): + return self.shape_dict['P'] + + @property + def shape_dict(self): + return dict(zip(('P', 'Y', 'X', 'C', 'Z'), self.data.shape)) + + def get_list(self): + n = self.nz + return [self.data[:, :, 0, zi] for zi in range(0, n)] + + @property + def pyxcz(self): + return self.data + + @property + def pczyx(self): + return np.moveaxis( + self.data, + [0, 3, 4, 1, 2], + [0, 1, 2, 3, 4] + ) + @property + def shape(self): + return self.data.shape + + @property + def shape_dict(self): + return dict(zip(('P', 'Y', 'X', 'C', 'Z'), self.data.shape)) diff --git a/model_server/base/roiset.py b/model_server/base/roiset.py index 608e9d0965f5ea68f6186b36eb9c591dcb91f6fa..819f8552660f1051f2bf3e713faf74d21e58d508 100644 --- a/model_server/base/roiset.py +++ b/model_server/base/roiset.py @@ -17,7 +17,8 @@ from model_server.base.accessors import GenericImageDataAccessor, InMemoryDataAc from model_server.base.models import InstanceSegmentationModel from model_server.base.process import pad, rescale, resample_to_8bit, make_rgb from base.annotators import draw_box_on_patch, draw_contours_on_patch, draw_boxes_on_3d_image -from model_server.extensions.chaeo.accessors import write_patch_to_file, PatchStack +from model_server.extensions.chaeo.accessors import write_patch_to_file +from base.accessors import PatchStack from base.process import mask_largest_object diff --git a/model_server/extensions/chaeo/accessors.py b/model_server/extensions/chaeo/accessors.py index 45eee67b63847ba4df80814fd2d6a1fc0f154550..2307d80ccb0b90bd70e2ca03c6a3b14800069e65 100644 --- a/model_server/extensions/chaeo/accessors.py +++ b/model_server/extensions/chaeo/accessors.py @@ -7,67 +7,6 @@ from tifffile import imwrite from base.process import make_rgb from model_server.base.accessors import generate_file_accessor, InMemoryDataAccessor -class MonoPatchStack(InMemoryDataAccessor): - - def __init__(self, data): - """ - A sequence of n monochrome images of the same size - :param data: either np.ndarray of dimensions YXn, or a list of np.ndarrays of size YX - """ - - if isinstance(data, np.ndarray): - if data.ndim == 3: # interpret as YXZ - self._data = np.expand_dims(data, 2) - elif data.ndim == 4: # interpret as a copy another patch stack - self._data = data - else: - raise InvalidDataForPatchStackError() - elif isinstance(data, list): # list of YX patches - if len(data) == 0: - self._data = np.ndarray([0, 0, 0, 0], dtype='uin9') - elif len(data) == 1: - self._data = np.expand_dims( - np.array( - data[0].squeeze() - ), - (2, 3) - ) - else: - nda = np.array(data).squeeze() - assert nda.ndim == 3 - self._data = np.expand_dims( - np.moveaxis( - nda, - [1, 2, 0], - [0, 1, 2]), - 2 - ) - else: - raise InvalidDataForPatchStackError(f'Cannot create accessor from {type(data)}') - - def make_tczyx(self): - assert self.chroma == 1 - tyx = np.moveaxis( - self.data[:, :, 0, :], # YX(C)Z - [2, 0, 1], - [0, 1, 2] - ) - return np.expand_dims(tyx, (1, 2)) - - @property - def count(self): - return self.nz - - def iat(self, i): - return self.data[:, :, 0, i] - - def iat_yxcz(self, i): - return np.expand_dims(self.iat(i), (2, 3)) - - def get_list(self): - n = self.nz - return [self.data[:, :, 0, zi] for zi in range(0, n)] - class MonoPatchStackFromFile(MonoPatchStack): def __init__(self, fpath): @@ -81,70 +20,6 @@ class MonoPatchStackFromFile(MonoPatchStack): return self.file_acc.fpath -class PatchStack(InMemoryDataAccessor): - - def __init__(self, data): - """ - A sequence of n (generally) color 3D images of the same size - :param data: either a list of np.ndarrays of size YXCZ, or np.ndarray of size PYXCZ - """ - - if isinstance(data, list): # list of YXCZ patches - n = len(data) - yxcz_shape = np.array([e.shape for e in data]).max(axis=0) - nda = np.zeros( - (n, *yxcz_shape), dtype=data[0].dtype - ) - for i in range(0, len(data)): - s = tuple([slice(0, c) for c in data[i].shape]) - nda[i][s] = data[i] - - elif isinstance(data, np.ndarray) and len(data.shape) == 5: # interpret as PYXCZ - nda = data - else: - raise InvalidDataForPatchStackError(f'Cannot create accessor from {type(data)}') - - assert nda.ndim == 5 - self._data = nda - - def iat(self, i): - return self.data[i, :, :, :, :] - - def iat_yxcz(self, i): - return self.iat(i) - - @property - def count(self): - return self.shape_dict['P'] - - @property - def shape_dict(self): - return dict(zip(('P', 'Y', 'X', 'C', 'Z'), self.data.shape)) - - def get_list(self): - n = self.nz - return [self.data[:, :, 0, zi] for zi in range(0, n)] - - @property - def pyxcz(self): - return self.data - - @property - def pczyx(self): - return np.moveaxis( - self.data, - [0, 3, 4, 1, 2], - [0, 1, 2, 3, 4] - ) - @property - def shape(self): - return self.data.shape - - @property - def shape_dict(self): - return dict(zip(('P', 'Y', 'X', 'C', 'Z'), self.data.shape)) - - def write_patch_to_file(where, fname, yxcz): ext = fname.split('.')[-1].upper() where.mkdir(parents=True, exist_ok=True) diff --git a/model_server/extensions/chaeo/tests/test_accessors.py b/model_server/extensions/chaeo/tests/test_accessors.py index 0aa364c794de074524ecc231ee602208a8c99b4a..a92c058c6d9c490ba47f9b5d0d250c7acd8a8030 100644 --- a/model_server/extensions/chaeo/tests/test_accessors.py +++ b/model_server/extensions/chaeo/tests/test_accessors.py @@ -3,8 +3,8 @@ import unittest import numpy as np from model_server.conf.testing import monozstackmask -from model_server.extensions.chaeo.accessors import MonoPatchStackFromFile, PatchStack - +from model_server.extensions.chaeo.accessors import MonoPatchStackFromFile +from base.accessors import PatchStack class TestMultipositionCziImageFileAccess(unittest.TestCase): diff --git a/model_server/extensions/ilastik/models.py b/model_server/extensions/ilastik/models.py index 9a6de5e86bf9d13a1aa2d7e62af7ba974acd2920..91493a2bed5fc0f676206a3cf1b75a3ff4f7c00b 100644 --- a/model_server/extensions/ilastik/models.py +++ b/model_server/extensions/ilastik/models.py @@ -5,7 +5,7 @@ import numpy as np import vigra import model_server.extensions.ilastik.conf -from extensions.chaeo.accessors import PatchStack +from base.accessors import PatchStack from model_server.base.accessors import GenericImageDataAccessor, InMemoryDataAccessor from model_server.base.models import Model, ImageToImageModel, InstanceSegmentationModel, InvalidInputImageError, ParameterExpectedError, SemanticSegmentationModel