-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BUG: Fix Import Fiji Montage Origin and Grayscale conversions (#777)
* Fix incorrect origins during preflight * Extracted Functions to read image files into a utility header. * Explicit string conversions for MSVC compile * Update type restrictions for color to grayscale option and test updates * Remove unused test file * Documentation updates --------- Signed-off-by: Michael Jackson <[email protected]> Co-authored-by: Michael Jackson <[email protected]>
- Loading branch information
1 parent
b32d15b
commit 462b70b
Showing
10 changed files
with
292 additions
and
259 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
60 changes: 60 additions & 0 deletions
60
src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
#include "ReadImageUtils.hpp" | ||
|
||
namespace cxItkImageReader | ||
{ | ||
|
||
//------------------------------------------------------------------------------ | ||
Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath imageGeomPath, std::string cellDataName, DataPath arrayPath) | ||
{ | ||
OutputActions actions; | ||
|
||
try | ||
{ | ||
itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fileName.c_str(), itk::ImageIOFactory::ReadMode); | ||
if(imageIO == nullptr) | ||
{ | ||
return MakeErrorResult<OutputActions>(-5, fmt::format("ITK could not read the given file \"{}\". Format is likely unsupported.", fileName)); | ||
} | ||
|
||
imageIO->SetFileName(fileName); | ||
imageIO->ReadImageInformation(); | ||
|
||
itk::ImageIOBase::IOComponentEnum component = imageIO->GetComponentType(); | ||
|
||
std::optional<DataType> numericType = ITK::ConvertIOComponentToDataType(component); | ||
if(!numericType.has_value()) | ||
{ | ||
return MakeErrorResult<OutputActions>(-4, fmt::format("Unsupported pixel component: {}", imageIO->GetComponentTypeAsString(component))); | ||
} | ||
|
||
uint32 nDims = imageIO->GetNumberOfDimensions(); | ||
|
||
std::vector<size_t> dims = {1, 1, 1}; | ||
std::vector<float32> origin = {0.0f, 0.0f, 0.0f}; | ||
std::vector<float32> spacing = {1.0f, 1.0f, 1.0f}; | ||
|
||
for(uint32 i = 0; i < nDims; i++) | ||
{ | ||
dims[i] = static_cast<usize>(imageIO->GetDimensions(i)); | ||
origin[i] = static_cast<float32>(imageIO->GetOrigin(i)); | ||
spacing[i] = static_cast<float32>(imageIO->GetSpacing(i)); | ||
} | ||
|
||
uint32 nComponents = imageIO->GetNumberOfComponents(); | ||
|
||
// DataArray dimensions are stored slowest to fastest, the opposite of ImageGeometry | ||
std::vector<usize> arrayDims(dims.crbegin(), dims.crend()); | ||
|
||
std::vector<usize> cDims = {nComponents}; | ||
|
||
actions.appendAction(std::make_unique<CreateImageGeometryAction>(std::move(imageGeomPath), std::move(dims), std::move(origin), std::move(spacing), cellDataName)); | ||
actions.appendAction(std::make_unique<CreateArrayAction>(*numericType, std::move(arrayDims), std::move(cDims), std::move(arrayPath))); | ||
|
||
} catch(const itk::ExceptionObject& err) | ||
{ | ||
return MakeErrorResult<OutputActions>(-55557, fmt::format("ITK exception was thrown while processing input file: {}", err.what())); | ||
} | ||
|
||
return {std::move(actions)}; | ||
} | ||
} // namespace cxItkImageReader |
173 changes: 173 additions & 0 deletions
173
src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
#pragma once | ||
|
||
#include "complex/Common/Result.hpp" | ||
#include "complex/Common/Types.hpp" | ||
#include "complex/Filter/Actions/CreateImageGeometryAction.hpp" | ||
|
||
#include "ITKImageProcessing/Common/ITKArrayHelper.hpp" | ||
|
||
#include <itkImageFileReader.h> | ||
|
||
using namespace complex; | ||
|
||
namespace cxItkImageReader | ||
{ | ||
// This functor is a dummy that will return a valid Result<> if the ImageIOBase is a supported type, dimension, etc. | ||
struct PreflightFunctor | ||
{ | ||
//------------------------------------------------------------------------------ | ||
template <class PixelT, uint32 Dimension> | ||
Result<> operator()() const | ||
{ | ||
return {}; | ||
} | ||
}; | ||
|
||
struct ReadImageIntoArrayFunctor | ||
{ | ||
//------------------------------------------------------------------------------ | ||
template <class PixelT, uint32 Dimension> | ||
Result<> operator()(DataStructure& dataStructure, const DataPath& arrayPath, const std::string& filePath) const | ||
{ | ||
using ImageType = itk::Image<PixelT, Dimension>; | ||
using ReaderType = itk::ImageFileReader<ImageType>; | ||
|
||
using T = ITK::UnderlyingType_t<PixelT>; | ||
|
||
auto& dataArray = dataStructure.getDataRefAs<DataArray<T>>(arrayPath); | ||
auto& dataStore = dataArray.template getIDataStoreRefAs<DataStore<T>>(); | ||
|
||
typename ReaderType::Pointer reader = ReaderType::New(); | ||
reader->SetFileName(filePath); | ||
|
||
reader->Update(); | ||
typename ImageType::Pointer outputImage = reader->GetOutput(); | ||
|
||
auto imageStore = ITK::ConvertImageToDataStore(*outputImage); | ||
|
||
dataStore = std::move(imageStore); | ||
|
||
return {}; | ||
} | ||
}; | ||
|
||
//------------------------------------------------------------------------------ | ||
template <class T, usize Dimension, class FunctorT, class... ArgsT> | ||
Result<> ReadImageByPixelType(const itk::ImageIOBase& imageIO, ArgsT&&... args) | ||
{ | ||
const uint32 numComponents = imageIO.GetNumberOfComponents(); | ||
|
||
switch(numComponents) | ||
{ | ||
case 1: { | ||
return FunctorT().template operator()<itk::Vector<T, 1>, Dimension>(std::forward<ArgsT>(args)...); | ||
} | ||
case 2: { | ||
return FunctorT().template operator()<itk::Vector<T, 2>, Dimension>(std::forward<ArgsT>(args)...); | ||
} | ||
case 3: { | ||
return FunctorT().template operator()<itk::Vector<T, 3>, Dimension>(std::forward<ArgsT>(args)...); | ||
} | ||
case 4: { | ||
return FunctorT().template operator()<itk::Vector<T, 4>, Dimension>(std::forward<ArgsT>(args)...); | ||
} | ||
case 36: { | ||
return FunctorT().template operator()<itk::Vector<T, 36>, Dimension>(std::forward<ArgsT>(args)...); | ||
} | ||
default: { | ||
return MakeErrorResult(-4, fmt::format("Unsupported number of components: {} in image file. 1,2,3,4,36 are the only supported number of components", numComponents)); | ||
} | ||
} | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
template <class T, class FunctorT, class... ArgsT> | ||
Result<> ReadImageByDimension(const itk::ImageIOBase& imageIO, ArgsT&&... args) | ||
{ | ||
uint32 dimensions = imageIO.GetNumberOfDimensions(); | ||
switch(dimensions) | ||
{ | ||
case 1: { | ||
return ReadImageByPixelType<T, 1, FunctorT>(imageIO, args...); | ||
} | ||
case 2: { | ||
return ReadImageByPixelType<T, 2, FunctorT>(imageIO, args...); | ||
} | ||
case 3: { | ||
return ReadImageByPixelType<T, 3, FunctorT>(imageIO, args...); | ||
} | ||
default: { | ||
return MakeErrorResult(-1, fmt::format("Unsupported number of dimensions: {}", dimensions)); | ||
} | ||
} | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
template <class FunctorT, class... ArgsT> | ||
Result<> ReadImageExecute(const std::string& fileName, ArgsT&&... args) | ||
{ | ||
try | ||
{ | ||
itk::ImageIOBase::Pointer imageIO = itk::ImageIOFactory::CreateImageIO(fileName.c_str(), itk::ImageIOFactory::ReadMode); | ||
if(imageIO == nullptr) | ||
{ | ||
return MakeErrorResult(-5, fmt::format("ITK could not read the given file \"{}\". Format is likely unsupported.", fileName)); | ||
} | ||
|
||
imageIO->SetFileName(fileName); | ||
imageIO->ReadImageInformation(); | ||
|
||
itk::ImageIOBase::IOComponentEnum component = imageIO->GetComponentType(); | ||
|
||
std::optional<NumericType> numericType = ITK::ConvertIOComponentToNumericType(component); | ||
if(!numericType.has_value()) | ||
{ | ||
return MakeErrorResult(-4, fmt::format("Unsupported pixel component: {}", imageIO->GetComponentTypeAsString(component))); | ||
} | ||
|
||
switch(*numericType) | ||
{ | ||
case NumericType::uint8: { | ||
return ReadImageByDimension<uint8, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::int8: { | ||
return ReadImageByDimension<int8, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::uint16: { | ||
return ReadImageByDimension<uint16, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::int16: { | ||
return ReadImageByDimension<int16, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::uint32: { | ||
return ReadImageByDimension<uint32, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::int32: { | ||
return ReadImageByDimension<int32, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::uint64: { | ||
return ReadImageByDimension<uint64, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::int64: { | ||
return ReadImageByDimension<int64, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::float32: { | ||
return ReadImageByDimension<float32, FunctorT>(*imageIO, args...); | ||
} | ||
case NumericType::float64: { | ||
return ReadImageByDimension<float64, FunctorT>(*imageIO, args...); | ||
} | ||
default: { | ||
throw std::runtime_error(""); | ||
} | ||
} | ||
} catch(const itk::ExceptionObject& err) | ||
{ | ||
return MakeErrorResult(-55557, fmt::format("ITK exception was thrown while processing input file: {}", err.what())); | ||
} | ||
} | ||
|
||
//------------------------------------------------------------------------------ | ||
Result<OutputActions> ReadImagePreflight(const std::string& fileName, DataPath imageGeomPath, std::string cellDataName, DataPath arrayPath); | ||
|
||
} // namespace cxItkImageReader |
Oops, something went wrong.