Skip to content

Commit

Permalink
BUG: Fix Import Fiji Montage Origin and Grayscale conversions (#777)
Browse files Browse the repository at this point in the history
* 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
nyoungbq and imikejackson authored Nov 30, 2023
1 parent b32d15b commit 462b70b
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 259 deletions.
1 change: 0 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,6 @@ file(WRITE ${FETCH_FILE_PATH} "# -----------------------------------------------
# -----------------------------------------------------------------------\n
cmake_policy(SET CMP0012 NEW)
cmake_policy(SET CMP0054 NEW)\n
message(STATUS \"CMAKE_CONFIG=\${CMAKE_CONFIG}\")
")

set(TEST_WORKING_DIR "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}")
Expand Down
2 changes: 2 additions & 0 deletions src/Plugins/ITKImageProcessing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,8 @@ 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/ReadImageUtils.hpp
${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/Common/ReadImageUtils.cpp
)

set(${PLUGIN_NAME}_GENERATED_DIR ${complex_BINARY_DIR}/Plugins/${ARGS_PLUGIN_NAME}/generated)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,10 @@ Utilizes the *itkReadImage* and *ColorToGrayScale* filters
SampleMosaic_p4.bmp; ; (0.23675, 1839.55)
SampleMosaic_p5.bmp; ; (1227.31, 1839.55)


### Color To Gray Scale Notes

**For this option to work the read in color array must be a *UInt8Array* otherwise the image will be skipped over when loading**

The luminosity method is a more sophisticated version of the average method. It also averages the values, but it forms a weighted average to account for human perception. We re more sensitive to green than other colors, so green is weighted most heavily. The default formula for luminosity is BT709 Gray scale:

Red: 0.2125 Green: 0.7154 Blue: 0.0721.
Expand Down
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
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
Loading

0 comments on commit 462b70b

Please sign in to comment.