From 844e90f954bbda2355cdfa492c47d2f7e18b2473 Mon Sep 17 00:00:00 2001 From: mhitzem <88333018+mhitzem@users.noreply.github.com> Date: Mon, 18 Oct 2021 14:44:58 -0400 Subject: [PATCH 01/10] Filter to normalize contrast values in a 3d array This filter takes a 3D grayscale data array and normalizes the contrast and brightness values on each layer. --- normalize_grayscale.py | 149 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 normalize_grayscale.py diff --git a/normalize_grayscale.py b/normalize_grayscale.py new file mode 100644 index 00000000..dc14a46c --- /dev/null +++ b/normalize_grayscale.py @@ -0,0 +1,149 @@ +import numpy as np +import os + +from contextlib import ExitStack +from pathlib import Path +from typing import Any, AnyStr, Dict, Generator, List, Match, Pattern +from enum import IntEnum +from typing import List, Tuple, Union + +from dream3d.Filter import Filter, FilterDelegatePy +from dream3d.simpl import * + +class NormalizeGrayscale(Filter): + def __init__(self) -> None: + self.data_array: DataArrayPath = DataArrayPath('', '', '') + self.arr_name: str = '' + self.choice: bool = False + self.axes: int = 0 + self.new_arr: DataArrayPath = DataArrayPath('', '', '') + + def _set_data_array(self, value: DataArrayPath) -> None: + self.data_array = value + + def _get_data_array(self) -> DataArrayPath: + return self.data_array + + def _set_axes(self, value: int) -> None: + self.axes = value + + def _get_axes(self) -> int: + return self.axes + + def _set_choice(self, value: bool) -> None: + self.choice = value + + def _get_choice(self) -> bool: + return self.choice + + def _set_arr_name(self, value: str) -> None: + self.arr_name = value + + def _get_arr_name(self) -> str: + return self.arr_name + + def _set_new_arr(self, value: DataArrayPath) -> None: + self.new_arr = value + + def _get_new_arr(self) -> DataArrayPath: + return self.new_arr + + @staticmethod + def name() -> str: + return 'Grayscale Normalizer' + + @staticmethod + def uuid() -> str: + return '{b98fa052-4c74-4d25-96ce-5b95074fcecf}' + + @staticmethod + def group_name() -> str: + return 'Example' + + @staticmethod + def sub_group_name() -> str: + return 'Sub Example' + + @staticmethod + def human_label() -> str: + return 'Normalize Grayscale Values' + + @staticmethod + def version() -> str: + return '1.0.0' + + @staticmethod + def compiled_lib_name() -> str: + return 'Python' + + def setup_parameters(self) -> List[FilterParameter]: + req = DataArraySelectionFilterParameter.RequirementType([IGeometry.Type.Image], [], [], []) + return [ + DataArraySelectionFilterParameter('Select Data Array', 'data_array', self.data_array, FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), + ChoiceFilterParameter('Slice Axis', 'axes', self.axes, FilterParameter.Category.Parameter, self._set_axes, + self._get_axes, ["x", "y", "z"], False, -1), + LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.choice, FilterParameter.Category.Parameter, self._set_choice, self._get_choice, ['new_array'], -1), + DataArrayCreationFilterParameter('New Array', 'new_array', self.new_arr, FilterParameter.Category.CreatedArray, self._set_new_arr, self._get_new_arr, DataArrayCreationFilterParameter.RequirementType(), -1) + ] + + def data_check(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + dc = dca.getDataContainer(self.data_array) + am = dca.getAttributeMatrix(self.data_array) + + if not dca.doesAttributeMatrixExist(self.data_array): + return (-5550, 'One data array must be selected') + + if type(dc.Geometry.getDimensions()) != SizeVec3: + return (-301, 'Not a 3D array') + + if not dc.Geometry: + return (-302, 'DataContainer has no geometry') + + if dc.Geometry.getGeometryType() != IGeometry.Type.Image: + return (-303, 'Wrong geometry type') + + return (0, 'Success') + + def _execute_impl(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + dc = dca.getDataContainer(self.data_array) + udims = dc.Geometry.getDimensions() + shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] + + da = dca.getAttributeMatrix(self.data_array).getAttributeArray(self.data_array) + data = da.npview() + + data = data.astype(np.float64) + data = np.reshape(data, shape) + + arr = [] + + slice_axis = self.axes + shape = data.shape + + arr_avg = float(np.average(data)) + + #slices are in x-axis + if slice_axis == 0: + layer_avg = np.average(data, axis=(1,2)) + correction_factor = layer_avg/arr_avg + corrected_data = data / correction_factor[:, None, None] + + #slices are in y-axis + elif slice_axis == 1: + layer_avg = np.average(data, axis=(0,2)) + correction_factor = layer_avg / arr_avg + corrected_data = data/correction_factor[None, :, None] + + #slices are in z-axis + elif slice_axis == 2: + layer_avg = np.average(data, axis=(0,1)) + correction_factor = layer_avg / arr_avg + corrected_data = data / correction_factor[None, None, :] + + if self.choice: + self.new_arr = corrected_data + + return (0, 'Success') + + +filters = [NormalizeGrayscale] From 65f5576784b2621fe2ffd7082ec5024dfad0522c Mon Sep 17 00:00:00 2001 From: mhitzem <88333018+mhitzem@users.noreply.github.com> Date: Fri, 22 Oct 2021 14:22:41 -0400 Subject: [PATCH 02/10] Updated to add array into AttributeMatrix --- normalize_grayscale.py | 64 +++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 22 deletions(-) diff --git a/normalize_grayscale.py b/normalize_grayscale.py index dc14a46c..19503938 100644 --- a/normalize_grayscale.py +++ b/normalize_grayscale.py @@ -8,20 +8,21 @@ from typing import List, Tuple, Union from dream3d.Filter import Filter, FilterDelegatePy -from dream3d.simpl import * +import dream3d.simpl as simpl +import dream3d.simplpy as simplpy class NormalizeGrayscale(Filter): def __init__(self) -> None: - self.data_array: DataArrayPath = DataArrayPath('', '', '') + self.data_array: DataArrayPath = simpl.DataArrayPath('', '', '') self.arr_name: str = '' self.choice: bool = False self.axes: int = 0 - self.new_arr: DataArrayPath = DataArrayPath('', '', '') + self.new_arr: DataArrayPath = simpl.DataArrayPath('', '', '') - def _set_data_array(self, value: DataArrayPath) -> None: + def _set_data_array(self, value: simpl.DataArrayPath) -> None: self.data_array = value - def _get_data_array(self) -> DataArrayPath: + def _get_data_array(self) -> simpl.DataArrayPath: return self.data_array def _set_axes(self, value: int) -> None: @@ -42,10 +43,10 @@ def _set_arr_name(self, value: str) -> None: def _get_arr_name(self) -> str: return self.arr_name - def _set_new_arr(self, value: DataArrayPath) -> None: + def _set_new_arr(self, value: simpl.DataArrayPath) -> None: self.new_arr = value - def _get_new_arr(self) -> DataArrayPath: + def _get_new_arr(self) -> simpl.DataArrayPath: return self.new_arr @staticmethod @@ -76,51 +77,70 @@ def version() -> str: def compiled_lib_name() -> str: return 'Python' - def setup_parameters(self) -> List[FilterParameter]: - req = DataArraySelectionFilterParameter.RequirementType([IGeometry.Type.Image], [], [], []) + def setup_parameters(self) -> List[simpl.FilterParameter]: + req = simpl.DataArraySelectionFilterParameter.RequirementType([simpl.IGeometry.Type.Image], [], [], []) return [ - DataArraySelectionFilterParameter('Select Data Array', 'data_array', self.data_array, FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), - ChoiceFilterParameter('Slice Axis', 'axes', self.axes, FilterParameter.Category.Parameter, self._set_axes, + simpl.DataArraySelectionFilterParameter('Select Data Array', 'data_array', self.data_array, simpl.FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), + simpl.ChoiceFilterParameter('Slice Axis', 'axes', self.axes, simpl.FilterParameter.Category.Parameter, self._set_axes, self._get_axes, ["x", "y", "z"], False, -1), - LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.choice, FilterParameter.Category.Parameter, self._set_choice, self._get_choice, ['new_array'], -1), - DataArrayCreationFilterParameter('New Array', 'new_array', self.new_arr, FilterParameter.Category.CreatedArray, self._set_new_arr, self._get_new_arr, DataArrayCreationFilterParameter.RequirementType(), -1) + simpl.LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.choice, simpl.FilterParameter.Category.Parameter, self._set_choice, self._get_choice, ['new_array'], -1), + simpl.DataArrayCreationFilterParameter('New Array', 'new_array', self.new_arr, simpl.FilterParameter.Category.CreatedArray, self._set_new_arr, self._get_new_arr, simpl.DataArrayCreationFilterParameter.RequirementType(), -1) ] - def data_check(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + def data_check(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: dc = dca.getDataContainer(self.data_array) am = dca.getAttributeMatrix(self.data_array) + selected_data_array = am.getAttributeArray(self.data_array.DataArrayName) + #selected_data_array.getNumComponents != 1 -> could be req if not dca.doesAttributeMatrixExist(self.data_array): return (-5550, 'One data array must be selected') - if type(dc.Geometry.getDimensions()) != SizeVec3: + if type(dc.Geometry.getDimensions()) != simpl.SizeVec3: return (-301, 'Not a 3D array') if not dc.Geometry: return (-302, 'DataContainer has no geometry') - if dc.Geometry.getGeometryType() != IGeometry.Type.Image: + if dc.Geometry.getGeometryType() != simpl.IGeometry.Type.Image: return (-303, 'Wrong geometry type') + if self.choice: + udims = dc.Geometry.getDimensions() + shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] + arr_dtype = am.getAttributeArray(self.data_array).dtype + + new_data_array = np.zeros(shape, dtype = arr_dtype) + + # Create a simpl.DataArray object to hold the vertices by reference + #switch statement for dtype + new_simpl_array = simpl.UInt8Array(new_data_array, self.new_arr.DataArrayName) + am.addOrReplaceAttributeArray(new_simpl_array) + return (0, 'Success') - def _execute_impl(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + def _execute_impl(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: dc = dca.getDataContainer(self.data_array) udims = dc.Geometry.getDimensions() shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] da = dca.getAttributeMatrix(self.data_array).getAttributeArray(self.data_array) data = da.npview() - - data = data.astype(np.float64) data = np.reshape(data, shape) + if self.choice: + #simpl_data_array + da_new = dca.getAttributeMatrix(self.new_arr).getAttributeArray(self.new_arr) + #npv_data_array + corrected_data = da_new.npview() + corrected_data = np.reshape(corrected_data, shape) + arr = [] slice_axis = self.axes shape = data.shape - arr_avg = float(np.average(data)) + arr_avg = np.average(data) #slices are in x-axis if slice_axis == 0: @@ -140,8 +160,8 @@ def _execute_impl(self, dca: DataContainerArray, status_delegate: Union[FilterDe correction_factor = layer_avg / arr_avg corrected_data = data / correction_factor[None, None, :] - if self.choice: - self.new_arr = corrected_data + if not self.choice: + data = corrected_data return (0, 'Success') From 10d8718c14c3245b7636c9ff0f1c80b2449a4b18 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Fri, 22 Oct 2021 15:27:12 -0400 Subject: [PATCH 03/10] Add example for DataArraySelectionFilterParameter Requirement Type Signed-off-by: Michael Jackson --- Python/Filters/d3d_review_filter_1.py | 76 ++++++++++++++++++--------- 1 file changed, 52 insertions(+), 24 deletions(-) diff --git a/Python/Filters/d3d_review_filter_1.py b/Python/Filters/d3d_review_filter_1.py index 283c811d..36f7afaf 100644 --- a/Python/Filters/d3d_review_filter_1.py +++ b/Python/Filters/d3d_review_filter_1.py @@ -58,14 +58,17 @@ from typing import List, Tuple, Union -from dream3d.Filter import Filter, FilterDelegatePy -from dream3d.simpl import BooleanFilterParameter, DataContainerArray, StringFilterParameter, InputFileFilterParameter, FilterDelegateCpp, FilterParameter, IntFilterParameter, DataArraySelectionFilterParameter, DataArrayPath -from dream3d.simpl import InputPathFilterParameter, FloatFilterParameter -class D3DReviewTestFilter(Filter): +from dream3d.Filter import Filter as SIMPLFilter +from dream3d.Filter import FilterDelegatePy as SIMPLFilterDelegatePy +from dream3d.simpl import * +import dream3d.simpl as simpl + + +class D3DReviewTestFilter(SIMPLFilter): def __init__(self) -> None: self.int_param: int = 5 - self.dap_param: DataArrayPath = DataArrayPath('', '', '') + self.dap_param: simpl.DataArrayPath = simpl.DataArrayPath('', '', '') self.str_param: str = "Something" self.bool_param:bool = False self.input_file_param:str = "/No/Path/anywhere.txt" @@ -74,6 +77,7 @@ def __init__(self) -> None: def _set_float_param(self, value: float) -> None: self.float_param = value + def _get_float_param(self) -> float: return self.float_param @@ -83,10 +87,10 @@ def _set_int(self, value: int) -> None: def _get_int(self) -> int: return self.int_param - def _set_dap(self, value: DataArrayPath) -> None: + def _set_dap(self, value: simpl.DataArrayPath) -> None: self.dap_param = value - def _get_dap(self) -> DataArrayPath: + def _get_dap(self) -> simpl.DataArrayPath: return self.dap_param def _get_str(self) -> str: @@ -113,7 +117,6 @@ def _get_input_path_param(self) -> str: def _set_input_path_param(self, value: str) -> None: self.input_path_param = value - @staticmethod def name() -> str: return 'D3DReviewTestFilter' @@ -142,34 +145,59 @@ def version() -> str: def compiled_lib_name() -> str: return 'DREAM3DReview [Python]' - def setup_parameters(self) -> List[FilterParameter]: - req = DataArraySelectionFilterParameter.RequirementType() + + def setup_parameters(self) -> List[simpl.FilterParameter]: + + """ + inline const QString Bool("bool"); + inline const QString Float("float"); + inline const QString Double("double"); + inline const QString Int8("int8_t"); + inline const QString UInt8("uint8_t"); + inline const QString Int16("int16_t"); + inline const QString UInt16("uint16_t"); + inline const QString Int32("int32_t"); + inline const QString UInt32("uint32_t"); + inline const QString Int64("int64_t"); + inline const QString UInt64("uint64_t"); + """ + # Create a DataArraySelectionFilterParameter Requirement Type that only takes the following: + # Image Geometry + # AttributeMatrix must be a CellType + # DataArrayTypes are only float, double, int32_t # See the list from above + # DataArray Component dimensions are 1 or 3, i.e., a scalar or vector of size 3 only + req = simpl.DataArraySelectionFilterParameter.RequirementType() + req.dcGeometryTypes = [simpl.IGeometry.Type.Image] + req.amTypes = [simpl.AttributeMatrix.Type.Cell] + req.daTypes = ["float", "double", "int32_t"] + req.componentDimensions = [VectorSizeT([1]), VectorSizeT([3])] + return [ - IntFilterParameter('Integer', 'int_param', self.int_param, FilterParameter.Category.Parameter, self._set_int, self._get_int, -1), - DataArraySelectionFilterParameter('Data Array Path Selection', 'dap_param', self.dap_param, FilterParameter.Category.RequiredArray, self._set_dap, self._get_dap, req, -1), - StringFilterParameter('String', 'str_param', self.str_param, FilterParameter.Category.Parameter, self._set_str, self._get_str, -1), - BooleanFilterParameter('Boolean', 'bool_param', self.bool_param, FilterParameter.Category.Parameter, self._set_bool, self._get_bool, -1), - InputFileFilterParameter('Input File', 'input_file_param', self.input_file_param, - FilterParameter.Category.Parameter, self._set_input_file, self._get_input_file,'*.ang', 'EDAX Ang', -1), - InputPathFilterParameter('Input Directory', 'input_path_param', self.input_path_param, - FilterParameter.Category.Parameter, self._set_input_path_param, self._get_input_path_param, -1) + simpl.IntFilterParameter('Integer', 'int_param', self.int_param, simpl.FilterParameter.Category.Parameter, self._set_int, self._get_int, -1), + simpl.DataArraySelectionFilterParameter('Data Array Path Selection', 'dap_param', self.dap_param, simpl.FilterParameter.Category.RequiredArray, self._set_dap, self._get_dap, req, -1), + simpl.StringFilterParameter('String', 'str_param', self.str_param, simpl.FilterParameter.Category.Parameter, self._set_str, self._get_str, -1), + simpl.BooleanFilterParameter('Boolean', 'bool_param', self.bool_param, simpl.FilterParameter.Category.Parameter, self._set_bool, self._get_bool, -1), + simpl.InputFileFilterParameter('Input File', 'input_file_param', self.input_file_param, + simpl.FilterParameter.Category.Parameter, self._set_input_file, self._get_input_file,'*.ang', 'EDAX Ang', -1), + simpl.InputPathFilterParameter('Input Directory', 'input_path_param', self.input_path_param, + simpl.FilterParameter.Category.Parameter, self._set_input_path_param, self._get_input_path_param, -1) ] - def data_check(self, dca: DataContainerArray, delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + def data_check(self, dca: simpl.DataContainerArray, delegate: Union[simpl.FilterDelegateCpp, SIMPLFilterDelegatePy] = SIMPLFilterDelegatePy()) -> Tuple[int, str]: am = dca.getAttributeMatrix(self.dap_param) if am is None: - return (-1, 'AttributeMatrix is None') + return -1, 'AttributeMatrix is None' da = am.getAttributeArray(self.dap_param) if da is None: - return (-2, 'DataArray is None') + return -2, 'DataArray is None' delegate.notifyStatusMessage('data_check finished!') - return (0, 'Success') + return 0, 'Success' - def _execute_impl(self, dca: DataContainerArray, delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + def _execute_impl(self, dca: simpl.DataContainerArray, delegate: Union[simpl.FilterDelegateCpp, SIMPLFilterDelegatePy] = SIMPLFilterDelegatePy()) -> Tuple[int, str]: delegate.notifyStatusMessage(f'int_param = {self.int_param}') da = dca.getAttributeMatrix(self.dap_param).getAttributeArray(self.dap_param) @@ -180,6 +208,6 @@ def _execute_impl(self, dca: DataContainerArray, delegate: Union[FilterDelegateC delegate.notifyStatusMessage(f'after = {data}') delegate.notifyStatusMessage('execute finished!') - return (0, 'Success') + return 0, 'Success' filters = [D3DReviewTestFilter] \ No newline at end of file From 2bdcea861f6ff43c7c6fd25945b9325755c8f06f Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Fri, 22 Oct 2021 15:28:35 -0400 Subject: [PATCH 04/10] Move normalize_grayscale.py into the Python/Filters directory Signed-off-by: Michael Jackson --- Python/Filters/d3d_review_filter_1.py | 2 +- .../Filters/normalize_grayscale.py | 338 +++++++++--------- 2 files changed, 170 insertions(+), 170 deletions(-) rename normalize_grayscale.py => Python/Filters/normalize_grayscale.py (97%) diff --git a/Python/Filters/d3d_review_filter_1.py b/Python/Filters/d3d_review_filter_1.py index 36f7afaf..0df82f42 100644 --- a/Python/Filters/d3d_review_filter_1.py +++ b/Python/Filters/d3d_review_filter_1.py @@ -171,7 +171,7 @@ def setup_parameters(self) -> List[simpl.FilterParameter]: req.amTypes = [simpl.AttributeMatrix.Type.Cell] req.daTypes = ["float", "double", "int32_t"] req.componentDimensions = [VectorSizeT([1]), VectorSizeT([3])] - + return [ simpl.IntFilterParameter('Integer', 'int_param', self.int_param, simpl.FilterParameter.Category.Parameter, self._set_int, self._get_int, -1), simpl.DataArraySelectionFilterParameter('Data Array Path Selection', 'dap_param', self.dap_param, simpl.FilterParameter.Category.RequiredArray, self._set_dap, self._get_dap, req, -1), diff --git a/normalize_grayscale.py b/Python/Filters/normalize_grayscale.py similarity index 97% rename from normalize_grayscale.py rename to Python/Filters/normalize_grayscale.py index 19503938..a4b6210e 100644 --- a/normalize_grayscale.py +++ b/Python/Filters/normalize_grayscale.py @@ -1,169 +1,169 @@ -import numpy as np -import os - -from contextlib import ExitStack -from pathlib import Path -from typing import Any, AnyStr, Dict, Generator, List, Match, Pattern -from enum import IntEnum -from typing import List, Tuple, Union - -from dream3d.Filter import Filter, FilterDelegatePy -import dream3d.simpl as simpl -import dream3d.simplpy as simplpy - -class NormalizeGrayscale(Filter): - def __init__(self) -> None: - self.data_array: DataArrayPath = simpl.DataArrayPath('', '', '') - self.arr_name: str = '' - self.choice: bool = False - self.axes: int = 0 - self.new_arr: DataArrayPath = simpl.DataArrayPath('', '', '') - - def _set_data_array(self, value: simpl.DataArrayPath) -> None: - self.data_array = value - - def _get_data_array(self) -> simpl.DataArrayPath: - return self.data_array - - def _set_axes(self, value: int) -> None: - self.axes = value - - def _get_axes(self) -> int: - return self.axes - - def _set_choice(self, value: bool) -> None: - self.choice = value - - def _get_choice(self) -> bool: - return self.choice - - def _set_arr_name(self, value: str) -> None: - self.arr_name = value - - def _get_arr_name(self) -> str: - return self.arr_name - - def _set_new_arr(self, value: simpl.DataArrayPath) -> None: - self.new_arr = value - - def _get_new_arr(self) -> simpl.DataArrayPath: - return self.new_arr - - @staticmethod - def name() -> str: - return 'Grayscale Normalizer' - - @staticmethod - def uuid() -> str: - return '{b98fa052-4c74-4d25-96ce-5b95074fcecf}' - - @staticmethod - def group_name() -> str: - return 'Example' - - @staticmethod - def sub_group_name() -> str: - return 'Sub Example' - - @staticmethod - def human_label() -> str: - return 'Normalize Grayscale Values' - - @staticmethod - def version() -> str: - return '1.0.0' - - @staticmethod - def compiled_lib_name() -> str: - return 'Python' - - def setup_parameters(self) -> List[simpl.FilterParameter]: - req = simpl.DataArraySelectionFilterParameter.RequirementType([simpl.IGeometry.Type.Image], [], [], []) - return [ - simpl.DataArraySelectionFilterParameter('Select Data Array', 'data_array', self.data_array, simpl.FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), - simpl.ChoiceFilterParameter('Slice Axis', 'axes', self.axes, simpl.FilterParameter.Category.Parameter, self._set_axes, - self._get_axes, ["x", "y", "z"], False, -1), - simpl.LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.choice, simpl.FilterParameter.Category.Parameter, self._set_choice, self._get_choice, ['new_array'], -1), - simpl.DataArrayCreationFilterParameter('New Array', 'new_array', self.new_arr, simpl.FilterParameter.Category.CreatedArray, self._set_new_arr, self._get_new_arr, simpl.DataArrayCreationFilterParameter.RequirementType(), -1) - ] - - def data_check(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: - dc = dca.getDataContainer(self.data_array) - am = dca.getAttributeMatrix(self.data_array) - selected_data_array = am.getAttributeArray(self.data_array.DataArrayName) - #selected_data_array.getNumComponents != 1 -> could be req - - if not dca.doesAttributeMatrixExist(self.data_array): - return (-5550, 'One data array must be selected') - - if type(dc.Geometry.getDimensions()) != simpl.SizeVec3: - return (-301, 'Not a 3D array') - - if not dc.Geometry: - return (-302, 'DataContainer has no geometry') - - if dc.Geometry.getGeometryType() != simpl.IGeometry.Type.Image: - return (-303, 'Wrong geometry type') - - if self.choice: - udims = dc.Geometry.getDimensions() - shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] - arr_dtype = am.getAttributeArray(self.data_array).dtype - - new_data_array = np.zeros(shape, dtype = arr_dtype) - - # Create a simpl.DataArray object to hold the vertices by reference - #switch statement for dtype - new_simpl_array = simpl.UInt8Array(new_data_array, self.new_arr.DataArrayName) - am.addOrReplaceAttributeArray(new_simpl_array) - - return (0, 'Success') - - def _execute_impl(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: - dc = dca.getDataContainer(self.data_array) - udims = dc.Geometry.getDimensions() - shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] - - da = dca.getAttributeMatrix(self.data_array).getAttributeArray(self.data_array) - data = da.npview() - data = np.reshape(data, shape) - - if self.choice: - #simpl_data_array - da_new = dca.getAttributeMatrix(self.new_arr).getAttributeArray(self.new_arr) - #npv_data_array - corrected_data = da_new.npview() - corrected_data = np.reshape(corrected_data, shape) - - arr = [] - - slice_axis = self.axes - shape = data.shape - - arr_avg = np.average(data) - - #slices are in x-axis - if slice_axis == 0: - layer_avg = np.average(data, axis=(1,2)) - correction_factor = layer_avg/arr_avg - corrected_data = data / correction_factor[:, None, None] - - #slices are in y-axis - elif slice_axis == 1: - layer_avg = np.average(data, axis=(0,2)) - correction_factor = layer_avg / arr_avg - corrected_data = data/correction_factor[None, :, None] - - #slices are in z-axis - elif slice_axis == 2: - layer_avg = np.average(data, axis=(0,1)) - correction_factor = layer_avg / arr_avg - corrected_data = data / correction_factor[None, None, :] - - if not self.choice: - data = corrected_data - - return (0, 'Success') - - -filters = [NormalizeGrayscale] +import numpy as np +import os + +from contextlib import ExitStack +from pathlib import Path +from typing import Any, AnyStr, Dict, Generator, List, Match, Pattern +from enum import IntEnum +from typing import List, Tuple, Union + +from dream3d.Filter import Filter, FilterDelegatePy +import dream3d.simpl as simpl +import dream3d.simplpy as simplpy + +class NormalizeGrayscale(Filter): + def __init__(self) -> None: + self.data_array: DataArrayPath = simpl.DataArrayPath('', '', '') + self.arr_name: str = '' + self.choice: bool = False + self.axes: int = 0 + self.new_arr: DataArrayPath = simpl.DataArrayPath('', '', '') + + def _set_data_array(self, value: simpl.DataArrayPath) -> None: + self.data_array = value + + def _get_data_array(self) -> simpl.DataArrayPath: + return self.data_array + + def _set_axes(self, value: int) -> None: + self.axes = value + + def _get_axes(self) -> int: + return self.axes + + def _set_choice(self, value: bool) -> None: + self.choice = value + + def _get_choice(self) -> bool: + return self.choice + + def _set_arr_name(self, value: str) -> None: + self.arr_name = value + + def _get_arr_name(self) -> str: + return self.arr_name + + def _set_new_arr(self, value: simpl.DataArrayPath) -> None: + self.new_arr = value + + def _get_new_arr(self) -> simpl.DataArrayPath: + return self.new_arr + + @staticmethod + def name() -> str: + return 'Grayscale Normalizer' + + @staticmethod + def uuid() -> str: + return '{b98fa052-4c74-4d25-96ce-5b95074fcecf}' + + @staticmethod + def group_name() -> str: + return 'Example' + + @staticmethod + def sub_group_name() -> str: + return 'Sub Example' + + @staticmethod + def human_label() -> str: + return 'Normalize Grayscale Values' + + @staticmethod + def version() -> str: + return '1.0.0' + + @staticmethod + def compiled_lib_name() -> str: + return 'Python' + + def setup_parameters(self) -> List[simpl.FilterParameter]: + req = simpl.DataArraySelectionFilterParameter.RequirementType([simpl.IGeometry.Type.Image], [], [], []) + return [ + simpl.DataArraySelectionFilterParameter('Select Data Array', 'data_array', self.data_array, simpl.FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), + simpl.ChoiceFilterParameter('Slice Axis', 'axes', self.axes, simpl.FilterParameter.Category.Parameter, self._set_axes, + self._get_axes, ["x", "y", "z"], False, -1), + simpl.LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.choice, simpl.FilterParameter.Category.Parameter, self._set_choice, self._get_choice, ['new_array'], -1), + simpl.DataArrayCreationFilterParameter('New Array', 'new_array', self.new_arr, simpl.FilterParameter.Category.CreatedArray, self._set_new_arr, self._get_new_arr, simpl.DataArrayCreationFilterParameter.RequirementType(), -1) + ] + + def data_check(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + dc = dca.getDataContainer(self.data_array) + am = dca.getAttributeMatrix(self.data_array) + selected_data_array = am.getAttributeArray(self.data_array.DataArrayName) + #selected_data_array.getNumComponents != 1 -> could be req + + if not dca.doesAttributeMatrixExist(self.data_array): + return (-5550, 'One data array must be selected') + + if type(dc.Geometry.getDimensions()) != simpl.SizeVec3: + return (-301, 'Not a 3D array') + + if not dc.Geometry: + return (-302, 'DataContainer has no geometry') + + if dc.Geometry.getGeometryType() != simpl.IGeometry.Type.Image: + return (-303, 'Wrong geometry type') + + if self.choice: + udims = dc.Geometry.getDimensions() + shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] + arr_dtype = am.getAttributeArray(self.data_array).dtype + + new_data_array = np.zeros(shape, dtype = arr_dtype) + + # Create a simpl.DataArray object to hold the vertices by reference + #switch statement for dtype + new_simpl_array = simpl.UInt8Array(new_data_array, self.new_arr.DataArrayName) + am.addOrReplaceAttributeArray(new_simpl_array) + + return (0, 'Success') + + def _execute_impl(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + dc = dca.getDataContainer(self.data_array) + udims = dc.Geometry.getDimensions() + shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] + + da = dca.getAttributeMatrix(self.data_array).getAttributeArray(self.data_array) + data = da.npview() + data = np.reshape(data, shape) + + if self.choice: + #simpl_data_array + da_new = dca.getAttributeMatrix(self.new_arr).getAttributeArray(self.new_arr) + #npv_data_array + corrected_data = da_new.npview() + corrected_data = np.reshape(corrected_data, shape) + + arr = [] + + slice_axis = self.axes + shape = data.shape + + arr_avg = np.average(data) + + #slices are in x-axis + if slice_axis == 0: + layer_avg = np.average(data, axis=(1,2)) + correction_factor = layer_avg/arr_avg + corrected_data = data / correction_factor[:, None, None] + + #slices are in y-axis + elif slice_axis == 1: + layer_avg = np.average(data, axis=(0,2)) + correction_factor = layer_avg / arr_avg + corrected_data = data/correction_factor[None, :, None] + + #slices are in z-axis + elif slice_axis == 2: + layer_avg = np.average(data, axis=(0,1)) + correction_factor = layer_avg / arr_avg + corrected_data = data / correction_factor[None, None, :] + + if not self.choice: + data = corrected_data + + return (0, 'Success') + + +filters = [NormalizeGrayscale] From 2dfbcd0adf572ea9f7d3ff090bae3dcf3bfd5e09 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Fri, 22 Oct 2021 16:52:05 -0400 Subject: [PATCH 05/10] Fix Unit test to not crash on macOS. The line causing the crash was not needed Signed-off-by: Michael Jackson --- Python/unit_test.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Python/unit_test.py b/Python/unit_test.py index 0e63a52f..fdefd2d0 100644 --- a/Python/unit_test.py +++ b/Python/unit_test.py @@ -1,8 +1,7 @@ # make sure test_filter.py is on PYTHONPATH so it can be found for import from Filters import d3d_review_filter_1 -import dream3d.simpl as simpl -pf = simpl.PythonFilter(d3d_review_filter_1.D3DReviewTestFilter()) +import dream3d.simpl as simpl dca = simpl.DataContainerArray() @@ -11,6 +10,5 @@ py_filter = d3d_review_filter_1.D3DReviewTestFilter() error_code, message = py_filter.execute(dca) - print('Done.') From 2b7391db81e5e0748577e05cb5dec5a960c69173 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Sat, 23 Oct 2021 12:58:44 -0400 Subject: [PATCH 06/10] Add a conda environment file that will help users setup a virtual env Signed-off-by: Michael Jackson --- Python/Filters/TDMStoH5.md | 5 +++++ Python/environment.yml | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 Python/environment.yml diff --git a/Python/Filters/TDMStoH5.md b/Python/Filters/TDMStoH5.md index b0ba3331..e346cfba 100644 --- a/Python/Filters/TDMStoH5.md +++ b/Python/Filters/TDMStoH5.md @@ -4,6 +4,11 @@ Example(Sub Example) +## Required Conda Packages ## + ++ h5py ++ nptdms + ## Description ## This **Filter** converts a series of TDMS files into individual part files, where each part is stored in an HDF5 file. Three offsets can be used for the camera, diode, and laser. diff --git a/Python/environment.yml b/Python/environment.yml new file mode 100644 index 00000000..d0da7145 --- /dev/null +++ b/Python/environment.yml @@ -0,0 +1,26 @@ +name: bar +channels: + - conda-forge + - nodefaults + - https://dream3d.bluequartz.net/binaries/conda +dependencies: + - python=3.9 + - hdf5=1.10.6 + - zarr + - xarray + - Pillow>=8.2.0 + - tqdm=4.60.0 + - lxml=4.6.3 + - pyevtk + - scikit-image + - matplotlib + - ipyparallel + - pathlib>=1.0.1 + - h5py>=3.2.1 + - dream3d-conda>=1.4.2 + - pip + - pip: + - aicspylibczi + - itk==5.1.2 + + From d0886bfe598edc72db9469b89349817ce5093857 Mon Sep 17 00:00:00 2001 From: Michael Jackson Date: Sat, 23 Oct 2021 12:59:15 -0400 Subject: [PATCH 07/10] Lots of changes and updates. The algorithm is still not working Signed-off-by: Michael Jackson --- Python/Filters/normalize_grayscale.py | 224 +++++++++++++++----------- 1 file changed, 129 insertions(+), 95 deletions(-) diff --git a/Python/Filters/normalize_grayscale.py b/Python/Filters/normalize_grayscale.py index a4b6210e..e81a3483 100644 --- a/Python/Filters/normalize_grayscale.py +++ b/Python/Filters/normalize_grayscale.py @@ -1,29 +1,25 @@ import numpy as np -import os -from contextlib import ExitStack -from pathlib import Path -from typing import Any, AnyStr, Dict, Generator, List, Match, Pattern +from typing import List from enum import IntEnum from typing import List, Tuple, Union from dream3d.Filter import Filter, FilterDelegatePy -import dream3d.simpl as simpl -import dream3d.simplpy as simplpy +from dream3d.simpl import NumericTypes +from dream3d.simpl import * class NormalizeGrayscale(Filter): def __init__(self) -> None: - self.data_array: DataArrayPath = simpl.DataArrayPath('', '', '') - self.arr_name: str = '' - self.choice: bool = False + self.selected_data_array_path: DataArrayPath = DataArrayPath('', '', '') + self.create_data_array: bool = False self.axes: int = 0 - self.new_arr: DataArrayPath = simpl.DataArrayPath('', '', '') + self.created_data_array_path: DataArrayPath = DataArrayPath('', '', '') - def _set_data_array(self, value: simpl.DataArrayPath) -> None: - self.data_array = value + def _set_selected_data_array_path(self, value: DataArrayPath) -> None: + self.selected_data_array_path = value - def _get_data_array(self) -> simpl.DataArrayPath: - return self.data_array + def _get_selected_data_array_path(self) -> DataArrayPath: + return self.selected_data_array_path def _set_axes(self, value: int) -> None: self.axes = value @@ -31,27 +27,21 @@ def _set_axes(self, value: int) -> None: def _get_axes(self) -> int: return self.axes - def _set_choice(self, value: bool) -> None: - self.choice = value + def _set_create_data_array(self, value: bool) -> None: + self.create_data_array = value - def _get_choice(self) -> bool: - return self.choice + def _get_create_data_array(self) -> bool: + return self.create_data_array - def _set_arr_name(self, value: str) -> None: - self.arr_name = value + def _set_created_data_array_path(self, value: DataArrayPath) -> None: + self.created_data_array_path = value - def _get_arr_name(self) -> str: - return self.arr_name - - def _set_new_arr(self, value: simpl.DataArrayPath) -> None: - self.new_arr = value - - def _get_new_arr(self) -> simpl.DataArrayPath: - return self.new_arr + def _get_created_data_array_path(self) -> DataArrayPath: + return self.created_data_array_path @staticmethod def name() -> str: - return 'Grayscale Normalizer' + return 'Grayscale Normalizer [Python]' @staticmethod def uuid() -> str: @@ -59,15 +49,15 @@ def uuid() -> str: @staticmethod def group_name() -> str: - return 'Example' + return 'Processing' @staticmethod def sub_group_name() -> str: - return 'Sub Example' + return 'Normalization' @staticmethod def human_label() -> str: - return 'Normalize Grayscale Values' + return 'Normalize Grayscale Values [Python]' @staticmethod def version() -> str: @@ -77,92 +67,136 @@ def version() -> str: def compiled_lib_name() -> str: return 'Python' - def setup_parameters(self) -> List[simpl.FilterParameter]: - req = simpl.DataArraySelectionFilterParameter.RequirementType([simpl.IGeometry.Type.Image], [], [], []) + def setup_parameters(self) -> List[FilterParameter]: + # Create a DataArraySelectionFilterParameter Requirement Type that only takes the following: + # Image Geometry + # AttributeMatrix must be a CellType + # DataArray Component dimensions are 1, i.e., a scalar value + req = DataArraySelectionFilterParameter.RequirementType() + req.dcGeometryTypes = [IGeometry.Type.Image] + req.amTypes = [AttributeMatrix.Type.Cell] + # req.daTypes = ["float", "double", "int32_t"] + req.componentDimensions = [VectorSizeT([1])] + return [ - simpl.DataArraySelectionFilterParameter('Select Data Array', 'data_array', self.data_array, simpl.FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), - simpl.ChoiceFilterParameter('Slice Axis', 'axes', self.axes, simpl.FilterParameter.Category.Parameter, self._set_axes, + DataArraySelectionFilterParameter('Input Data Array', 'selected_data_array_path', self.selected_data_array_path, FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), + ChoiceFilterParameter('Slice Axis', 'axes', self.axes, FilterParameter.Category.Parameter, self._set_axes, self._get_axes, ["x", "y", "z"], False, -1), - simpl.LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.choice, simpl.FilterParameter.Category.Parameter, self._set_choice, self._get_choice, ['new_array'], -1), - simpl.DataArrayCreationFilterParameter('New Array', 'new_array', self.new_arr, simpl.FilterParameter.Category.CreatedArray, self._set_new_arr, self._get_new_arr, simpl.DataArrayCreationFilterParameter.RequirementType(), -1) + LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.create_data_array, FilterParameter.Category.Parameter, self._set_create_data_array, self._get_create_data_array, ['new_array'], -1), + DataArrayCreationFilterParameter('New Array', 'new_array', self.created_data_array_path, FilterParameter.Category.CreatedArray, self._set_created_data_array_path, self._get_created_data_array_path, DataArrayCreationFilterParameter.RequirementType(), -1) ] - def data_check(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: - dc = dca.getDataContainer(self.data_array) - am = dca.getAttributeMatrix(self.data_array) - selected_data_array = am.getAttributeArray(self.data_array.DataArrayName) - #selected_data_array.getNumComponents != 1 -> could be req - - if not dca.doesAttributeMatrixExist(self.data_array): - return (-5550, 'One data array must be selected') + def data_check(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + # Methodically check for each level of the selected data array path. + # Ensure the proper geometry is available + # Ensure the number of components of the input data array is 1 + # Error messages should give as much information as possible. users will rely on the + # error message to debug their pipelines, sometimes from the command line so printing + # out the DataArrayPath that was passed in is helpful for the user. + # Error Codes: Should start at a unique value somewhere between 10,000 and 100,000 and then just increment up from there - if type(dc.Geometry.getDimensions()) != simpl.SizeVec3: - return (-301, 'Not a 3D array') + dc:DataContainer = dca.getDataContainer(self.selected_data_array_path) + if dc is None: + return -61550, 'DataContainer does not exist: ' + self.selected_data_array_path.DataContainerName if not dc.Geometry: - return (-302, 'DataContainer has no geometry') - - if dc.Geometry.getGeometryType() != simpl.IGeometry.Type.Image: - return (-303, 'Wrong geometry type') - - if self.choice: - udims = dc.Geometry.getDimensions() - shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] - arr_dtype = am.getAttributeArray(self.data_array).dtype - - new_data_array = np.zeros(shape, dtype = arr_dtype) - - # Create a simpl.DataArray object to hold the vertices by reference - #switch statement for dtype - new_simpl_array = simpl.UInt8Array(new_data_array, self.new_arr.DataArrayName) - am.addOrReplaceAttributeArray(new_simpl_array) + return (-61551, 'Selected DataContainer has no geometry.') + + if dc.Geometry.getGeometryType() != IGeometry.Type.Image: + return (-61552, 'This filter requires a DataContainer with an ImageGeometry. Selected DataContainer has ' + dc.Geometry.getGeometryTypeAsString()) + + am:AttributeMatrix = dca.getAttributeMatrix(self.selected_data_array_path) + if am is None: + return -61553, 'AttributeMatrix does not exist: ' + self.selected_data_array_path.AttributeMatrixName + + selected_data_array:IDataArray = am.getAttributeArray(self.selected_data_array_path.DataArrayName) + if selected_data_array is None: + return -61554, 'DataArray does not exist: ' + self.selected_data_array_path.DataArrayName + + if selected_data_array.getNumberOfComponents() != 1: + return -61555, 'Selected DataArray must have a single component: ' + str(selected_data_array.getNumberOfComponents()) + + if not self.create_data_array: + return -61556, 'New Array must be created' + + # If we are creating an array, what other options are there, then we need to create the array (but not allocate the memory during preflight) + # using the createNonPrereqArrayFromPath should do this for us. + if self.create_data_array: + arr_dtype = selected_data_array.dtype + if arr_dtype == "int8": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int8, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "uint8": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt8, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "int16": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int16, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "uint16": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt16, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "int32": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int32, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "uint32": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt32, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "int64": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int64, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "uint64": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt64, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "float32": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Float, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "float64": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Double, self.created_data_array_path, 0, VectorSizeT([1])) + elif arr_dtype == "bool": + return -61556, 'Bool type arrays are not supported as inputs to this algorithm' return (0, 'Success') - def _execute_impl(self, dca: simpl.DataContainerArray, status_delegate: Union[simpl.FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: - dc = dca.getDataContainer(self.data_array) - udims = dc.Geometry.getDimensions() - shape = [np.int64(udims[2]), np.int64(udims[1]), np.int64(udims[0])] - - da = dca.getAttributeMatrix(self.data_array).getAttributeArray(self.data_array) - data = da.npview() - data = np.reshape(data, shape) - - if self.choice: - #simpl_data_array - da_new = dca.getAttributeMatrix(self.new_arr).getAttributeArray(self.new_arr) - #npv_data_array - corrected_data = da_new.npview() - corrected_data = np.reshape(corrected_data, shape) - - arr = [] - + def _execute_impl(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + + # The super class will rerun preflight but this time all of the arrays will get allocated. If preflight _still_ + # caused an error the function will *not* get called at all so in theory everything should be available without + # much error checking going on if the data check is detailed enough to catch all of the possible errors. + + # Grab out all of the needed objects from the DataContainerArray + dc:DataContainer = dca.getDataContainer(self.selected_data_array_path) + am:AttributeMatrix = dc.getAttributeMatrix(self.selected_data_array_path) + selected_data_array:IDataArray = am.getAttributeArray(self.selected_data_array_path) + created_data_array:IDataArray = am.getAttributeArray(self.created_data_array_path) + + # Note that the dimensions come back as X, Y, Z (Fastest to slowest) so we will need to invert the order if using them in a numpy array + # Note however that the data is actually stored in the SIMPL DataArray in the correct order + imageGeomDims:SizeVec3 = dc.Geometry.getDimensions() + shape = [np.int64(imageGeomDims[2]), np.int64(imageGeomDims[1]), np.int64(imageGeomDims[0])] + + npv_selected_data = selected_data_array.npview() + npv_selected_data = np.reshape(npv_selected_data, shape) + + # Get a numpy view into the data and then reshape the numpy array + corrected_data = created_data_array.npview() + corrected_data = np.reshape(corrected_data, shape) + + # Get the average of the entire data set + arr_avg = np.average(npv_selected_data) + print(f'arr_avg: {arr_avg}') slice_axis = self.axes - shape = data.shape - - arr_avg = np.average(data) - #slices are in x-axis if slice_axis == 0: - layer_avg = np.average(data, axis=(1,2)) + layer_avg = np.average(npv_selected_data, axis=(0,1)) correction_factor = layer_avg/arr_avg - corrected_data = data / correction_factor[:, None, None] + corrected_data = npv_selected_data / correction_factor[None, None, :] #slices are in y-axis elif slice_axis == 1: - layer_avg = np.average(data, axis=(0,2)) + layer_avg = np.average(npv_selected_data, axis=(0,2)) correction_factor = layer_avg / arr_avg - corrected_data = data/correction_factor[None, :, None] + corrected_data = npv_selected_data/correction_factor[None, :, None] #slices are in z-axis elif slice_axis == 2: - layer_avg = np.average(data, axis=(0,1)) + layer_avg = np.average(npv_selected_data, axis=(1,2)) correction_factor = layer_avg / arr_avg - corrected_data = data / correction_factor[None, None, :] - - if not self.choice: - data = corrected_data + corrected_data = npv_selected_data / correction_factor[:, None, None] + # if not self.create_data_array: + # data = corrected_data + return (0, 'Success') From c3009990264fe3c995761949218e95a23df01ecd Mon Sep 17 00:00:00 2001 From: mhitzem <88333018+mhitzem@users.noreply.github.com> Date: Mon, 25 Oct 2021 14:14:39 -0400 Subject: [PATCH 08/10] Add md documentation for Grayscale Normalizer --- Python/Filters/GrayscaleNormalizer.md | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 Python/Filters/GrayscaleNormalizer.md diff --git a/Python/Filters/GrayscaleNormalizer.md b/Python/Filters/GrayscaleNormalizer.md new file mode 100644 index 00000000..de5b853a --- /dev/null +++ b/Python/Filters/GrayscaleNormalizer.md @@ -0,0 +1,47 @@ +# Normalize Grayscale Values [Python] # + +## Group (Subgroup) ## + +Processing(Normalization) + +## Description ## + +This **Filter** normalizes brightness and contrast values in a 3D grayscale data array. The **Filter** gets the averages each layer in the x, y, or z direction. + +## Parameters ## + +| Name | Type | Description | +|------|------|------| +| Slice Axis | int | The direction for each layer | +| Create New Array | boolean | If checked, a new array is created with the corrected values. Else, the inputted array is overwritten. | + + +## Required Geometry ## + +Image + +## Required Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|------|------|------|------| +| **Cell Data Array** | AttributeArray Name | Any | (3) | The selected data array to apply normalization | + +## Created Objects ## + +| Kind | Default Name | Type | Component Dimensions | Description | +|------|------|------|------|------| + | **Cell Data Array** |AttributeArray Name | same as Required Data Array | (3) | Optional array created to store normalized data | + + +## Example Pipelines ## + +List the names of the example pipelines where this filter is used. + +## License & Copyright ## + +Please see the description file distributed with this plugin. + +## DREAM3D Mailing Lists ## + +If you need more help with a filter, please consider asking your question on the DREAM3D Users mailing list: +https://groups.google.com/forum/?hl=en#!forum/dream3d-users From dc6437056b2fd6a18d0ddbe8f97d7d8e66bf2eec Mon Sep 17 00:00:00 2001 From: mhitzem <88333018+mhitzem@users.noreply.github.com> Date: Mon, 15 Nov 2021 15:53:10 -0500 Subject: [PATCH 09/10] Fixed issues with np array to d3d array --- Python/Filters/normalize_grayscale.py | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Python/Filters/normalize_grayscale.py b/Python/Filters/normalize_grayscale.py index e81a3483..a7f66708 100644 --- a/Python/Filters/normalize_grayscale.py +++ b/Python/Filters/normalize_grayscale.py @@ -79,7 +79,7 @@ def setup_parameters(self) -> List[FilterParameter]: req.componentDimensions = [VectorSizeT([1])] return [ - DataArraySelectionFilterParameter('Input Data Array', 'selected_data_array_path', self.selected_data_array_path, FilterParameter.Category.RequiredArray, self._set_data_array, self._get_data_array, req, -1), + DataArraySelectionFilterParameter('Input Data Array', 'selected_data_array_path', self.selected_data_array_path, FilterParameter.Category.RequiredArray, self._set_selected_data_array_path, self._get_selected_data_array_path, req, -1), ChoiceFilterParameter('Slice Axis', 'axes', self.axes, FilterParameter.Category.Parameter, self._set_axes, self._get_axes, ["x", "y", "z"], False, -1), LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.create_data_array, FilterParameter.Category.Parameter, self._set_create_data_array, self._get_create_data_array, ['new_array'], -1), @@ -150,15 +150,12 @@ def data_check(self, dca: DataContainerArray, status_delegate: Union[FilterDeleg def _execute_impl(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: - # The super class will rerun preflight but this time all of the arrays will get allocated. If preflight _still_ - # caused an error the function will *not* get called at all so in theory everything should be available without - # much error checking going on if the data check is detailed enough to catch all of the possible errors. - - # Grab out all of the needed objects from the DataContainerArray dc:DataContainer = dca.getDataContainer(self.selected_data_array_path) am:AttributeMatrix = dc.getAttributeMatrix(self.selected_data_array_path) selected_data_array:IDataArray = am.getAttributeArray(self.selected_data_array_path) created_data_array:IDataArray = am.getAttributeArray(self.created_data_array_path) + status_delegate.notifyStatusMessage(f" Selected D3D data array is {selected_data_array}") + # Note that the dimensions come back as X, Y, Z (Fastest to slowest) so we will need to invert the order if using them in a numpy array # Note however that the data is actually stored in the SIMPL DataArray in the correct order @@ -167,36 +164,39 @@ def _execute_impl(self, dca: DataContainerArray, status_delegate: Union[FilterDe npv_selected_data = selected_data_array.npview() npv_selected_data = np.reshape(npv_selected_data, shape) + selected_data_dtype = npv_selected_data.dtype # Get a numpy view into the data and then reshape the numpy array - corrected_data = created_data_array.npview() - corrected_data = np.reshape(corrected_data, shape) + npv_created_data_array = created_data_array.npview() + npv_created_data_array = np.reshape(npv_created_data_array, shape) # Get the average of the entire data set arr_avg = np.average(npv_selected_data) - print(f'arr_avg: {arr_avg}') slice_axis = self.axes + #slices are in x-axis if slice_axis == 0: layer_avg = np.average(npv_selected_data, axis=(0,1)) correction_factor = layer_avg/arr_avg - corrected_data = npv_selected_data / correction_factor[None, None, :] + numpy_created_data_array = npv_selected_data / correction_factor[None, None, :] # Makes a copy #slices are in y-axis elif slice_axis == 1: layer_avg = np.average(npv_selected_data, axis=(0,2)) correction_factor = layer_avg / arr_avg - corrected_data = npv_selected_data/correction_factor[None, :, None] + numpy_created_data_array = npv_selected_data / correction_factor[None, :, None] # Makes a copy #slices are in z-axis elif slice_axis == 2: layer_avg = np.average(npv_selected_data, axis=(1,2)) correction_factor = layer_avg / arr_avg - corrected_data = npv_selected_data / correction_factor[:, None, None] + numpy_created_data_array = npv_selected_data / correction_factor[:, None, None] # Makes a copy + + # Assigns the data from the numpy array numpy_created_data_array to the view into the SIMPL.DataArray + # through a copy operation. + # https://numpy.org/doc/stable/user/basics.indexing.html#assigning-values-to-indexed-arrays + npv_created_data_array[...] = numpy_created_data_array - # if not self.create_data_array: - # data = corrected_data - return (0, 'Success') From 7bbd17c7ee55c18ba799bb96a4d988b59447e166 Mon Sep 17 00:00:00 2001 From: mhitzem <88333018+mhitzem@users.noreply.github.com> Date: Tue, 21 Dec 2021 13:29:57 -0500 Subject: [PATCH 10/10] Removed Linked Boolean --- Python/Filters/normalize_grayscale.py | 387 +++++++++++++------------- 1 file changed, 198 insertions(+), 189 deletions(-) diff --git a/Python/Filters/normalize_grayscale.py b/Python/Filters/normalize_grayscale.py index a7f66708..bdcf74d2 100644 --- a/Python/Filters/normalize_grayscale.py +++ b/Python/Filters/normalize_grayscale.py @@ -8,196 +8,205 @@ from dream3d.simpl import NumericTypes from dream3d.simpl import * + class NormalizeGrayscale(Filter): - def __init__(self) -> None: - self.selected_data_array_path: DataArrayPath = DataArrayPath('', '', '') - self.create_data_array: bool = False - self.axes: int = 0 - self.created_data_array_path: DataArrayPath = DataArrayPath('', '', '') - - def _set_selected_data_array_path(self, value: DataArrayPath) -> None: - self.selected_data_array_path = value - - def _get_selected_data_array_path(self) -> DataArrayPath: - return self.selected_data_array_path - - def _set_axes(self, value: int) -> None: - self.axes = value - - def _get_axes(self) -> int: - return self.axes - - def _set_create_data_array(self, value: bool) -> None: - self.create_data_array = value - - def _get_create_data_array(self) -> bool: - return self.create_data_array - - def _set_created_data_array_path(self, value: DataArrayPath) -> None: - self.created_data_array_path = value - - def _get_created_data_array_path(self) -> DataArrayPath: - return self.created_data_array_path - - @staticmethod - def name() -> str: - return 'Grayscale Normalizer [Python]' - - @staticmethod - def uuid() -> str: - return '{b98fa052-4c74-4d25-96ce-5b95074fcecf}' - - @staticmethod - def group_name() -> str: - return 'Processing' - - @staticmethod - def sub_group_name() -> str: - return 'Normalization' - - @staticmethod - def human_label() -> str: - return 'Normalize Grayscale Values [Python]' - - @staticmethod - def version() -> str: - return '1.0.0' - - @staticmethod - def compiled_lib_name() -> str: - return 'Python' - - def setup_parameters(self) -> List[FilterParameter]: - # Create a DataArraySelectionFilterParameter Requirement Type that only takes the following: - # Image Geometry - # AttributeMatrix must be a CellType - # DataArray Component dimensions are 1, i.e., a scalar value - req = DataArraySelectionFilterParameter.RequirementType() - req.dcGeometryTypes = [IGeometry.Type.Image] - req.amTypes = [AttributeMatrix.Type.Cell] - # req.daTypes = ["float", "double", "int32_t"] - req.componentDimensions = [VectorSizeT([1])] - - return [ - DataArraySelectionFilterParameter('Input Data Array', 'selected_data_array_path', self.selected_data_array_path, FilterParameter.Category.RequiredArray, self._set_selected_data_array_path, self._get_selected_data_array_path, req, -1), - ChoiceFilterParameter('Slice Axis', 'axes', self.axes, FilterParameter.Category.Parameter, self._set_axes, - self._get_axes, ["x", "y", "z"], False, -1), - LinkedBooleanFilterParameter('Create New Array', 'create_new_array', self.create_data_array, FilterParameter.Category.Parameter, self._set_create_data_array, self._get_create_data_array, ['new_array'], -1), - DataArrayCreationFilterParameter('New Array', 'new_array', self.created_data_array_path, FilterParameter.Category.CreatedArray, self._set_created_data_array_path, self._get_created_data_array_path, DataArrayCreationFilterParameter.RequirementType(), -1) - ] - - def data_check(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: - # Methodically check for each level of the selected data array path. - # Ensure the proper geometry is available - # Ensure the number of components of the input data array is 1 - # Error messages should give as much information as possible. users will rely on the - # error message to debug their pipelines, sometimes from the command line so printing - # out the DataArrayPath that was passed in is helpful for the user. - # Error Codes: Should start at a unique value somewhere between 10,000 and 100,000 and then just increment up from there - - dc:DataContainer = dca.getDataContainer(self.selected_data_array_path) - if dc is None: - return -61550, 'DataContainer does not exist: ' + self.selected_data_array_path.DataContainerName - - if not dc.Geometry: - return (-61551, 'Selected DataContainer has no geometry.') - - if dc.Geometry.getGeometryType() != IGeometry.Type.Image: - return (-61552, 'This filter requires a DataContainer with an ImageGeometry. Selected DataContainer has ' + dc.Geometry.getGeometryTypeAsString()) - - am:AttributeMatrix = dca.getAttributeMatrix(self.selected_data_array_path) - if am is None: - return -61553, 'AttributeMatrix does not exist: ' + self.selected_data_array_path.AttributeMatrixName - - selected_data_array:IDataArray = am.getAttributeArray(self.selected_data_array_path.DataArrayName) - if selected_data_array is None: - return -61554, 'DataArray does not exist: ' + self.selected_data_array_path.DataArrayName - - if selected_data_array.getNumberOfComponents() != 1: - return -61555, 'Selected DataArray must have a single component: ' + str(selected_data_array.getNumberOfComponents()) - - if not self.create_data_array: - return -61556, 'New Array must be created' - - # If we are creating an array, what other options are there, then we need to create the array (but not allocate the memory during preflight) - # using the createNonPrereqArrayFromPath should do this for us. - if self.create_data_array: - arr_dtype = selected_data_array.dtype - if arr_dtype == "int8": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int8, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "uint8": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt8, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "int16": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int16, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "uint16": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt16, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "int32": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int32, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "uint32": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt32, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "int64": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int64, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "uint64": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt64, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "float32": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Float, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "float64": - dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Double, self.created_data_array_path, 0, VectorSizeT([1])) - elif arr_dtype == "bool": - return -61556, 'Bool type arrays are not supported as inputs to this algorithm' - - return (0, 'Success') - - def _execute_impl(self, dca: DataContainerArray, status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: - - dc:DataContainer = dca.getDataContainer(self.selected_data_array_path) - am:AttributeMatrix = dc.getAttributeMatrix(self.selected_data_array_path) - selected_data_array:IDataArray = am.getAttributeArray(self.selected_data_array_path) - created_data_array:IDataArray = am.getAttributeArray(self.created_data_array_path) - status_delegate.notifyStatusMessage(f" Selected D3D data array is {selected_data_array}") - - - # Note that the dimensions come back as X, Y, Z (Fastest to slowest) so we will need to invert the order if using them in a numpy array - # Note however that the data is actually stored in the SIMPL DataArray in the correct order - imageGeomDims:SizeVec3 = dc.Geometry.getDimensions() - shape = [np.int64(imageGeomDims[2]), np.int64(imageGeomDims[1]), np.int64(imageGeomDims[0])] - - npv_selected_data = selected_data_array.npview() - npv_selected_data = np.reshape(npv_selected_data, shape) - selected_data_dtype = npv_selected_data.dtype - - # Get a numpy view into the data and then reshape the numpy array - npv_created_data_array = created_data_array.npview() - npv_created_data_array = np.reshape(npv_created_data_array, shape) - - # Get the average of the entire data set - arr_avg = np.average(npv_selected_data) - slice_axis = self.axes - - #slices are in x-axis - if slice_axis == 0: - layer_avg = np.average(npv_selected_data, axis=(0,1)) - correction_factor = layer_avg/arr_avg - numpy_created_data_array = npv_selected_data / correction_factor[None, None, :] # Makes a copy - - #slices are in y-axis - elif slice_axis == 1: - layer_avg = np.average(npv_selected_data, axis=(0,2)) - correction_factor = layer_avg / arr_avg - numpy_created_data_array = npv_selected_data / correction_factor[None, :, None] # Makes a copy - - #slices are in z-axis - elif slice_axis == 2: - layer_avg = np.average(npv_selected_data, axis=(1,2)) - correction_factor = layer_avg / arr_avg - numpy_created_data_array = npv_selected_data / correction_factor[:, None, None] # Makes a copy - - # Assigns the data from the numpy array numpy_created_data_array to the view into the SIMPL.DataArray - # through a copy operation. - # https://numpy.org/doc/stable/user/basics.indexing.html#assigning-values-to-indexed-arrays - npv_created_data_array[...] = numpy_created_data_array - - return (0, 'Success') + def __init__(self) -> None: + self.selected_data_array_path: DataArrayPath = DataArrayPath('', '', '') + self.axes: int = 0 + self.created_data_array_path: DataArrayPath = DataArrayPath('', '', '') + + def _set_selected_data_array_path(self, value: DataArrayPath) -> None: + self.selected_data_array_path = value + + def _get_selected_data_array_path(self) -> DataArrayPath: + return self.selected_data_array_path + + def _set_axes(self, value: int) -> None: + self.axes = value + + def _get_axes(self) -> int: + return self.axes + + def _set_created_data_array_path(self, value: DataArrayPath) -> None: + self.created_data_array_path = value + + def _get_created_data_array_path(self) -> DataArrayPath: + return self.created_data_array_path + + @staticmethod + def name() -> str: + return 'Grayscale Normalizer [Python]' + + @staticmethod + def uuid() -> str: + return '{b98fa052-4c74-4d25-96ce-5b95074fcecf}' + + @staticmethod + def group_name() -> str: + return 'Processing' + + @staticmethod + def sub_group_name() -> str: + return 'Normalization' + + @staticmethod + def human_label() -> str: + return 'Normalize Grayscale Values [Python]' + + @staticmethod + def version() -> str: + return '1.0.0' + + @staticmethod + def compiled_lib_name() -> str: + return 'Python' + + def setup_parameters(self) -> List[FilterParameter]: + # Create a DataArraySelectionFilterParameter Requirement Type that only takes the following: + # Image Geometry + # AttributeMatrix must be a CellType + # DataArray Component dimensions are 1, i.e., a scalar value + req = DataArraySelectionFilterParameter.RequirementType() + req.dcGeometryTypes = [IGeometry.Type.Image] + req.amTypes = [AttributeMatrix.Type.Cell] + # req.daTypes = ["float", "double", "int32_t"] + req.componentDimensions = [VectorSizeT([1])] + + return [ + DataArraySelectionFilterParameter('Input Data Array', 'selected_data_array_path', + self.selected_data_array_path, FilterParameter.Category.RequiredArray, + self._set_selected_data_array_path, self._get_selected_data_array_path, + req, -1), + ChoiceFilterParameter('Slice Axis', 'axes', self.axes, FilterParameter.Category.Parameter, self._set_axes, + self._get_axes, ["x", "y", "z"], False, -1), + DataArrayCreationFilterParameter('New Array', 'new_array', self.created_data_array_path, + FilterParameter.Category.CreatedArray, self._set_created_data_array_path, + self._get_created_data_array_path, + DataArrayCreationFilterParameter.RequirementType(), -1) + ] + + def data_check(self, dca: DataContainerArray, + status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[int, str]: + # Methodically check for each level of the selected data array path. + # Ensure the proper geometry is available + # Ensure the number of components of the input data array is 1 + # Error messages should give as much information as possible. users will rely on the + # error message to debug their pipelines, sometimes from the command line so printing + # out the DataArrayPath that was passed in is helpful for the user. + # Error Codes: Should start at a unique value somewhere between 10,000 and 100,000 and then just increment up from there + + dc: DataContainer = dca.getDataContainer(self.selected_data_array_path) + if dc is None: + return -61550, 'DataContainer does not exist: ' + self.selected_data_array_path.DataContainerName + + if not dc.Geometry: + return (-61551, 'Selected DataContainer has no geometry.') + + if dc.Geometry.getGeometryType() != IGeometry.Type.Image: + return (-61552, + 'This filter requires a DataContainer with an ImageGeometry. Selected DataContainer has ' + dc.Geometry.getGeometryTypeAsString()) + + am: AttributeMatrix = dca.getAttributeMatrix(self.selected_data_array_path) + if am is None: + return -61553, 'AttributeMatrix does not exist: ' + self.selected_data_array_path.AttributeMatrixName + + selected_data_array: IDataArray = am.getAttributeArray(self.selected_data_array_path.DataArrayName) + if selected_data_array is None: + return -61554, 'DataArray does not exist: ' + self.selected_data_array_path.DataArrayName + + if selected_data_array.getNumberOfComponents() != 1: + return -61555, 'Selected DataArray must have a single component: ' + str( + selected_data_array.getNumberOfComponents()) + + # If we are creating an array, what other options are there, then we need to create the array (but not allocate the memory during preflight) + # using the createNonPrereqArrayFromPath should do this for us. + arr_dtype = selected_data_array.dtype + if arr_dtype == "int8": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int8, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "uint8": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt8, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "int16": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int16, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "uint16": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt16, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "int32": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int32, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "uint32": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt32, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "int64": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Int64, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "uint64": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.UInt64, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "float32": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Float, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "float64": + dca.createNonPrereqArrayFromPath(status_delegate, NumericTypes.Double, self.created_data_array_path, 0, + VectorSizeT([1])) + elif arr_dtype == "bool": + return -61556, 'Bool type arrays are not supported as inputs to this algorithm' + + return (0, 'Success') + + def _execute_impl(self, dca: DataContainerArray, + status_delegate: Union[FilterDelegateCpp, FilterDelegatePy] = FilterDelegatePy()) -> Tuple[ + int, str]: + + dc: DataContainer = dca.getDataContainer(self.selected_data_array_path) + am: AttributeMatrix = dc.getAttributeMatrix(self.selected_data_array_path) + selected_data_array: IDataArray = am.getAttributeArray(self.selected_data_array_path) + created_data_array: IDataArray = am.getAttributeArray(self.created_data_array_path) + status_delegate.notifyStatusMessage(f" Selected D3D data array is {selected_data_array}") + + # Note that the dimensions come back as X, Y, Z (Fastest to slowest) so we will need to invert the order if using them in a numpy array + # Note however that the data is actually stored in the SIMPL DataArray in the correct order + imageGeomDims: SizeVec3 = dc.Geometry.getDimensions() + shape = [np.int64(imageGeomDims[2]), np.int64(imageGeomDims[1]), np.int64(imageGeomDims[0])] + + npv_selected_data = selected_data_array.npview() + npv_selected_data = np.reshape(npv_selected_data, shape) + selected_data_dtype = npv_selected_data.dtype + + # Get a numpy view into the data and then reshape the numpy array + npv_created_data_array = created_data_array.npview() + npv_created_data_array = np.reshape(npv_created_data_array, shape) + + # Get the average of the entire data set + arr_avg = np.average(npv_selected_data) + slice_axis = self.axes + + # slices are in x-axis + if slice_axis == 0: + layer_avg = np.average(npv_selected_data, axis=(0, 1)) + correction_factor = layer_avg / arr_avg + numpy_created_data_array = npv_selected_data / correction_factor[None, None, :] # Makes a copy + + # slices are in y-axis + elif slice_axis == 1: + layer_avg = np.average(npv_selected_data, axis=(0, 2)) + correction_factor = layer_avg / arr_avg + numpy_created_data_array = npv_selected_data / correction_factor[None, :, None] # Makes a copy + + # slices are in z-axis + elif slice_axis == 2: + layer_avg = np.average(npv_selected_data, axis=(1, 2)) + correction_factor = layer_avg / arr_avg + numpy_created_data_array = npv_selected_data / correction_factor[:, None, None] # Makes a copy + + # Assigns the data from the numpy array numpy_created_data_array to the view into the SIMPL.DataArray + # through a copy operation. + # https://numpy.org/doc/stable/user/basics.indexing.html#assigning-values-to-indexed-arrays + npv_created_data_array[...] = numpy_created_data_array + + return (0, 'Success') filters = [NormalizeGrayscale]