-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
FILTER: ITK Projection Filters #1162
FILTER: ITK Projection Filters #1162
Conversation
I think this should allow for reuse of code between projection filters since they seem to be mostly identical. As for the the fixed output type, I don't think it should be an issue? I was able to define a helper structure to avoid having to define separate fixed output aliases. Functions: // uint8, int16, uint16, float32
using ITKProjectionSupportedOutputTypes = ArrayTypeOptions<false, false, true, true, true, false, false, false, false, true, false>;
template <class T>
struct FixedOutputTypeHelper
{
template <class PixelT>
using FilterOutputType = T;
};
template <class ArrayOptionsType>
struct RunITKProjectionDataCheckFunctor
{
template <class FixedOutputType>
auto operator()(const DataStructure& dataStructure, const DataPath& selectedInputArray, const DataPath& imageGeomPath, const DataPath& outputArrayPath) const
{
return ITK::DataCheck<ArrayOptionsType, typename FixedOutputTypeHelper<FixedOutputType>::FilterOutputType>(dataStructure, selectedInputArray, imageGeomPath, outputArrayPath);
}
};
template <class ArrayOptionsType, class ITKFunctorType>
struct RunITKProjectionExecuteFunctor
{
template <class FixedOutputType>
auto operator()(DataStructure& dataStructure, const DataPath& selectedInputArray, const DataPath& imageGeomPath, const DataPath& outputArrayPath, ITKFunctorType&& itkFunctor,
const std::atomic_bool& shouldCancel) const
{
return ITK::Execute<ArrayOptionsType, typename FixedOutputTypeHelper<FixedOutputType>::FilterOutputType>(dataStructure, selectedInputArray, imageGeomPath, outputArrayPath, itkFunctor,
shouldCancel);
}
};
template <class ArrayTypeOptions, class FuncT, class FallbackFuncT, class... ArgsT>
auto RunTemplateFunctor(FuncT&& func, FallbackFuncT&& fallback, DataType dataType, ArgsT&&... args)
{
if constexpr(ArrayTypeOptions::UsingBoolean)
{
if(dataType == DataType::boolean)
{
return func.template operator()<bool>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingInt8)
{
if(dataType == DataType::int8)
{
return func.template operator()<int8>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingInt16)
{
if(dataType == DataType::int16)
{
return func.template operator()<int16>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingInt32)
{
if(dataType == DataType::int32)
{
return func.template operator()<int32>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingInt64)
{
if(dataType == DataType::int64)
{
return func.template operator()<int64>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingUInt8)
{
if(dataType == DataType::uint8)
{
return func.template operator()<uint8>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingUInt16)
{
if(dataType == DataType::uint16)
{
return func.template operator()<uint16>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingUInt32)
{
if(dataType == DataType::uint32)
{
return func.template operator()<uint32>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingUInt64)
{
if(dataType == DataType::uint64)
{
return func.template operator()<uint64>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingFloat32)
{
if(dataType == DataType::float32)
{
return func.template operator()<float32>(std::forward<ArgsT>(args)...);
}
}
if constexpr(ArrayTypeOptions::UsingFloat64)
{
if(dataType == DataType::float64)
{
return func.template operator()<float64>(std::forward<ArgsT>(args)...);
}
}
return fallback(dataType);
}
template <class ArrayOptionsType>
IFilter::PreflightResult RunITKProjectionDataCheck(const DataStructure& dataStructure, const DataPath& selectedInputArray, const DataPath& imageGeomPath, const std::string& outputGeomName,
bool performInPlace, const std::string& outputArrayName)
{
DataPath outputArrayPath = selectedInputArray.replaceName(outputArrayName);
Result<OutputActions> resultOutputActions;
// The input geometry must be preserved, so we will just copy the needed array into newly created output geometry
if(!performInPlace)
{
DataPath outputGeomPath({outputGeomName});
const auto& originalGeometry = dataStructure.getDataRefAs<ImageGeom>(imageGeomPath);
// Make copy of input geometry
resultOutputActions.value().appendAction(std::make_unique<CreateImageGeometryAction>(
outputGeomPath, originalGeometry.getDimensions().toContainer<CreateImageGeometryAction::DimensionType>(), originalGeometry.getOrigin().toContainer<CreateImageGeometryAction::OriginType>(),
originalGeometry.getSpacing().toContainer<CreateImageGeometryAction::SpacingType>(), originalGeometry.getCellDataPath().getTargetName()));
outputArrayPath = outputGeomPath.createChildPath(originalGeometry.getCellDataPath().getTargetName()).createChildPath(outputArrayName);
}
auto fallbackFunc = [](DataType dataType) {
return MakeErrorResult<OutputActions>(-76590, fmt::format("Input {} type is not currently supported. Please reach out to devs if you have a use case.", DataTypeToString(dataType)));
};
DataType type = dataStructure.getDataRefAs<IDataArray>(selectedInputArray).getDataType();
Result<OutputActions> helperOutputActions = RunTemplateFunctor<ITKProjectionSupportedOutputTypes>(RunITKProjectionDataCheckFunctor<ArrayOptionsType>{}, fallbackFunc, type, dataStructure,
selectedInputArray, imageGeomPath, outputArrayPath);
if(helperOutputActions.invalid())
{
return {std::move(helperOutputActions)};
}
// Consolidate actions
resultOutputActions.value().actions.insert(resultOutputActions.value().actions.end(), helperOutputActions.value().actions.begin(), helperOutputActions.value().actions.end());
return {std::move(resultOutputActions)};
}
template <class ArrayOptionsType, class ITKFunctorType>
Result<> RunITKProjectionExecute(DataStructure& dataStructure, const DataPath& selectedInputArray, const DataPath& imageGeomPath, const std::atomic_bool& shouldCancel,
const std::string& outputArrayName, bool performInPlace, ITKFunctorType&& itkFunctor, const std::string& outputImageGeomName)
{
DataPath outputArrayPath = selectedInputArray.replaceName(outputArrayName);
DataPath finalImageGeomPath = imageGeomPath;
if(!performInPlace)
{
const auto& originalGeometry = dataStructure.getDataRefAs<ImageGeom>(imageGeomPath);
finalImageGeomPath = DataPath({outputImageGeomName});
outputArrayPath = finalImageGeomPath.createChildPath(originalGeometry.getCellDataPath().getTargetName()).createChildPath(outputArrayName);
}
DataType type = dataStructure.getDataRefAs<IDataArray>(selectedInputArray).getDataType();
auto fallbackFunc = [](DataType dataType) {
return MakeErrorResult(-76591, fmt::format("Input {} type is not currently supported. Please reach out to devs if you have a use case.", DataTypeToString(dataType)));
};
Result<> result = RunTemplateFunctor<ITKProjectionSupportedOutputTypes>(RunITKProjectionExecuteFunctor<ArrayOptionsType, ITKFunctorType>{}, fallbackFunc, type, dataStructure, selectedInputArray,
finalImageGeomPath, outputArrayPath, itkFunctor, shouldCancel);
if(result.invalid())
{
return result;
}
auto& imageGeom = dataStructure.getDataRefAs<ImageGeom>(finalImageGeomPath);
auto iArrayTupleShape = dataStructure.getDataAs<IArray>(outputArrayPath)->getTupleShape();
// Update the Image Geometry with the new dimensions
imageGeom.setDimensions({iArrayTupleShape[2], iArrayTupleShape[1], iArrayTupleShape[0]});
// Update the AttributeMatrix with the new tuple shape. THIS WILL ALSO CHANGE ANY OTHER DATA ARRAY THAT IS ALSO
// STORED IN THAT ATTRIBUTE MATRIX
dataStructure.getDataAs<AttributeMatrix>(outputArrayPath.getParent())->resizeTuples(iArrayTupleShape);
return {};
} Usage: //------------------------------------------------------------------------------
IFilter::PreflightResult ITKMedianProjectionImageFilter::preflightImpl(const DataStructure& dataStructure, const Arguments& filterArgs, const MessageHandler& messageHandler,
const std::atomic_bool& shouldCancel) const
{
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 projectionDimension = filterArgs.value<uint32>(k_ProjectionDimension_Key);
auto performInPlace = filterArgs.value<bool>(k_RemoveOriginalGeometry_Key);
auto outputGeomName = filterArgs.value<std::string>(k_OutputImageGeomName_Key);
return RunITKProjectionDataCheck<cxITKMedianProjectionImageFilter::ArrayOptionsType>(dataStructure, selectedInputArray, imageGeomPath, outputGeomName, performInPlace, outputArrayName);
}
//------------------------------------------------------------------------------
Result<> ITKMedianProjectionImageFilter::executeImpl(DataStructure& dataStructure, const Arguments& filterArgs, const PipelineFilter* pipelineNode, const MessageHandler& messageHandler,
const std::atomic_bool& shouldCancel) const
{
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 performInPlace = filterArgs.value<bool>(k_RemoveOriginalGeometry_Key);
auto outputImageGeomName = filterArgs.value<std::string>(k_OutputImageGeomName_Key);
auto projectionDimension = filterArgs.value<uint32>(k_ProjectionDimension_Key);
const cxITKMedianProjectionImageFilter::ITKMedianProjectionImageFilterFunctor itkFunctor = {projectionDimension};
return RunITKProjectionExecute<cxITKMedianProjectionImageFilter::ArrayOptionsType>(dataStructure, selectedInputArray, imageGeomPath, shouldCancel, outputArrayName, performInPlace, itkFunctor,
outputImageGeomName);
} |
eb345e3
to
85c9d6e
Compare
…angleGeometrySizes (BlueQuartzSoftware#1134) * BUG: Fixes issue where any component shape were allowed for ComputeTriangleGeometrySizes.cpp * Fix all instances of "Face Labels" are only allowed to use 2 component input arrays * ComputeTriangleGeomVolumes - fix possible walking off the end of the array. A negative value for the face label when used as an index will cast into an unsigned value and bad things are going to happen. * BUG: Fixes negative volumes produced from calculations --------- Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net>
…efault) Done by creating a new resized geom with only projected data in it
6cea7f5
to
31c1dee
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are ITKBinaryProjectionFilter
and ITKMeanProjectionFilter
able to use the ProjectionUtils
functions?
@JDuffeyBQ No, ITKMeans is a fixed output type of double which doesn't work with the other options, so it would need further templating to allow for its use with the util functions. Binary projection is much of the same in that it doesn't need a defined Filter output type so it works with much of the existing ITK interface outside of the new util file. They are similar in preflight and execute in handling the geometries themselves but the typing handling needed by the other projections are unique in these. |
* Maximum, Median, Minimum, Mean Projection Filters * Update documentation, test cases, extraneous code removal * Make Filters conform to the Binary Projection precedent * Added functionality to preserve integrity of input geometry (off by default) Done by creating a new resized geom with only projected data in it * Limit typing and strip RGB/ARGB support --------- Signed-off-by: Michael Jackson <mike.jackson@bluequartz.net> Co-authored-by: Michael Jackson <mike.jackson@bluequartz.net>
Naming Conventions
Naming of variables should descriptive where needed. Loop Control Variables can use
i
if warranted. Most of these conventions are enforced through the clang-tidy and clang-format configuration files. See the filesimplnx/docs/Code_Style_Guide.md
for a more in depth explanation.Filter Checklist
The help file
simplnx/docs/Porting_Filters.md
has documentation to help you port or write new filters. At the top is a nice checklist of items that should be noted when porting a filter.Unit Testing
The idea of unit testing is to test the filter for proper execution and error handling. How many variations on a unit test each filter needs is entirely dependent on what the filter is doing. Generally, the variations can fall into a few categories:
Code Cleanup