Skip to content

Commit

Permalink
FILTER: ITK Projection Filters (BlueQuartzSoftware#1162)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
2 people authored and mmarineBlueQuartz committed Feb 4, 2025
1 parent 95019fe commit a2a09b2
Show file tree
Hide file tree
Showing 24 changed files with 2,002 additions and 58 deletions.
9 changes: 5 additions & 4 deletions src/Plugins/ITKImageProcessing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,9 @@ if(NOT ITKIMAGEPROCESSING_LEAN_AND_MEAN)
# ITKBinaryClosingByReconstructionImage
# ITKBinomialBlurImage
# ITKLaplacianSharpeningImage
# ITKMaximumProjectionImage
# ITKMedianProjectionImage
# ITKMinimumProjectionImage
ITKMaximumProjectionImageFilter
ITKMedianProjectionImageFilter
ITKMinimumProjectionImageFilter
# ITKMultiScaleHessianBasedObjectnessImage
# ITKSaltAndPepperNoiseImage
# ITKShiftScaleImage
Expand All @@ -182,7 +182,7 @@ if(NOT ITKIMAGEPROCESSING_LEAN_AND_MEAN)
# -----------------------------------------------------------------------------
# ITKBoundedReciprocalImage
# ITKNormalizeToConstantImage
# ITKMeanProjectionImage
ITKMeanProjectionImageFilter
# ITKStandardDeviationProjectionImage
# ITKSumProjectionImage
# ITKMorphologicalWatershedFromMarkersImage
Expand Down Expand Up @@ -265,6 +265,7 @@ set(${PLUGIN_NAME}_Common_Srcs
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Common/ITKArrayHelper.cpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Common/ITKProgressObserver.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Common/ITKDream3DFilterInterruption.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Common/ProjectionUtils.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Common/ReadImageUtils.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Common/ReadImageUtils.cpp
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# ITK Maximum Projection Image Filter (ITKMaximumProjectionImage)

Maximum projection.

## Group (Subgroup)

ITKImageStatistics (ImageStatistics)

## Description

This class was contributed to the Insight Journal by Gaetan Lehmann. The original paper can be found at https://www.insight-journal.org/browse/publication/71

## Author

- Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France.

## See Also

- [ProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1ProjectionImageFilter.html)

- [MedianProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MedianProjectionImageFilter.html)

- [MinimumProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MinimumProjectionImageFilter.html)

- [StandardDeviationProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1StandardDeviationProjectionImageFilter.html)

- [SumProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1SumProjectionImageFilter.html)

- [BinaryProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1BinaryProjectionImageFilter.html)

- [MeanProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MeanProjectionImageFilter.html)

% Auto generated parameter table will be inserted here

## Example Pipelines

## License & Copyright

Please see the description file distributed with this plugin.

## DREAM3D-NX Help

If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues/discussions) GitHub site where the community of DREAM3D-NX users can help answer your questions.
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ This class was contributed to the Insight Journal by Gaetan Lehmann. The origina

Please see the description file distributed with this plugin.

## DREAM3D Mailing Lists
## DREAM3D-NX Help

If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues/discussions) GitHub site where the community of DREAM3D-NX users can help answer your questions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# ITK Median Projection Image Filter (ITKMedianProjectionImage)

Median projection.

## Group (Subgroup)

ITKImageStatistics (ImageStatistics)

## Description

This class was contributed to the Insight Journal by Gaetan Lehmann. The original paper can be found at https://www.insight-journal.org/browse/publication/71

## Author

- Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France.

## See Also

- [ProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1ProjectionImageFilter.html)

- [MeanProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MeanProjectionImageFilter.html)

- [MinimumProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MinimumProjectionImageFilter.html)

- [StandardDeviationProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1StandardDeviationProjectionImageFilter.html)

- [SumProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1SumProjectionImageFilter.html)

- [BinaryProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1BinaryProjectionImageFilter.html)

- [MaximumProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MaximumProjectionImageFilter.html)

% Auto generated parameter table will be inserted here

## Example Pipelines

## License & Copyright

Please see the description file distributed with this plugin.

## DREAM3D-NX Help

If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues/discussions) GitHub site where the community of DREAM3D-NX users can help answer your questions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# ITK Minimum Projection Image Filter (ITKMinimumProjectionImage)

Minimum projection.

## Group (Subgroup)

ITKImageStatistics (ImageStatistics)

## Description

This class was contributed to the Insight Journal by Gaetan Lehmann. The original paper can be found at https://www.insight-journal.org/browse/publication/71

## Author

- Gaetan Lehmann. Biologie du Developpement et de la Reproduction, INRA de Jouy-en-Josas, France.

## See Also

- [ProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1ProjectionImageFilter.html)

- [MedianProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MedianProjectionImageFilter.html)

- [MeanProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MeanProjectionImageFilter.html)

- [StandardDeviationProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1StandardDeviationProjectionImageFilter.html)

- [SumProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1SumProjectionImageFilter.html)

- [BinaryProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1BinaryProjectionImageFilter.html)

- [MaximumProjectionImageFilter](https://itk.org/Doxygen/html/classitk_1_1MaximumProjectionImageFilter.html)

% Auto generated parameter table will be inserted here

## Example Pipelines

## License & Copyright

Please see the description file distributed with this plugin.

## DREAM3D-NX Help

If you need help, need to file a bug report or want to request a new feature, please head over to the [DREAM3DNX-Issues](https://github.com/BlueQuartzSoftware/DREAM3DNX-Issues/discussions) GitHub site where the community of DREAM3D-NX users can help answer your questions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
#pragma once

#include "simplnx/Common/Result.hpp"
#include "simplnx/Common/Types.hpp"
#include "simplnx/Filter/Actions/CreateImageGeometryAction.hpp"
#include "simplnx/Filter/IFilter.hpp"

#include "ITKArrayHelper.hpp"

using namespace nx::core;

namespace ProjectionUtilities
{
// 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, FixedOutputTypeHelper<FixedOutputType>::template 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, FixedOutputTypeHelper<FixedOutputType>::template 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 {};
}
} // namespace ProjectionUtilities
Loading

0 comments on commit a2a09b2

Please sign in to comment.