Source code for ddd_pylib.labels_manip._replace_values_operator
from ddd_pylib._base import ImageOperator, MeasurementsOperator
import numpy as np
import xarray as xr
from typing import List, Dict, Tuple
from skimage.segmentation import clear_border
import pandas as pd
[docs]
class ReplaceValuesOperator(ImageOperator):
"""
Takes an image and a dictionary of values representing a LUT and replaces the values in
the image according to this LUT. Only images with integer dtypes are supported.
"""
def __init__(self):
super().__init__()
self._valuesDict = None
self._lut = None
[docs]
def getPrefix(self) -> str:
return "ReplaceValues-"
[docs]
def setValuesDict(self, values_dict: Dict[np.integer, np.integer]|Dict[int, int]):
"""
Set the dictionary of values to be replaced. The keys are the original values, and the values are the new values.
If a value is not specified in the dictionary, it will be replaced by itself (i.e., it will remain unchanged).
"""
self._valuesDict = values_dict
[docs]
def sanityCheck(self):
super().sanityCheck()
dt = self.getMainImage().dtype
if not np.issubdtype(dt, np.integer):
raise TypeError("Main image must have an integer dtype.")
def _createLUT(self) -> np.ndarray:
self.sanityCheck()
if self._valuesDict is None:
raise ValueError("Values dictionary is not set.")
img = self.getMainImage()
# size of the LUT
max_value = int(np.max(img.values)) + 1
as_vector = [(k, v) for k, v in self._valuesDict.items()]
as_vector = sorted(as_vector, key=lambda x: x[0])
# list of original values
orig = np.array([k for k, _ in as_vector])
# what each original value should be mapped to
mapped_values = np.array([v for _, v in as_vector])
# LUT: indices = original values, values = new values
# default: values mapped to themselves
lut = np.arange(max_value, dtype=mapped_values.dtype)
# filling the LUT with the new value values
lut[orig] = mapped_values
return lut
def _replaceValues(self, image: xr.DataArray) -> xr.DataArray:
if self._lut is None:
raise ValueError("LUT is not created. Call _run() before applying value replacement.")
new_image = image.copy()
# broadcasting the shape of 'image' through the LUT to get the new values map
new_image.values = self._lut[image.values]
return new_image
def _applyToFrame(self, frameData, t, nT):
image = frameData[0]
return self._replaceValues(image)
def _run(self):
self._lut = self._createLUT()
yield from super()._run()