Skip to content

Commit

Permalink
Unit Test cleanup
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson committed Apr 29, 2024
1 parent 04a028a commit 78da350
Show file tree
Hide file tree
Showing 28 changed files with 435 additions and 241 deletions.
Original file line number Diff line number Diff line change
@@ -1,35 +1,107 @@
#include "ITKMaskImageFilter.hpp"

#include "ITKImageProcessing/Common/ITKArrayHelper.hpp"
#include "ITKImageProcessing/Common/sitkCommon.hpp"

#include "simplnx/Parameters/ArrayCreationParameter.hpp"
#include "simplnx/Parameters/ArraySelectionParameter.hpp"
#include "simplnx/Parameters/DataObjectNameParameter.hpp"
#include "simplnx/Parameters/GeometrySelectionParameter.hpp"
#include "simplnx/Parameters/NumberParameter.hpp"

#include <itkCastImageFilter.h>
#include <itkMaskImageFilter.h>

#include <numeric>

#include "simplnx/Utilities/SIMPLConversion.hpp"

#include <itkMaskImageFilter.h>
#include <stdexcept>

using namespace nx::core;

namespace cxITKMaskImageFilter
{
using ArrayOptionsType = ITK::ScalarVectorPixelIdTypeList;
template <uint32 Dimension>
using MaskImageT = itk::Image<uint32, Dimension>;

template <uint32 Dimension, class T>
typename MaskImageT<Dimension>::Pointer CastDataStoreToUInt32Image(DataStore<T>& dataStore, const ImageGeom& imageGeom)
{
static_assert(std::is_same_v<T, uint8> || std::is_same_v<T, uint16> || std::is_same_v<T, uint32>);

using InputImageT = itk::Image<T, Dimension>;
using OutputImageT = MaskImageT<Dimension>;
using CastFilterT = itk::CastImageFilter<InputImageT, OutputImageT>;

auto inputImage = ITK::WrapDataStoreInImage<T, Dimension>(dataStore, imageGeom);

auto castFilter = CastFilterT::New();
castFilter->SetInput(inputImage);
castFilter->Update();

typename OutputImageT::Pointer outputImage = castFilter->GetOutput();
return outputImage;
}

template <uint32 Dimension>
typename MaskImageT<Dimension>::Pointer CastIDataStoreToUInt32Image(IDataStore& dataStore, const ImageGeom& imageGeom)
{
DataType dataType = dataStore.getDataType();
switch(dataType)
{
case DataType::uint8: {
auto& typedDataStore = dynamic_cast<DataStore<uint8>&>(dataStore);
return CastDataStoreToUInt32Image<Dimension>(typedDataStore, imageGeom);
}
case DataType::uint16: {
auto& typedDataStore = dynamic_cast<DataStore<uint16>&>(dataStore);
return CastDataStoreToUInt32Image<Dimension>(typedDataStore, imageGeom);
}
case DataType::uint32: {
auto& typedDataStore = dynamic_cast<DataStore<uint32>&>(dataStore);
return CastDataStoreToUInt32Image<Dimension>(typedDataStore, imageGeom);
}
default: {
throw std::runtime_error("Unsupported mask image type");
}
}
}

template <class InputPixelT, class OutputPixelT>
OutputPixelT MakeOutsideValue(float64 value)
{
std::vector<usize> cDims = ITK::GetComponentDimensions<InputPixelT>();
usize numComponents = std::accumulate(cDims.cbegin(), cDims.cend(), static_cast<usize>(1), std::multiplies<>());
OutputPixelT outsideValue{};
itk::NumericTraits<OutputPixelT>::SetLength(outsideValue, numComponents);
outsideValue = static_cast<OutputPixelT>(value);
return outsideValue;
}

using ArrayOptionsT = ITK::ScalarVectorPixelIdTypeList;

struct ITKMaskImageFunctor
struct ITKMaskImageFilterFunctor
{
float64 outsideValue = 0;
float64 maskingValue = 0;
const ImageGeom& imageGeom;
IDataStore& maskDataStore;

template <class InputImageT, class OutputImageT, uint32 Dimension>
auto createFilter() const
{
using FilterType = itk::MaskImageFilter<InputImageT, OutputImageT>;
auto filter = FilterType::New();
filter->SetOutsideValue(outsideValue);
filter->SetMaskingValue(maskingValue);
using MaskImageType = MaskImageT<Dimension>;
using FilterT = itk::MaskImageFilter<InputImageT, MaskImageType, OutputImageT>;
using InputPixelT = typename InputImageT::ValueType;
using OutsideValueT = typename OutputImageT::PixelType;

typename MaskImageType::Pointer maskImage = CastIDataStoreToUInt32Image<Dimension>(maskDataStore, imageGeom);

OutsideValueT trueOutsideValue = MakeOutsideValue<InputPixelT, OutsideValueT>(outsideValue);

auto filter = FilterT::New();
filter->SetOutsideValue(trueOutsideValue);
filter->SetMaskImage(maskImage);

return filter;
}
};
Expand Down Expand Up @@ -64,24 +136,26 @@ std::string ITKMaskImageFilter::humanName() const
//------------------------------------------------------------------------------
std::vector<std::string> ITKMaskImageFilter::defaultTags() const
{
return {className(), "ITKImageProcessing", "ITKMaskImage", "ITKImageIntensity", "ImageIntensity"};
return {className(), "ITKImageProcessing", "ITKMaskImageFilter", "ITKImageIntensity", "ImageIntensity"};
}

//------------------------------------------------------------------------------
Parameters ITKMaskImageFilter::parameters() const
{
Parameters params;

params.insertSeparator(Parameters::Separator{"Input Parameters"});
params.insert(std::make_unique<Float64Parameter>(k_OutsideValue_Key, "OutsideValue", "Method to explicitly set the outside value of the mask. Defaults to 0", 0));
params.insert(std::make_unique<Float64Parameter>(k_MaskingValue_Key, "MaskingValue", "Method to explicitly set the masking value of the mask. Defaults to 0", 0));
params.insert(std::make_unique<Float64Parameter>(k_OutsideValue_Key, "Outside Value", "Method to explicitly set the outside value of the mask.", 0));

params.insertSeparator(Parameters::Separator{"Required Input Cell Data"});
params.insert(std::make_unique<GeometrySelectionParameter>(k_InputImageGeomPath_Key, "Image Geometry", "Select the Image Geometry Group from the DataStructure.", DataPath({"Image Geometry"}),
params.insertSeparator(Parameters::Separator{"Required Data Objects"});
params.insert(std::make_unique<GeometrySelectionParameter>(k_InputImageGeomPath_Key, "Image Geometry", "Select the Image Geometry Group from the DataStructure.", DataPath{},
GeometrySelectionParameter::AllowedTypes{IGeometry::Type::Image}));
params.insert(std::make_unique<ArraySelectionParameter>(k_InputImageDataPath_Key, "Input Image Data Array", "The image data that will be processed by this filter.", DataPath{},
nx::core::ITK::GetNonLabelPixelAllowedTypes()));
params.insert(
std::make_unique<ArraySelectionParameter>(k_InputImageDataPath_Key, "Input Image Data Array", "The image data that will be processed by this filter.", DataPath{}, nx::core::GetAllDataTypes()));
params.insert(std::make_unique<ArraySelectionParameter>(k_MaskImageDataPath_Key, "MaskImage", "The path to the image data to be used as the mask (should be the same size as the input image)",
DataPath{}, nx::core::GetAllDataTypes()));

params.insertSeparator(Parameters::Separator{"Created Cell Data"});
params.insertSeparator(Parameters::Separator{"Created Data Objects"});
params.insert(std::make_unique<DataObjectNameParameter>(k_OutputImageArrayName_Key, "Output Image Array Name",
"The result of the processing will be stored in this Data Array inside the same group as the input data.", "Output Image Data"));

Expand All @@ -101,11 +175,18 @@ IFilter::PreflightResult ITKMaskImageFilter::preflightImpl(const DataStructure&
auto imageGeomPath = filterArgs.value<DataPath>(k_InputImageGeomPath_Key);
auto selectedInputArray = filterArgs.value<DataPath>(k_InputImageDataPath_Key);
auto outputArrayName = filterArgs.value<DataObjectNameParameter::ValueType>(k_OutputImageArrayName_Key);
auto outsideValue = filterArgs.value<float64>(k_OutsideValue_Key);
auto maskingValue = filterArgs.value<float64>(k_MaskingValue_Key);
const DataPath outputArrayPath = selectedInputArray.replaceName(outputArrayName);
auto outsideValue = filterArgs.value<float64>(k_OutsideValue_Key);
auto maskArrayPath = filterArgs.value<DataPath>(k_MaskImageDataPath_Key);

// Once ArraySelectionParameter allows for restricting the type, this check can be removed
Result<> result = ITK::CheckImageType({DataType::uint8, DataType::uint16, DataType::uint32}, dataStructure, maskArrayPath);
if(result.invalid())
{
return {ConvertResultTo<OutputActions>(std::move(result), {})};
}

Result<OutputActions> resultOutputActions = ITK::DataCheck<cxITKMaskImageFilter::ArrayOptionsType>(dataStructure, selectedInputArray, imageGeomPath, outputArrayPath);
Result<OutputActions> resultOutputActions = ITK::DataCheck<cxITKMaskImageFilter::ArrayOptionsT>(dataStructure, selectedInputArray, imageGeomPath, outputArrayPath);

return {std::move(resultOutputActions)};
}
Expand All @@ -118,16 +199,18 @@ Result<> ITKMaskImageFilter::executeImpl(DataStructure& dataStructure, const Arg
auto selectedInputArray = filterArgs.value<DataPath>(k_InputImageDataPath_Key);
auto outputArrayName = filterArgs.value<DataObjectNameParameter::ValueType>(k_OutputImageArrayName_Key);
const DataPath outputArrayPath = selectedInputArray.replaceName(outputArrayName);

auto outsideValue = filterArgs.value<float64>(k_OutsideValue_Key);
auto maskingValue = filterArgs.value<float64>(k_MaskingValue_Key);
auto maskArrayPath = filterArgs.value<DataPath>(k_MaskImageDataPath_Key);

const cxITKMaskImageFilter::ITKMaskImageFunctor itkFunctor = {outsideValue, maskingValue};
ImageGeom& imageGeom = dataStructure.getDataRefAs<ImageGeom>(imageGeomPath);
IDataArray& maskArray = dataStructure.getDataRefAs<IDataArray>(maskArrayPath);
IDataStore& maskStore = maskArray.getIDataStoreRef();

auto& imageGeom = dataStructure.getDataRefAs<ImageGeom>(imageGeomPath);
cxITKMaskImageFilter::ITKMaskImageFilterFunctor itkFunctor = {outsideValue, imageGeom, maskStore};

return ITK::Execute<cxITKMaskImageFilter::ArrayOptionsType>(dataStructure, selectedInputArray, imageGeomPath, outputArrayPath, itkFunctor, shouldCancel);
return ITK::Execute<cxITKMaskImageFilter::ArrayOptionsT>(dataStructure, selectedInputArray, imageGeomPath, outputArrayPath, itkFunctor, shouldCancel);
}
} // namespace nx::core

namespace
{
Expand All @@ -149,12 +232,10 @@ Result<Arguments> ITKMaskImageFilter::FromSIMPLJson(const nlohmann::json& json)
results.push_back(SIMPLConversion::ConvertParameter<SIMPLConversion::DoubleFilterParameterConverter>(args, json, SIMPL::k_OutsideValueKey, k_OutsideValue_Key));
results.push_back(SIMPLConversion::ConvertParameter<SIMPLConversion::DataContainerSelectionFilterParameterConverter>(args, json, SIMPL::k_SelectedCellArrayPathKey, k_InputImageGeomPath_Key));
results.push_back(SIMPLConversion::ConvertParameter<SIMPLConversion::DataArraySelectionFilterParameterConverter>(args, json, SIMPL::k_SelectedCellArrayPathKey, k_InputImageDataPath_Key));
// results.push_back(SIMPLConversion::ConvertParameter<SIMPLConversion::DataArraySelectionFilterParameterConverter>(args, json, SIMPL::k_MaskCellArrayPathKey, k_MaskImageDataPath_Key));
results.push_back(SIMPLConversion::ConvertParameter<SIMPLConversion::DataArraySelectionFilterParameterConverter>(args, json, SIMPL::k_MaskCellArrayPathKey, k_MaskImageDataPath_Key));
results.push_back(SIMPLConversion::ConvertParameter<SIMPLConversion::StringFilterParameterConverter>(args, json, SIMPL::k_NewCellArrayNameKey, k_OutputImageArrayName_Key));

Result<> conversionResult = MergeResults(std::move(results));

return ConvertResultTo<Arguments>(std::move(conversionResult), std::move(args));
}

} // namespace nx::core
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,7 @@ class ITKIMAGEPROCESSING_EXPORT ITKMaskImageFilter : public IFilter
static inline constexpr StringLiteral k_InputImageDataPath_Key = "input_image_data_path";
static inline constexpr StringLiteral k_OutputImageArrayName_Key = "output_array_name";
static inline constexpr StringLiteral k_OutsideValue_Key = "outside_value";
static inline constexpr StringLiteral k_MaskingValue_Key = "masking_value";
static inline constexpr StringLiteral k_ImageDataPath_Key = "image_data_path";
static inline constexpr StringLiteral k_MaskImage_Key = "mask_image";
static inline constexpr StringLiteral k_MaskImageDataPath_Key = "mask_image_data_path";

/**
* @brief Reads SIMPL json and converts it simplnx Arguments.
Expand Down Expand Up @@ -131,9 +129,8 @@ class ITKIMAGEPROCESSING_EXPORT ITKMaskImageFilter : public IFilter
* @param shouldCancel Boolean that gets set if the filter should stop executing and return
* @return Returns a Result object with error or warning values if any of those occurred during execution of this function
*/
Result<> executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler,
const std::atomic_bool& shouldCancel) const override;
Result<> executeImpl(DataStructure& data, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler, const std::atomic_bool& shouldCancel) const override;
};
} // namespace nx::core

SIMPLNX_DEF_FILTER_TRAITS(nx::core, ITKMaskImageFilter, "916ffd00-25db-4293-826a-540e859ab2cb");
SIMPLNX_DEF_FILTER_TRAITS(nx::core, ITKMaskImageFilter, "d3138266-3f34-4d6e-8e21-904c94351293");
Loading

0 comments on commit 78da350

Please sign in to comment.