diff --git a/CMakeLists.txt b/CMakeLists.txt index da38d7f90d..ce69a866e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/src/Plugins/ITKImageProcessing/CMakeLists.txt b/src/Plugins/ITKImageProcessing/CMakeLists.txt index d10cccdfe9..98eeaefd32 100644 --- a/src/Plugins/ITKImageProcessing/CMakeLists.txt +++ b/src/Plugins/ITKImageProcessing/CMakeLists.txt @@ -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) diff --git a/src/Plugins/ITKImageProcessing/docs/ITKImportFijiMontageFilter.md b/src/Plugins/ITKImageProcessing/docs/ITKImportFijiMontageFilter.md index c63f4e43f9..46991cbe1c 100644 --- a/src/Plugins/ITKImageProcessing/docs/ITKImportFijiMontageFilter.md +++ b/src/Plugins/ITKImageProcessing/docs/ITKImportFijiMontageFilter.md @@ -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. diff --git a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.cpp b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.cpp new file mode 100644 index 0000000000..317bfb2b48 --- /dev/null +++ b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.cpp @@ -0,0 +1,60 @@ +#include "ReadImageUtils.hpp" + +namespace cxItkImageReader +{ + +//------------------------------------------------------------------------------ +Result 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(-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 = ITK::ConvertIOComponentToDataType(component); + if(!numericType.has_value()) + { + return MakeErrorResult(-4, fmt::format("Unsupported pixel component: {}", imageIO->GetComponentTypeAsString(component))); + } + + uint32 nDims = imageIO->GetNumberOfDimensions(); + + std::vector dims = {1, 1, 1}; + std::vector origin = {0.0f, 0.0f, 0.0f}; + std::vector spacing = {1.0f, 1.0f, 1.0f}; + + for(uint32 i = 0; i < nDims; i++) + { + dims[i] = static_cast(imageIO->GetDimensions(i)); + origin[i] = static_cast(imageIO->GetOrigin(i)); + spacing[i] = static_cast(imageIO->GetSpacing(i)); + } + + uint32 nComponents = imageIO->GetNumberOfComponents(); + + // DataArray dimensions are stored slowest to fastest, the opposite of ImageGeometry + std::vector arrayDims(dims.crbegin(), dims.crend()); + + std::vector cDims = {nComponents}; + + actions.appendAction(std::make_unique(std::move(imageGeomPath), std::move(dims), std::move(origin), std::move(spacing), cellDataName)); + actions.appendAction(std::make_unique(*numericType, std::move(arrayDims), std::move(cDims), std::move(arrayPath))); + + } catch(const itk::ExceptionObject& err) + { + return MakeErrorResult(-55557, fmt::format("ITK exception was thrown while processing input file: {}", err.what())); + } + + return {std::move(actions)}; +} +} // namespace cxItkImageReader diff --git a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.hpp b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.hpp new file mode 100644 index 0000000000..c3850d38ce --- /dev/null +++ b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Common/ReadImageUtils.hpp @@ -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 + +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 + Result<> operator()() const + { + return {}; + } +}; + +struct ReadImageIntoArrayFunctor +{ + //------------------------------------------------------------------------------ + template + Result<> operator()(DataStructure& dataStructure, const DataPath& arrayPath, const std::string& filePath) const + { + using ImageType = itk::Image; + using ReaderType = itk::ImageFileReader; + + using T = ITK::UnderlyingType_t; + + auto& dataArray = dataStructure.getDataRefAs>(arrayPath); + auto& dataStore = dataArray.template getIDataStoreRefAs>(); + + 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 +Result<> ReadImageByPixelType(const itk::ImageIOBase& imageIO, ArgsT&&... args) +{ + const uint32 numComponents = imageIO.GetNumberOfComponents(); + + switch(numComponents) + { + case 1: { + return FunctorT().template operator(), Dimension>(std::forward(args)...); + } + case 2: { + return FunctorT().template operator(), Dimension>(std::forward(args)...); + } + case 3: { + return FunctorT().template operator(), Dimension>(std::forward(args)...); + } + case 4: { + return FunctorT().template operator(), Dimension>(std::forward(args)...); + } + case 36: { + return FunctorT().template operator(), Dimension>(std::forward(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 +Result<> ReadImageByDimension(const itk::ImageIOBase& imageIO, ArgsT&&... args) +{ + uint32 dimensions = imageIO.GetNumberOfDimensions(); + switch(dimensions) + { + case 1: { + return ReadImageByPixelType(imageIO, args...); + } + case 2: { + return ReadImageByPixelType(imageIO, args...); + } + case 3: { + return ReadImageByPixelType(imageIO, args...); + } + default: { + return MakeErrorResult(-1, fmt::format("Unsupported number of dimensions: {}", dimensions)); + } + } +} + +//------------------------------------------------------------------------------ +template +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 = 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(*imageIO, args...); + } + case NumericType::int8: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::uint16: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::int16: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::uint32: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::int32: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::uint64: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::int64: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::float32: { + return ReadImageByDimension(*imageIO, args...); + } + case NumericType::float64: { + return ReadImageByDimension(*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 ReadImagePreflight(const std::string& fileName, DataPath imageGeomPath, std::string cellDataName, DataPath arrayPath); + +} // namespace cxItkImageReader diff --git a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/Algorithms/ITKImportFijiMontage.cpp b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/Algorithms/ITKImportFijiMontage.cpp index 8b05ff663e..a44b355642 100644 --- a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/Algorithms/ITKImportFijiMontage.cpp +++ b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/Algorithms/ITKImportFijiMontage.cpp @@ -1,5 +1,7 @@ #include "ITKImportFijiMontage.hpp" +#include "ITKImageProcessing/Common/ITKArrayHelper.hpp" +#include "ITKImageProcessing/Common/ReadImageUtils.hpp" #include "ITKImageProcessing/Filters/ITKImageReader.hpp" #include "complex/Common/Array.hpp" @@ -53,11 +55,11 @@ class IOHandler if(!m_Allocate) { - FillCache(); + fillCache(); } else { - return ReadImages(); + return readImages(); } return {}; @@ -73,7 +75,7 @@ class IOHandler const ITKImportFijiMontageInputValues* m_InputValues; // ----------------------------------------------------------------------------- - void ParseConfigFile() + void parseConfigFile() { bool dimFound = false; bool dataFound = false; @@ -137,10 +139,9 @@ class IOHandler } // ----------------------------------------------------------------------------- - void FillCache() + void fillCache() { - // This next function will set the FileName (Partial), Row, Col for each "bound" object - ParseConfigFile(); + parseConfigFile(); Point3Df minCoord = {std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()}; @@ -148,7 +149,7 @@ class IOHandler for(auto& bound : m_Cache.bounds) { std::stringstream dcNameStream; - dcNameStream << m_InputValues->imagePrefix << bound.Filepath.stem(); + dcNameStream << m_InputValues->imagePrefix << bound.Filepath.stem().string(); bound.ImageName = dcNameStream.str(); { @@ -177,7 +178,7 @@ class IOHandler } // ----------------------------------------------------------------------------- - Result<> ReadImages() + Result<> readImages() { Result<> outputResult = {}; @@ -188,40 +189,41 @@ class IOHandler { m_Filter->sendUpdate(("Importing " + bound.Filepath.filename().string())); - DataPath imageDataProxy = {}; + DataPath imageDataPath = {}; if(m_InputValues->parentDataGroup) { - imageDataProxy = DataPath({m_InputValues->DataGroupName, bound.ImageName, m_InputValues->cellAMName, m_InputValues->imageDataArrayName}); + imageDataPath = DataPath({m_InputValues->DataGroupName, bound.ImageName, m_InputValues->cellAMName, m_InputValues->imageDataArrayName}); } else { - imageDataProxy = DataPath({bound.ImageName, bound.ImageName, m_InputValues->cellAMName, m_InputValues->imageDataArrayName}); + imageDataPath = DataPath({bound.ImageName, bound.ImageName, m_InputValues->cellAMName, m_InputValues->imageDataArrayName}); } - // Instantiate the Image Import Filter to actually read the image into a data array + // Ensure that we are dealing with in-core memory ONLY + const IDataArray* inputArrayPtr = m_DataStructure.getDataAs(imageDataPath); + if(inputArrayPtr->getDataFormat() != "") { - // execute image import filter - // This same filter was used to preflight so as long as nothing changes on disk this really should work.... - Arguments imageImportArgs; - imageImportArgs.insertOrAssign(ITKImageReader::k_FileName_Key, std::make_any(bound.Filepath)); - imageImportArgs.insertOrAssign(ITKImageReader::k_ImageGeometryPath_Key, std::make_any(imageDataProxy.getParent().getParent())); - imageImportArgs.insertOrAssign(ITKImageReader::k_CellDataName_Key, std::make_any(imageDataProxy.getParent().getTargetName())); - imageImportArgs.insertOrAssign(ITKImageReader::k_ImageDataArrayPath_Key, std::make_any(imageDataProxy)); - - auto result = imageImportFilter.execute(m_DataStructure, imageImportArgs).result; - if(result.invalid()) - { - outputResult = MergeResults(outputResult, result); - continue; - } + return MakeErrorResult(-9999, fmt::format("Input Array '{}' utilizes out-of-core data. This is not supported within ITK filters.", imageDataPath.toString())); } - auto* image = m_DataStructure.getDataAs(imageDataProxy.getParent().getParent()); + // Set the Correct Origin, Spacing and Units for the Image Geometry + auto* image = m_DataStructure.getDataAs(imageDataPath.getParent().getParent()); image->setUnits(m_InputValues->lengthUnit); image->setOrigin(bound.Origin); image->setSpacing(FloatVec3(1.0f, 1.0f, 1.0f)); - // Now transfer the image data from the actual image data read from disk into our existing Attribute Matrix + // Use ITKUtils to read the image into the DataStructure + Result<> imageReaderResult = cxItkImageReader::ReadImageExecute(bound.Filepath.string(), m_DataStructure, imageDataPath, bound.Filepath.string()); + if(imageReaderResult.invalid()) + { + for(const auto& error : imageReaderResult.errors()) + { + m_Filter->sendUpdate(fmt::format("|-- Error Reading Image: Code ({}) - {}", error.code, error.message)); + } + continue; // Let us try to continue to the next image if we encountered a problem reading this image + } + + // Check if we need to convert to grayscale if(m_InputValues->convertToGrayScale) { if(!filterListPtr->containsPlugin(k_ComplexCorePluginId)) @@ -234,11 +236,18 @@ class IOHandler continue; } + if(m_DataStructure.getDataRefAs(imageDataPath).getDataType() != DataType::uint8) + { + outputResult.warnings().emplace_back( + Warning{-74320, fmt::format("The array ({}) is not a UIntArray, so it will not be converted to grayscale. Continuing...", imageDataPath.getTargetName())}); + continue; + } + // This same filter was used to preflight so as long as nothing changes on disk this really should work.... Arguments colorToGrayscaleArgs; colorToGrayscaleArgs.insertOrAssign("conversion_algorithm", std::make_any(0)); colorToGrayscaleArgs.insertOrAssign("color_weights", std::make_any(m_InputValues->colorWeights)); - colorToGrayscaleArgs.insertOrAssign("input_data_array_vector", std::make_any>(std::vector{imageDataProxy})); + colorToGrayscaleArgs.insertOrAssign("input_data_array_vector", std::make_any>(std::vector{imageDataPath})); colorToGrayscaleArgs.insertOrAssign("output_array_prefix", std::make_any("gray")); // Run grayscale filter and process results and messages @@ -251,19 +260,19 @@ class IOHandler // deletion of non-grayscale array DataObject::IdType id; { // scoped for safety since this reference will be nonexistent in a moment - auto& oldArray = m_DataStructure.getDataRefAs(imageDataProxy); + auto& oldArray = m_DataStructure.getDataRefAs(imageDataPath); id = oldArray.getId(); } m_DataStructure.removeData(id); // rename grayscale array to reflect original { - auto& gray = m_DataStructure.getDataRefAs(imageDataProxy.getParent().createChildPath("gray" + imageDataProxy.getTargetName())); - if(!gray.canRename(imageDataProxy.getTargetName())) + auto& gray = m_DataStructure.getDataRefAs(imageDataPath.getParent().createChildPath("gray" + imageDataPath.getTargetName())); + if(!gray.canRename(imageDataPath.getTargetName())) { - return MakeErrorResult(-18543, fmt::format("Unable to rename the grayscale array to {}", imageDataProxy.getTargetName())); + return MakeErrorResult(-18543, fmt::format("Unable to rename the grayscale array to {}", imageDataPath.getTargetName())); } - gray.rename(imageDataProxy.getTargetName()); + gray.rename(imageDataPath.getTargetName()); } } } diff --git a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImageReader.cpp b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImageReader.cpp index 9e92877394..4fbd908456 100644 --- a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImageReader.cpp +++ b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImageReader.cpp @@ -4,7 +4,6 @@ #include "complex/DataStructure/DataStore.hpp" #include "complex/DataStructure/Geometry/ImageGeom.hpp" #include "complex/Filter/Actions/CreateArrayAction.hpp" -#include "complex/Filter/Actions/CreateImageGeometryAction.hpp" #include "complex/Parameters/ArrayCreationParameter.hpp" #include "complex/Parameters/BoolParameter.hpp" #include "complex/Parameters/DataGroupCreationParameter.hpp" @@ -12,229 +11,14 @@ #include "complex/Parameters/FileSystemPathParameter.hpp" #include "ITKImageProcessing/Common/ITKArrayHelper.hpp" +#include "ITKImageProcessing/Common/ReadImageUtils.hpp" #include -#include - namespace fs = std::filesystem; 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 - Result<> operator()() const - { - return {}; - } -}; - -struct ReadImageIntoArrayFunctor -{ - //------------------------------------------------------------------------------ - template - Result<> operator()(DataStructure& dataStructure, const DataPath& arrayPath, const std::string& filePath) const - { - using ImageType = itk::Image; - using ReaderType = itk::ImageFileReader; - - using T = ITK::UnderlyingType_t; - - auto& dataArray = dataStructure.getDataRefAs>(arrayPath); - auto& dataStore = dataArray.template getIDataStoreRefAs>(); - - 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 -Result<> ReadImageByPixelType(const itk::ImageIOBase& imageIO, ArgsT&&... args) -{ - const uint32 numComponents = imageIO.GetNumberOfComponents(); - - switch(numComponents) - { - case 1: { - return FunctorT().template operator(), Dimension>(std::forward(args)...); - } - case 2: { - return FunctorT().template operator(), Dimension>(std::forward(args)...); - } - case 3: { - return FunctorT().template operator(), Dimension>(std::forward(args)...); - } - case 4: { - return FunctorT().template operator(), Dimension>(std::forward(args)...); - } - case 36: { - return FunctorT().template operator(), Dimension>(std::forward(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 -Result<> ReadImageByDimension(const itk::ImageIOBase& imageIO, ArgsT&&... args) -{ - uint32 dimensions = imageIO.GetNumberOfDimensions(); - switch(dimensions) - { - case 1: { - return ReadImageByPixelType(imageIO, args...); - } - case 2: { - return ReadImageByPixelType(imageIO, args...); - } - case 3: { - return ReadImageByPixelType(imageIO, args...); - } - default: { - return MakeErrorResult(-1, fmt::format("Unsupported number of dimensions: {}", dimensions)); - } - } -} - -//------------------------------------------------------------------------------ -template -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 = 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(*imageIO, args...); - } - case NumericType::int8: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::uint16: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::int16: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::uint32: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::int32: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::uint64: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::int64: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::float32: { - return ReadImageByDimension(*imageIO, args...); - } - case NumericType::float64: { - return ReadImageByDimension(*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 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(-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 = ITK::ConvertIOComponentToDataType(component); - if(!numericType.has_value()) - { - return MakeErrorResult(-4, fmt::format("Unsupported pixel component: {}", imageIO->GetComponentTypeAsString(component))); - } - - uint32 nDims = imageIO->GetNumberOfDimensions(); - - std::vector dims = {1, 1, 1}; - std::vector origin = {0.0f, 0.0f, 0.0f}; - std::vector spacing = {1.0f, 1.0f, 1.0f}; - - for(uint32 i = 0; i < nDims; i++) - { - dims[i] = static_cast(imageIO->GetDimensions(i)); - origin[i] = static_cast(imageIO->GetOrigin(i)); - spacing[i] = static_cast(imageIO->GetSpacing(i)); - } - - uint32 nComponents = imageIO->GetNumberOfComponents(); - - // DataArray dimensions are stored slowest to fastest, the opposite of ImageGeometry - std::vector arrayDims(dims.crbegin(), dims.crend()); - - std::vector cDims = {nComponents}; - - actions.appendAction(std::make_unique(std::move(imageGeomPath), std::move(dims), std::move(origin), std::move(spacing), cellDataName)); - actions.appendAction(std::make_unique(*numericType, std::move(arrayDims), std::move(cDims), std::move(arrayPath))); - - } catch(const itk::ExceptionObject& err) - { - return MakeErrorResult(-55557, fmt::format("ITK exception was thrown while processing input file: {}", err.what())); - } - - return {std::move(actions)}; -} - -} // namespace cxItkImageReader - namespace complex { //------------------------------------------------------------------------------ diff --git a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImportFijiMontageFilter.cpp b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImportFijiMontageFilter.cpp index beb3977fae..39207a3061 100644 --- a/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImportFijiMontageFilter.cpp +++ b/src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImportFijiMontageFilter.cpp @@ -7,6 +7,7 @@ #include "complex/Core/Application.hpp" #include "complex/DataStructure/DataPath.hpp" #include "complex/Filter/Actions/CreateDataGroupAction.hpp" +#include "complex/Filter/Actions/UpdateImageGeomAction.hpp" #include "complex/Parameters/BoolParameter.hpp" #include "complex/Parameters/ChoicesParameter.hpp" #include "complex/Parameters/FileSystemPathParameter.hpp" @@ -180,6 +181,10 @@ IFilter::PreflightResult ITKImportFijiMontageFilter::preflightImpl(const DataStr { return result; } + + std::optional originVec = FloatVec3(bound.Origin[0], bound.Origin[1], bound.Origin[2]); + std::optional spacingVec; + result.outputActions.value().appendAction(std::make_unique(originVec, spacingVec, imageDataProxy.getParent().getParent())); resultOutputActions = MergeOutputActionResults(resultOutputActions, result.outputActions); } diff --git a/src/Plugins/ITKImageProcessing/test/CMakeLists.txt b/src/Plugins/ITKImageProcessing/test/CMakeLists.txt index ef081d8051..7f246fbc15 100644 --- a/src/Plugins/ITKImageProcessing/test/CMakeLists.txt +++ b/src/Plugins/ITKImageProcessing/test/CMakeLists.txt @@ -108,7 +108,7 @@ if(EXISTS "${DREAM3D_DATA_DIR}" AND COMPLEX_DOWNLOAD_TEST_FILES) if(NOT EXISTS ${DREAM3D_DATA_DIR}/TestFiles/) file(MAKE_DIRECTORY "${DREAM3D_DATA_DIR}/TestFiles/") endif() - download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME fiji_montage_test.tar.gz SHA512 c2504af7bdeef3271281e50df494417a067e7d5ed403b3216de8e9588a2603ac65466385793e6180a70c627267d499a91725ec1762afbce2320a18f569e76cb1) + download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME fiji_montage.tar.gz SHA512 70139babc838ce3ab1f5adddfddc86dcc51996e614c6c2d757bcb2e59e8ebdc744dac269233494b1ef8d09397aecb4ccca3384f0a91bb017f2cf6309c4ac40fa) endif() diff --git a/src/Plugins/ITKImageProcessing/test/ITKImportFijiMontageTest.cpp b/src/Plugins/ITKImageProcessing/test/ITKImportFijiMontageTest.cpp index 639f283f75..94fd850015 100644 --- a/src/Plugins/ITKImageProcessing/test/ITKImportFijiMontageTest.cpp +++ b/src/Plugins/ITKImageProcessing/test/ITKImportFijiMontageTest.cpp @@ -19,7 +19,7 @@ namespace fs = std::filesystem; namespace { -const std::string k_SmallZeissZenDir = fmt::format("{}/fiji_montage_test/small_zeiss_zen", unit_test::k_TestFilesDir); +const std::string k_SmallZeissZenDir = fmt::format("{}/fiji_montage/small_zeiss_zen", unit_test::k_TestFilesDir); const std::string k_DataGroupName = "Zen DataGroup"; const DataPath k_DataGroupPath = {{k_DataGroupName}}; @@ -27,12 +27,12 @@ const DataPath k_DataGroupPath = {{k_DataGroupName}}; TEST_CASE("ITKImageProcessing::ITKImportFijiMontage: Basic 2x2 Grid Montage", "[ITKImageProcessing][ITKImportFijiMontage]") { - const complex::UnitTest::TestFileSentinel testDataSentinel(complex::unit_test::k_CMakeExecutable, complex::unit_test::k_TestFilesDir, "fiji_montage_test.tar.gz", "fiji_montage_test"); + const complex::UnitTest::TestFileSentinel testDataSentinel(complex::unit_test::k_CMakeExecutable, complex::unit_test::k_TestFilesDir, "fiji_montage.tar.gz", "fiji_montage"); auto app = Application::GetOrCreateInstance(); app->loadPlugins(unit_test::k_BuildDir.view(), true); - DataStructure exemplarDataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/fiji_montage_test/2x2_fiji_montage_test.dream3d", unit_test::k_TestFilesDir))); + DataStructure exemplarDataStructure = UnitTest::LoadDataStructure(fs::path(fmt::format("{}/fiji_montage/2x2_fiji_montage_test.dream3d", unit_test::k_TestFilesDir))); // Instantiate the filter, a DataStructure object and an Arguments Object ITKImportFijiMontageFilter filter; @@ -47,9 +47,9 @@ TEST_CASE("ITKImageProcessing::ITKImportFijiMontage: Basic 2x2 Grid Montage", "[ args.insertOrAssign(ITKImportFijiMontageFilter::k_DataGroupName_Key, std::make_any(k_DataGroupName)); args.insertOrAssign(ITKImportFijiMontageFilter::k_LengthUnit_Key, std::make_any(to_underlying(IGeometry::LengthUnit::Micrometer))); args.insertOrAssign(ITKImportFijiMontageFilter::k_ChangeOrigin_Key, std::make_any(false)); - args.insertOrAssign(ITKImportFijiMontageFilter::k_ConvertToGrayScale_Key, std::make_any(true)); + args.insertOrAssign(ITKImportFijiMontageFilter::k_ConvertToGrayScale_Key, std::make_any(false)); args.insertOrAssign(ITKImportFijiMontageFilter::k_ParentDataGroup_Key, std::make_any(true)); - args.insertOrAssign(ITKImportFijiMontageFilter::k_ColorWeights_Key, std::make_any({0.2125f, 0.7154f, 0.0721f})); + // args.insertOrAssign(ITKImportFijiMontageFilter::k_ColorWeights_Key, std::make_any({0.2125f, 0.7154f, 0.0721f})); args.insertOrAssign(ITKImportFijiMontageFilter::k_DataContainerPath_Key, std::make_any("Mosaic-")); args.insertOrAssign(ITKImportFijiMontageFilter::k_CellAttributeMatrixName_Key, std::make_any("Tile Data")); args.insertOrAssign(ITKImportFijiMontageFilter::k_ImageDataArrayName_Key, std::make_any("Image"));