Source code for pyfibre.core.base_multi_image
from abc import abstractmethod
import numpy as np
from traits.api import (
ABCHasTraits, ArrayOrNone, List, Dict, Str, Directory,
provides
)
from pyfibre.utilities import NotSupportedError
from .i_multi_image import IMultiImage
[docs]@provides(IMultiImage)
class BaseMultiImage(ABCHasTraits):
"""Base class representing an image with multiple channels,
expected to be more complex than just RGB"""
#: Name of BaseMultiImage
name = Str()
#: File path for images
path = Directory()
#: List of images in stack
image_stack = List(ArrayOrNone)
#: Dictionary containing references to each entry in
#: image_stack
image_dict = Dict(Str, ArrayOrNone)
def __init__(self, *args, **kwargs):
if 'image_stack' in kwargs:
if not self.verify_stack(kwargs['image_stack']):
raise ValueError(
f'image_stack not supported by {self.__class__}')
super(BaseMultiImage, self).__init__(*args, **kwargs)
def __len__(self):
return len(self.image_stack)
@property
def ndim(self):
"""Extends numpy API to get ndim of images in stack"""
if len(self):
return self.image_stack[0].ndim
@property
def shape(self):
"""Extends numpy API to get shape ofimages in stack"""
if len(self):
return self.image_stack[0].shape
@property
def size(self):
"""Extends numpy API to get size of images in stack"""
if len(self):
return self.image_stack[0].size
[docs] def append(self, image):
"""Appends an image to the image_stack. If image_stack
already contains existing images, make sure that the
shape on the incoming image matches"""
if len(self):
if image.shape != self.shape:
raise ValueError(
f'Image shape {image.shape} is not the same as '
f'BaseMultiImage shape {self.shape}')
self.image_stack.append(image)
[docs] def remove(self, image):
"""Removes an image with index from the image_stack"""
index = [
index for index, array in enumerate(self.image_stack)
if id(image) == id(array)
]
if index:
self.image_stack.pop(index[0])
else:
raise IndexError(
f"image not found in {self.__class__}.image_stack"
)
[docs] @classmethod
def from_array(cls, array):
"""Create instance from either a 2D or 3D numpy array"""
if array.ndim == 2:
return cls(image_stack=[array])
elif array.ndim == 3:
return cls(image_stack=[image for image in array])
raise NotSupportedError(
'MultiImage creation only supported for 2D or 3D arrays')
[docs] def to_array(self):
return np.stack(self.image_stack)
[docs] @classmethod
@abstractmethod
def verify_stack(cls, image_stack):
"""Perform verification that image_stack is allowed by
subclass of BaseMultiImage"""
[docs] @abstractmethod
def preprocess_images(self):
"""Implement operations that are used to pre-process
the image_stack before analysis"""