Skip to content

Commit 3f70819

Browse files
BUG: Fix ITKImportImageStack image flip operations. Add unit test for flip operations. (BlueQuartzSoftware#815)
Signed-off-by: Michael Jackson <[email protected]> Co-authored-by: nyoungbq <[email protected]>
1 parent 554be1b commit 3f70819

File tree

3 files changed

+230
-1
lines changed

3 files changed

+230
-1
lines changed

src/Plugins/ITKImageProcessing/src/ITKImageProcessing/Filters/ITKImportImageStack.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ void FlipAboutXAxis(DataArray<T>& dataArray, Vec3<usize>& dims)
104104
{
105105
auto& tempDataStore = dataArray.getDataStoreRef();
106106
usize numComp = tempDataStore.getNumberOfComponents();
107-
size_t rowLCV = (dims[1] % 2 == 1) ? (dims[1] - 1 / 2) : dims[1] / 2;
107+
size_t rowLCV = (dims[1] % 2 == 1) ? ((dims[1] - 1) / 2) : dims[1] / 2;
108108
usize bottomRow = dims[1] - 1;
109109

110110
for(usize row = 0; row < rowLCV; row++)

src/Plugins/ITKImageProcessing/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ if(EXISTS "${DREAM3D_DATA_DIR}" AND SIMPLNX_DOWNLOAD_TEST_FILES)
113113
file(MAKE_DIRECTORY "${DREAM3D_DATA_DIR}/TestFiles/")
114114
endif()
115115
download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME fiji_montage.tar.gz SHA512 70139babc838ce3ab1f5adddfddc86dcc51996e614c6c2d757bcb2e59e8ebdc744dac269233494b1ef8d09397aecb4ccca3384f0a91bb017f2cf6309c4ac40fa)
116+
download_test_data(DREAM3D_DATA_DIR ${DREAM3D_DATA_DIR} ARCHIVE_NAME image_flip_test_images.tar.gz SHA512 4e282a270251133004bf4b979d0d064631b618fc82f503184c602c40d388b725f81faf8e77654d285852acc3217d51534c9a71240be4a87a91dc46da7871e7d2)
116117

117118
endif()
118119

src/Plugins/ITKImageProcessing/test/ITKImportImageStackTest.cpp

Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#include <catch2/catch.hpp>
22

3+
#include "ITKImageProcessing/Filters/ITKImageReader.hpp"
34
#include "ITKImageProcessing/Filters/ITKImportImageStack.hpp"
45
#include "ITKImageProcessing/ITKImageProcessing_test_dirs.hpp"
56
#include "ITKTestBase.hpp"
67

78
#include "simplnx/DataStructure/DataArray.hpp"
89
#include "simplnx/DataStructure/Geometry/ImageGeom.hpp"
10+
#include "simplnx/Parameters/ChoicesParameter.hpp"
911
#include "simplnx/Parameters/GeneratedFileListParameter.hpp"
1012
#include "simplnx/UnitTest/UnitTestCommon.hpp"
1113

@@ -20,6 +22,144 @@ namespace
2022
const std::string k_ImageStackDir = unit_test::k_DataDir.str() + "/ImageStack";
2123
const DataPath k_ImageGeomPath = {{"ImageGeometry"}};
2224
const DataPath k_ImageDataPath = k_ImageGeomPath.createChildPath(ImageGeom::k_CellDataName).createChildPath("ImageData");
25+
const std::string k_FlippedImageStackDirName = "image_flip_test_images";
26+
const DataPath k_XGeneratedImageGeomPath = DataPath({"xGeneratedImageGeom"});
27+
const DataPath k_YGeneratedImageGeomPath = DataPath({"yGeneratedImageGeom"});
28+
const DataPath k_XFlipImageGeomPath = DataPath({"xFlipImageGeom"});
29+
const DataPath k_YFlipImageGeomPath = DataPath({"yFlipImageGeom"});
30+
const std::string k_ImageDataName = "ImageData";
31+
const ChoicesParameter::ValueType k_NoImageTransform = 0;
32+
const ChoicesParameter::ValueType k_FlipAboutXAxis = 1;
33+
const ChoicesParameter::ValueType k_FlipAboutYAxis = 2;
34+
const fs::path k_ImageFlipStackDir = fs::path(fmt::format("{}/{}", unit_test::k_TestFilesDir, k_FlippedImageStackDirName));
35+
36+
// Exemplar Array Paths
37+
const DataPath k_XFlippedImageDataPath = k_XFlipImageGeomPath.createChildPath(Constants::k_Cell_Data).createChildPath(::k_ImageDataName);
38+
const DataPath k_YFlippedImageDataPath = k_YFlipImageGeomPath.createChildPath(Constants::k_Cell_Data).createChildPath(::k_ImageDataName);
39+
40+
// Make sure we can instantiate the ITK Import Image Stack Filter
41+
// ITK Image Processing Plugin Uuid
42+
constexpr AbstractPlugin::IdType k_ITKImageProcessingID = *Uuid::FromString("115b0d10-ab97-5a18-88e8-80d35056a28e");
43+
const FilterHandle k_ImportImageStackFilterHandle(nx::core::FilterTraits<ITKImportImageStack>::uuid, k_ITKImageProcessingID);
44+
45+
void ExecuteImportImageStackXY(DataStructure& dataStructure, const std::string& filePrefix)
46+
{
47+
// Filter needs RotateSampleRefFrameFilter to run
48+
Application::GetOrCreateInstance()->loadPlugins(unit_test::k_BuildDir.view(), true);
49+
auto* filterList = nx::core::Application::Instance()->getFilterList();
50+
REQUIRE(filterList != nullptr);
51+
52+
// Define Shared parameters
53+
std::vector<float32> k_Origin = {0.0f, 0.0f, 0.0f};
54+
std::vector<float32> k_Spacing = {1.0f, 1.0f, 1.0f};
55+
GeneratedFileListParameter::ValueType k_FileListInfo;
56+
57+
// Set File list for reads
58+
{
59+
k_FileListInfo.inputPath = k_ImageFlipStackDir.string();
60+
k_FileListInfo.startIndex = 1;
61+
k_FileListInfo.endIndex = 1;
62+
k_FileListInfo.incrementIndex = 1;
63+
k_FileListInfo.fileExtension = ".tiff";
64+
k_FileListInfo.filePrefix = filePrefix;
65+
k_FileListInfo.fileSuffix = "";
66+
k_FileListInfo.paddingDigits = 1;
67+
k_FileListInfo.ordering = GeneratedFileListParameter::Ordering::LowToHigh;
68+
}
69+
70+
// Run generated X flip
71+
{
72+
auto importImageStackFilter = filterList->createFilter(::k_ImportImageStackFilterHandle);
73+
REQUIRE(nullptr != importImageStackFilter);
74+
75+
Arguments args;
76+
77+
args.insertOrAssign(ITKImportImageStack::k_Origin_Key, std::make_any<std::vector<float32>>(k_Origin));
78+
args.insertOrAssign(ITKImportImageStack::k_Spacing_Key, std::make_any<std::vector<float32>>(k_Spacing));
79+
args.insertOrAssign(ITKImportImageStack::k_InputFileListInfo_Key, std::make_any<GeneratedFileListParameter::ValueType>(k_FileListInfo));
80+
args.insertOrAssign(ITKImportImageStack::k_ImageGeometryPath_Key, std::make_any<DataPath>(::k_XGeneratedImageGeomPath));
81+
args.insertOrAssign(ITKImportImageStack::k_ImageTransformChoice_Key, std::make_any<ChoicesParameter::ValueType>(::k_FlipAboutXAxis));
82+
83+
auto preflightResult = importImageStackFilter->preflight(dataStructure, args);
84+
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions)
85+
86+
auto executeResult = importImageStackFilter->execute(dataStructure, args);
87+
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result)
88+
}
89+
90+
// Run generated Y flip
91+
{
92+
auto importImageStackFilter = filterList->createFilter(::k_ImportImageStackFilterHandle);
93+
REQUIRE(nullptr != importImageStackFilter);
94+
95+
Arguments args;
96+
97+
args.insertOrAssign(ITKImportImageStack::k_Origin_Key, std::make_any<std::vector<float32>>(k_Origin));
98+
args.insertOrAssign(ITKImportImageStack::k_Spacing_Key, std::make_any<std::vector<float32>>(k_Spacing));
99+
args.insertOrAssign(ITKImportImageStack::k_InputFileListInfo_Key, std::make_any<GeneratedFileListParameter::ValueType>(k_FileListInfo));
100+
args.insertOrAssign(ITKImportImageStack::k_ImageGeometryPath_Key, std::make_any<DataPath>(::k_YGeneratedImageGeomPath));
101+
args.insertOrAssign(ITKImportImageStack::k_ImageTransformChoice_Key, std::make_any<ChoicesParameter::ValueType>(::k_FlipAboutYAxis));
102+
103+
auto preflightResult = importImageStackFilter->preflight(dataStructure, args);
104+
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions)
105+
106+
auto executeResult = importImageStackFilter->execute(dataStructure, args);
107+
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result)
108+
}
109+
}
110+
111+
void ReadInFlippedXYExemplars(DataStructure& dataStructure, const std::string& filePrefix)
112+
{
113+
{
114+
ITKImageReader filter;
115+
Arguments args;
116+
117+
fs::path filePath = k_ImageFlipStackDir / (filePrefix + "flip_x.tiff");
118+
args.insertOrAssign(ITKImageReader::k_FileName_Key, filePath);
119+
args.insertOrAssign(ITKImageReader::k_ImageGeometryPath_Key, ::k_XFlipImageGeomPath);
120+
args.insertOrAssign(ITKImageReader::k_ImageDataArrayPath_Key, ::k_XFlippedImageDataPath);
121+
122+
auto preflightResult = filter.preflight(dataStructure, args);
123+
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions)
124+
125+
auto executeResult = filter.execute(dataStructure, args);
126+
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result)
127+
}
128+
{
129+
ITKImageReader filter;
130+
Arguments args;
131+
132+
fs::path filePath = k_ImageFlipStackDir / (filePrefix + "flip_y.tiff");
133+
args.insertOrAssign(ITKImageReader::k_FileName_Key, filePath);
134+
args.insertOrAssign(ITKImageReader::k_ImageGeometryPath_Key, ::k_YFlipImageGeomPath);
135+
args.insertOrAssign(ITKImageReader::k_ImageDataArrayPath_Key, ::k_YFlippedImageDataPath);
136+
137+
auto preflightResult = filter.preflight(dataStructure, args);
138+
SIMPLNX_RESULT_REQUIRE_VALID(preflightResult.outputActions)
139+
140+
auto executeResult = filter.execute(dataStructure, args);
141+
SIMPLNX_RESULT_REQUIRE_VALID(executeResult.result)
142+
}
143+
}
144+
145+
void CompareXYFlippedGeometries(DataStructure& dataStructure)
146+
{
147+
UnitTest::CompareImageGeometry(dataStructure, ::k_XFlipImageGeomPath, k_XGeneratedImageGeomPath);
148+
UnitTest::CompareImageGeometry(dataStructure, ::k_YFlipImageGeomPath, k_YGeneratedImageGeomPath);
149+
150+
// Processed
151+
DataPath k_XGeneratedImageDataPath = k_XGeneratedImageGeomPath.createChildPath(Constants::k_Cell_Data).createChildPath(::k_ImageDataName);
152+
DataPath k_YGeneratedImageDataPath = k_YGeneratedImageGeomPath.createChildPath(Constants::k_Cell_Data).createChildPath(::k_ImageDataName);
153+
const auto& xGeneratedImageData = dataStructure.getDataRefAs<UInt8Array>(k_XGeneratedImageDataPath);
154+
const auto& yGeneratedImageData = dataStructure.getDataRefAs<UInt8Array>(k_YGeneratedImageDataPath);
155+
156+
// Exemplar
157+
const auto& xFlippedImageData = dataStructure.getDataRefAs<UInt8Array>(k_XFlippedImageDataPath);
158+
const auto& yFlippedImageData = dataStructure.getDataRefAs<UInt8Array>(k_YFlippedImageDataPath);
159+
160+
UnitTest::CompareDataArrays<uint8>(xGeneratedImageData, xFlippedImageData);
161+
UnitTest::CompareDataArrays<uint8>(yGeneratedImageData, yFlippedImageData);
162+
}
23163
} // namespace
24164

25165
TEST_CASE("ITKImageProcessing::ITKImportImageStack: NoInput", "[ITKImageProcessing][ITKImportImageStack]")
@@ -154,3 +294,91 @@ TEST_CASE("ITKImageProcessing::ITKImportImageStack: CompareImage", "[ITKImagePro
154294
const std::string md5Hash = ITKTestBase::ComputeMd5Hash(dataStructure, k_ImageDataPath);
155295
REQUIRE(md5Hash == "2620b39f0dcaa866602c2591353116a4");
156296
}
297+
298+
TEST_CASE("ITKImageProcessing::ITKImportImageStack: Flipped Image Even-Even X/Y", "[ITKImageProcessing][ITKImportImageStack]")
299+
{
300+
const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "image_flip_test_images.tar.gz", k_FlippedImageStackDirName);
301+
302+
const std::string k_FilePrefix = "image_flip_even_even_";
303+
304+
DataStructure dataStructure;
305+
306+
// Generate XY Image Geometries with ITKImportImageStack
307+
::ExecuteImportImageStackXY(dataStructure, k_FilePrefix);
308+
309+
// Read in exemplars
310+
::ReadInFlippedXYExemplars(dataStructure, k_FilePrefix);
311+
312+
#ifdef SIMPLNX_WRITE_TEST_OUTPUT
313+
UnitTest::WriteTestDataStructure(dataStructure, fmt::format("{}/even_even_import_image_stack_test.dream3d", unit_test::k_BinaryTestOutputDir));
314+
#endif
315+
316+
// Compare against exemplars
317+
::CompareXYFlippedGeometries(dataStructure);
318+
}
319+
320+
TEST_CASE("ITKImageProcessing::ITKImportImageStack: Flipped Image Even-Odd X/Y", "[ITKImageProcessing][ITKImportImageStack]")
321+
{
322+
const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "image_flip_test_images.tar.gz", k_FlippedImageStackDirName);
323+
324+
const std::string k_FilePrefix = "image_flip_even_odd_";
325+
326+
DataStructure dataStructure;
327+
328+
// Generate XY Image Geometries with ITKImportImageStack
329+
::ExecuteImportImageStackXY(dataStructure, k_FilePrefix);
330+
331+
// Read in exemplars
332+
::ReadInFlippedXYExemplars(dataStructure, k_FilePrefix);
333+
334+
#ifdef SIMPLNX_WRITE_TEST_OUTPUT
335+
UnitTest::WriteTestDataStructure(dataStructure, fmt::format("{}/even_odd_import_image_stack_test.dream3d", unit_test::k_BinaryTestOutputDir));
336+
#endif
337+
338+
// Compare against exemplars
339+
::CompareXYFlippedGeometries(dataStructure);
340+
}
341+
342+
TEST_CASE("ITKImageProcessing::ITKImportImageStack: Flipped Image Odd-Even X/Y", "[ITKImageProcessing][ITKImportImageStack]")
343+
{
344+
const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "image_flip_test_images.tar.gz", k_FlippedImageStackDirName);
345+
346+
const std::string k_FilePrefix = "image_flip_odd_even_";
347+
348+
DataStructure dataStructure;
349+
350+
// Generate XY Image Geometries with ITKImportImageStack
351+
::ExecuteImportImageStackXY(dataStructure, k_FilePrefix);
352+
353+
// Read in exemplars
354+
::ReadInFlippedXYExemplars(dataStructure, k_FilePrefix);
355+
356+
#ifdef SIMPLNX_WRITE_TEST_OUTPUT
357+
UnitTest::WriteTestDataStructure(dataStructure, fmt::format("{}/odd_even_import_image_stack_test.dream3d", unit_test::k_BinaryTestOutputDir));
358+
#endif
359+
360+
// Compare against exemplars
361+
::CompareXYFlippedGeometries(dataStructure);
362+
}
363+
364+
TEST_CASE("ITKImageProcessing::ITKImportImageStack: Flipped Image Odd-Odd X/Y", "[ITKImageProcessing][ITKImportImageStack]")
365+
{
366+
const nx::core::UnitTest::TestFileSentinel testDataSentinel(nx::core::unit_test::k_CMakeExecutable, nx::core::unit_test::k_TestFilesDir, "image_flip_test_images.tar.gz", k_FlippedImageStackDirName);
367+
368+
const std::string k_FilePrefix = "image_flip_odd_odd_";
369+
370+
DataStructure dataStructure;
371+
372+
// Generate XY Image Geometries with ITKImportImageStack
373+
::ExecuteImportImageStackXY(dataStructure, k_FilePrefix);
374+
375+
// Read in exemplars
376+
::ReadInFlippedXYExemplars(dataStructure, k_FilePrefix);
377+
378+
#ifdef SIMPLNX_WRITE_TEST_OUTPUT
379+
UnitTest::WriteTestDataStructure(dataStructure, fmt::format("{}/odd_odd_import_image_stack_test.dream3d", unit_test::k_BinaryTestOutputDir));
380+
#endif
381+
382+
// Compare against exemplars
383+
::CompareXYFlippedGeometries(dataStructure);
384+
}

0 commit comments

Comments
 (0)