Skip to content
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

ENH: Import Image Convert Type #1036

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,20 @@

using namespace nx::core;

DataType ITK::detail::ConvertChoiceToDataType(types::usize choice)
{
switch(choice)
{
case 0:
return DataType::uint8;
case 1:
return DataType::uint16;
case 2:
return DataType::uint32;
}
return DataType::uint8;
}

bool ITK::DoTuplesMatch(const IDataStore& dataStore, const ImageGeom& imageGeom)
{
return imageGeom.getNumberOfCells() == dataStore.getNumberOfTuples();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,47 @@
#include <type_traits>
#include <vector>

namespace nx::core
namespace nx::core::ITK
{
namespace ITK
namespace detail
{
template <typename OriginT>
struct TypeConversionValidateFunctor
{
template <typename DestT>
bool operator()()
{
if constexpr(std::is_same_v<OriginT, DestT>)
{
// This won't actually fail, but it prevents an unnecessary copy
return false;
}
if constexpr(std::is_signed_v<OriginT> || std::is_signed_v<DestT>)
{
return false;
}
if constexpr(std::is_same_v<OriginT, bool> || std::is_same_v<DestT, bool>)
{
return false;
}

// Currently only works on uint8, uint16, or uint32
return true;
}
};

struct PreflightTypeConversionValidateFunctor
{
template <typename OriginT>
bool operator()(const DataType& destType)
{
return ExecuteNeighborFunction(TypeConversionValidateFunctor<OriginT>{}, destType);
}
};

DataType ConvertChoiceToDataType(usize choice);
} // namespace detail

struct ImageGeomData
{
SizeVec3 dims{};
Expand Down Expand Up @@ -346,6 +383,58 @@
return dataStore;
}

template <typename T>
concept NotBoolT = !std::is_same_v<T, bool>;

template <NotBoolT NewStoreT, class PixelT, uint32 Dimension>
Result<> ConvertImageToDataStore(DataStore<NewStoreT>& dataStore, itk::Image<PixelT, Dimension>& image)
{
using ImageType = itk::Image<PixelT, Dimension>;
using T = UnderlyingType_t<PixelT>;
typename ImageType::SizeType imageSize = image.GetLargestPossibleRegion().GetSize();
std::vector<usize> tDims(imageSize.rbegin(), imageSize.rend());
std::vector<usize> cDims = GetComponentDimensions<PixelT>();
if constexpr(Dimension == 2)
{
tDims.insert(tDims.begin(), 1);
}
typename ImageType::PixelContainer* pixelContainer = image.GetPixelContainer();

// ITK use the global new allocator
auto* rawBufferPtr = reinterpret_cast<T*>(pixelContainer->GetBufferPointer());
pixelContainer->ContainerManageMemoryOff();
if constexpr(std::is_same_v<NewStoreT, T>)
{
std::copy(rawBufferPtr, rawBufferPtr + pixelContainer->Size(), dataStore.data());
}
else
{
if constexpr(!std::is_signed_v<NewStoreT> && !std::is_signed_v<T>) // bool would slip through, but it is disallowed
{
constexpr auto destMaxV = static_cast<float64>(std::numeric_limits<NewStoreT>::max());
constexpr auto originMaxV = std::numeric_limits<T>::max();
std::transform(rawBufferPtr, rawBufferPtr + pixelContainer->Size(), dataStore.data(), [](auto value) {
float64 ratio = static_cast<float64>(value) / originMaxV;

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]

Check warning on line 417 in src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ITKArrayHelper.hpp

View workflow job for this annotation

GitHub Actions / build (ubuntu-22.04, clang++-14)

implicit conversion from 'const unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616 [-Wimplicit-const-int-float-conversion]
return static_cast<NewStoreT>(ratio * destMaxV);
});
}
}

return {};
}

struct ConvertImageToDatastoreFunctor
{
template <typename T, class... Args>
Result<> operator()(DataStructure& dataStructure, const DataPath& arrayPath, Args&&... args)
{
auto& dataArray = dataStructure.getDataRefAs<DataArray<T>>(arrayPath);
DataStore<T>& dataStore = dataArray.template getIDataStoreRefAs<DataStore<T>>();

return ConvertImageToDataStore(dataStore, std::forward<Args>(args)...);
}
};

// Could replace with class type non-type template parameters in C++20

template <bool UseScalarV, bool UseVectorV, bool UseRgbRgbaV>
Expand Down Expand Up @@ -827,5 +916,4 @@
return MakeErrorResult<ResultT>(-222, exception.GetDescription());
}
}
} // namespace ITK
} // namespace nx::core
} // namespace nx::core::ITK
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#include "ReadImageUtils.hpp"

#include "simplnx/Common/TypesUtility.hpp"
#include "simplnx/Filter/Actions/DeleteDataAction.hpp"

using namespace nx::core;

namespace cxItkImageReaderFilter
{

Expand Down Expand Up @@ -74,8 +79,16 @@ Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath i

actions.appendAction(std::make_unique<CreateImageGeometryAction>(std::move(imageGeomPath), std::move(dims), origin.toContainer<CreateImageGeometryAction::OriginType>(),
spacing.toContainer<CreateImageGeometryAction::SpacingType>(), cellDataName));
actions.appendAction(std::make_unique<CreateArrayAction>(*numericType, std::move(arrayDims), std::move(cDims), imageGeomPath.createChildPath(cellDataName).createChildPath(arrayName)));

if(imageReaderOptions.ChangeDataType && ExecuteNeighborFunction(ITK::detail::PreflightTypeConversionValidateFunctor{}, *numericType, imageReaderOptions.ImageDataType))
{
actions.appendAction(
std::make_unique<CreateArrayAction>(imageReaderOptions.ImageDataType, std::move(arrayDims), std::move(cDims), imageGeomPath.createChildPath(cellDataName).createChildPath(arrayName)));
}
else
{
actions.appendAction(std::make_unique<CreateArrayAction>(*numericType, std::move(arrayDims), std::move(cDims), imageGeomPath.createChildPath(cellDataName).createChildPath(arrayName)));
}
} catch(const itk::ExceptionObject& err)
{
return MakeErrorResult<OutputActions>(-55557, fmt::format("ITK exception was thrown while processing input file: {}", err.what()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "simplnx/Common/Result.hpp"
#include "simplnx/Common/Types.hpp"
#include "simplnx/Filter/Actions/CreateImageGeometryAction.hpp"
#include "simplnx/Utilities/FilterUtilities.hpp"

#include "ITKImageProcessing/Common/ITKArrayHelper.hpp"

Expand Down Expand Up @@ -49,6 +50,29 @@ struct ReadImageIntoArrayFunctor

return {};
}

//------------------------------------------------------------------------------
template <class PixelT, uint32 Dimension>
Result<> operator()(DataStructure& dataStructure, const std::string& filePath, const DataPath& arrayPath, const DataType& dataType) const
{
using ImageType = itk::Image<PixelT, Dimension>;
using ReaderType = itk::ImageFileReader<ImageType>;

if(!ExecuteNeighborFunction(ITK::detail::TypeConversionValidateFunctor<typename itk::NumericTraits<PixelT>::ValueType>{}, dataType))
{
// Not valid for conversion executing overload
return operator()<PixelT, Dimension>(dataStructure, arrayPath, filePath);
}

typename ReaderType::Pointer reader = ReaderType::New();
reader->SetFileName(filePath);

reader->Update();
typename ImageType::Pointer outputImage = reader->GetOutput();

return ExecuteNeighborFunction(ITK::ConvertImageToDatastoreFunctor{}, dataType, dataStructure, arrayPath, *outputImage);
;
}
};

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -174,6 +198,8 @@ struct ImageReaderOptions
bool OverrideSpacing = false;
FloatVec3 Origin;
FloatVec3 Spacing;
bool ChangeDataType = false;
DataType ImageDataType = DataType::uint8;
};

//------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#include "ITKImportFijiMontage.hpp"

#include "ITKImageProcessing/Common/ITKArrayHelper.hpp"
#include "ITKImageProcessing/Common/ReadImageUtils.hpp"
#include "ITKImageProcessing/Filters/ITKImageReaderFilter.hpp"

Expand Down Expand Up @@ -201,7 +200,7 @@ class IOHandler

// Ensure that we are dealing with in-core memory ONLY
const IDataArray* inputArrayPtr = m_DataStructure.getDataAs<IDataArray>(imageDataPath);
if(inputArrayPtr->getDataFormat() != "")
if(!inputArrayPtr->getDataFormat().empty())
{
return MakeErrorResult(-9999, fmt::format("Input Array '{}' utilizes out-of-core data. This is not supported within ITK filters.", imageDataPath.toString()));
}
Expand All @@ -213,8 +212,17 @@ class IOHandler
image->setSpacing(FloatVec3(1.0f, 1.0f, 1.0f));

// Use ITKUtils to read the image into the DataStructure
Result<> imageReaderResult =
cxItkImageReaderFilter::ReadImageExecute<cxItkImageReaderFilter::ReadImageIntoArrayFunctor>(bound.Filepath.string(), m_DataStructure, imageDataPath, bound.Filepath.string());
Result<> imageReaderResult;
if(m_InputValues->changeDataType)
{
imageReaderResult = cxItkImageReaderFilter::ReadImageExecute<cxItkImageReaderFilter::ReadImageIntoArrayFunctor>(bound.Filepath.string(), m_DataStructure, bound.Filepath.string(),
imageDataPath, m_InputValues->destType);
}
else
{
imageReaderResult =
cxItkImageReaderFilter::ReadImageExecute<cxItkImageReaderFilter::ReadImageIntoArrayFunctor>(bound.Filepath.string(), m_DataStructure, imageDataPath, bound.Filepath.string());
}
if(imageReaderResult.invalid())
{
for(const auto& error : imageReaderResult.errors())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ struct ITKIMAGEPROCESSING_EXPORT ITKImportFijiMontageInputValues
bool changeOrigin = false;
bool convertToGrayScale = false;
bool parentDataGroup = false;
bool changeDataType = false;
DataType destType = DataType::uint8;
fs::path inputFilePath = {};
IGeometry::LengthUnit lengthUnit = IGeometry::LengthUnit::Micrometer;
std::vector<float32> origin = {};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
#include "ITKImageReaderFilter.hpp"

#include "simplnx/Core/Application.hpp"
#include "simplnx/DataStructure/DataPath.hpp"
#include "simplnx/DataStructure/DataStore.hpp"
#include "simplnx/DataStructure/Geometry/ImageGeom.hpp"
#include "simplnx/Filter/Actions/CreateArrayAction.hpp"
#include "simplnx/Filter/Actions/UpdateImageGeomAction.hpp"
#include "simplnx/Parameters/ArrayCreationParameter.hpp"
#include "simplnx/Filter/FilterHandle.hpp"
#include "simplnx/Parameters/BoolParameter.hpp"
#include "simplnx/Parameters/DataGroupCreationParameter.hpp"
#include "simplnx/Parameters/DataObjectNameParameter.hpp"
#include "simplnx/Parameters/FileSystemPathParameter.hpp"
#include "simplnx/Utilities/SIMPLConversion.hpp"

#include "ITKImageProcessing/Common/ReadImageUtils.hpp"

#include <filesystem>

#include "simplnx/Utilities/SIMPLConversion.hpp"

namespace fs = std::filesystem;

using namespace nx::core;
Expand Down Expand Up @@ -73,6 +73,12 @@ Parameters ITKImageReaderFilter::parameters() const
params.insert(std::make_unique<VectorFloat64Parameter>(k_Spacing_Key, "Spacing (Physical Units)", "Specifies the new spacing values in physical units.", std::vector<float64>{1, 1, 1},
std::vector<std::string>{"X", "Y", "Z"}));

params.insertLinkableParameter(std::make_unique<BoolParameter>(k_ChangeDataType_Key, "Set Image Data Type", "Set the final created image data type.", false));
params.insert(std::make_unique<ChoicesParameter>(k_ImageDataType_Key, "Output Data Type", "Numeric Type of data to create", 0ULL,
ChoicesParameter::Choices{"uint8", "uint16", "uint32"})); // Sequence Dependent DO NOT REORDER

params.linkParameters(k_ChangeDataType_Key, k_ImageDataType_Key, true);

params.linkParameters(k_ChangeOrigin_Key, k_Origin_Key, std::make_any<bool>(true));
params.linkParameters(k_ChangeOrigin_Key, k_CenterOrigin_Key, std::make_any<bool>(true));
params.linkParameters(k_ChangeSpacing_Key, k_Spacing_Key, std::make_any<bool>(true));
Expand Down Expand Up @@ -109,6 +115,8 @@ IFilter::PreflightResult ITKImageReaderFilter::preflightImpl(const DataStructure
auto shouldChangeSpacing = filterArgs.value<bool>(k_ChangeSpacing_Key);
auto origin = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Origin_Key);
auto spacing = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Spacing_Key);
auto pChangeDataType = filterArgs.value<bool>(k_ChangeDataType_Key);
auto pChoiceType = filterArgs.value<ChoicesParameter::ValueType>(k_ImageDataType_Key);

std::string fileNameString = fileName.string();

Expand All @@ -119,6 +127,8 @@ IFilter::PreflightResult ITKImageReaderFilter::preflightImpl(const DataStructure
imageReaderOptions.OriginAtCenterOfGeometry = shouldCenterOrigin;
imageReaderOptions.Origin = FloatVec3(static_cast<float32>(origin[0]), static_cast<float32>(origin[1]), static_cast<float32>(origin[2]));
imageReaderOptions.Spacing = FloatVec3(static_cast<float32>(spacing[0]), static_cast<float32>(spacing[1]), static_cast<float32>(spacing[2]));
imageReaderOptions.ChangeDataType = pChangeDataType;
imageReaderOptions.ImageDataType = ITK::detail::ConvertChoiceToDataType(pChoiceType);

Result<OutputActions> result = cxItkImageReaderFilter::ReadImagePreflight(fileNameString, imageGeomPath, cellDataName, imageDataArrayName, imageReaderOptions);

Expand All @@ -138,9 +148,10 @@ Result<> ITKImageReaderFilter::executeImpl(DataStructure& dataStructure, const A
// auto shouldChangeSpacing = filterArgs.value<bool>(k_ChangeSpacing_Key);
auto origin = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Origin_Key);
auto spacing = filterArgs.value<VectorFloat64Parameter::ValueType>(k_Spacing_Key);
auto pChangeDataType = filterArgs.value<bool>(k_ChangeDataType_Key);
auto newDataType = ITK::detail::ConvertChoiceToDataType(filterArgs.value<ChoicesParameter::ValueType>(k_ImageDataType_Key));

DataPath imageDataArrayPath = imageGeometryPath.createChildPath(cellDataName).createChildPath(imageDataArrayName);

const IDataArray* inputArrayPtr = dataStructure.getDataAs<IDataArray>(imageDataArrayPath);
if(!inputArrayPtr->getDataFormat().empty())
{
Expand All @@ -149,9 +160,15 @@ Result<> ITKImageReaderFilter::executeImpl(DataStructure& dataStructure, const A

std::string fileNameString = fileName.string();

ImageGeom& imageGeom = dataStructure.getDataRefAs<ImageGeom>(imageGeometryPath);

auto result = cxItkImageReaderFilter::ReadImageExecute<cxItkImageReaderFilter::ReadImageIntoArrayFunctor>(fileNameString, dataStructure, imageDataArrayPath, fileNameString);
Result<> result = {};
if(pChangeDataType)
{
result = cxItkImageReaderFilter::ReadImageExecute<cxItkImageReaderFilter::ReadImageIntoArrayFunctor>(fileNameString, dataStructure, fileNameString, imageDataArrayPath, newDataType);
}
else
{
result = cxItkImageReaderFilter::ReadImageExecute<cxItkImageReaderFilter::ReadImageIntoArrayFunctor>(fileNameString, dataStructure, imageDataArrayPath, fileNameString);
}

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ class ITKIMAGEPROCESSING_EXPORT ITKImageReaderFilter : public IFilter
static inline constexpr StringLiteral k_ChangeSpacing_Key = "change_spacing";
static inline constexpr StringLiteral k_Spacing_Key = "spacing";

static inline constexpr StringLiteral k_ChangeDataType_Key = "change_image_data_type";
static inline constexpr StringLiteral k_ImageDataType_Key = "image_data_type_index";

/**
* @brief Reads SIMPL json and converts it simplnx Arguments.
* @param json
Expand Down
Loading
Loading