From 8f32d71c8aaa588245c640df3f78b4034f0f012a Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Wed, 15 May 2024 10:26:41 -0400 Subject: [PATCH 1/4] BUG: Check output of StatisticsUniqueLabelMapFilterTest1 Check the label maps are none overlapping. Overlapping labels are causing thread race conditions in the LabelMapToLabelImage filter. --- .../include/itkShapeUniqueLabelMapFilter.h | 2 + ...itkStatisticsUniqueLabelMapFilterTest1.cxx | 44 ++++++++++++++++++- 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h b/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h index 9fdaf98df0a..49cee797aad 100644 --- a/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h +++ b/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h @@ -177,6 +177,8 @@ class ITK_TEMPLATE_EXPORT ShapeUniqueLabelMapFilter : public InPlaceLabelMapFilt } } + assert(newMainLine || (!newMainLine && idx[0] >= prevIdx[0])); + if (newMainLine) { // just push the line diff --git a/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx b/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx index c98f058a9ba..ccb162f83b7 100644 --- a/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx +++ b/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx @@ -29,6 +29,39 @@ #include "itkGrayscaleDilateImageFilter.h" #include "itkObjectByObjectLabelMapFilter.h" +template +int +CheckLabelMapOverlap(TLabelMap * labelMap) +{ + int exitCode = EXIT_SUCCESS; + + for (auto & labelObject : labelMap->GetLabelObjects()) + { + // Manually check each label object against all other label objects, to ensure that no two label objects share an + // index. + for (itk::SizeValueType lineNumber = 0; lineNumber < labelObject->GetNumberOfLines(); ++lineNumber) + { + auto line = labelObject->GetLine(lineNumber); + auto idx = line.GetIndex(); + ITK_TEST_EXPECT_TRUE(line.GetLength() <= labelObject->GetNumberOfPixels()); + for (itk::SizeValueType lengthIndex = 0; lengthIndex < line.GetLength(); ++lengthIndex) + { + for (auto & checkObject : labelMap->GetLabelObjects()) + { + if (checkObject != labelObject && checkObject->HasIndex(idx)) + { + std::cerr << "Label: " << int(labelObject->GetLabel()) << " and " << int(checkObject->GetLabel()) + << " has index " << idx << std::endl; + exitCode = EXIT_FAILURE; + } + } + ++idx[0]; + } + } + } + return exitCode; +} + int itkStatisticsUniqueLabelMapFilterTest1(int argc, char * argv[]) { @@ -123,6 +156,15 @@ itkStatisticsUniqueLabelMapFilterTest1(int argc, char * argv[]) itk::SimpleFilterWatcher watcher(unique, "filter"); + ITK_TRY_EXPECT_NO_EXCEPTION(unique->Update()); + + int exitCode = CheckLabelMapOverlap(unique->GetOutput()); + + if (exitCode == EXIT_FAILURE) + { + std::cerr << "Overlap detected in the label map." << std::endl; + } + using LabelMapToImageFilterType = itk::LabelMapToLabelImageFilter; auto labelMapToImageFilter = LabelMapToImageFilterType::New(); labelMapToImageFilter->SetInput(unique->GetOutput()); @@ -144,5 +186,5 @@ itkStatisticsUniqueLabelMapFilterTest1(int argc, char * argv[]) ITK_TRY_EXPECT_NO_EXCEPTION(writer->Update()); - return EXIT_SUCCESS; + return exitCode; } From 00bfdb80e79ba372d06758e1cc0e97db21345fb3 Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 16 May 2024 08:50:22 -0400 Subject: [PATCH 2/4] BUG: Address bugs in unique label map filters Two bugs were addressed which could cause overlapping and/or errouneous outputs. 1. When a line we trimmed in the front, it is not placed back into the queue to ensure that it is still the first one, and the asserted assumptions of the order remain true. 2. Fix comparisons between start and ends to be correct for an interval of pixels where the start+length is not included. --- .../itkAttributeUniqueLabelMapFilter.hxx | 18 +++++++++++++----- .../include/itkShapeUniqueLabelMapFilter.h | 18 ++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Modules/Filtering/LabelMap/include/itkAttributeUniqueLabelMapFilter.hxx b/Modules/Filtering/LabelMap/include/itkAttributeUniqueLabelMapFilter.hxx index 133d1266c57..11545e267bf 100644 --- a/Modules/Filtering/LabelMap/include/itkAttributeUniqueLabelMapFilter.hxx +++ b/Modules/Filtering/LabelMap/include/itkAttributeUniqueLabelMapFilter.hxx @@ -111,6 +111,8 @@ AttributeUniqueLabelMapFilter::GenerateData() } } + assert(newMainLine || (idx[0] >= prevIdx[0])); + if (newMainLine) { // just push the line @@ -121,7 +123,7 @@ AttributeUniqueLabelMapFilter::GenerateData() OffsetValueType prevLength = prev.line.GetLength(); OffsetValueType length = l.line.GetLength(); - if (prevIdx[0] + prevLength >= idx[0]) + if (prevIdx[0] + prevLength > idx[0]) { // the lines are overlapping. We need to choose which line to keep. // the label, the only "attribute" to be guaranteed to be unique, is @@ -193,7 +195,7 @@ AttributeUniqueLabelMapFilter::GenerateData() // keep the previous one. If the previous line fully overlap the // current one, // the current one is fully discarded. - if (prevIdx[0] + prevLength > idx[0] + length) + if (prevIdx[0] + prevLength >= idx[0] + length) { // discarding the current line - just do nothing } @@ -202,9 +204,15 @@ AttributeUniqueLabelMapFilter::GenerateData() IndexType newIdx = idx; newIdx[0] = prevIdx[0] + prevLength; OffsetValueType newLength = idx[0] + length - newIdx[0]; - l.line.SetIndex(newIdx); - l.line.SetLength(newLength); - lines.push_back(l); + + if (newLength > 0) + { + l.line.SetIndex(newIdx); + l.line.SetLength(newLength); + // The front of this line is trimmed, it may occur after a line in the queue + // so the queue is used for the proper ordering. + pq.push(l); + } } } } diff --git a/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h b/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h index 49cee797aad..8ff188ab566 100644 --- a/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h +++ b/Modules/Filtering/LabelMap/include/itkShapeUniqueLabelMapFilter.h @@ -177,7 +177,7 @@ class ITK_TEMPLATE_EXPORT ShapeUniqueLabelMapFilter : public InPlaceLabelMapFilt } } - assert(newMainLine || (!newMainLine && idx[0] >= prevIdx[0])); + assert(newMainLine || (idx[0] >= prevIdx[0])); if (newMainLine) { @@ -189,7 +189,7 @@ class ITK_TEMPLATE_EXPORT ShapeUniqueLabelMapFilter : public InPlaceLabelMapFilt OffsetValueType prevLength = prev.line.GetLength(); OffsetValueType length = l.line.GetLength(); - if (prevIdx[0] + prevLength >= idx[0]) + if (prevIdx[0] + prevLength > idx[0]) { // the lines are overlapping. We need to choose which line to keep. // the label, the only "attribute" to be guaranteed to be unique, is @@ -246,6 +246,7 @@ class ITK_TEMPLATE_EXPORT ShapeUniqueLabelMapFilter : public InPlaceLabelMapFilt prevLength = idx[0] - prevIdx[0]; if (prevLength != 0) { + assert(prevIdx[0] <= idx[0]); lines.back().line.SetLength(idx[0] - prevIdx[0]); } else @@ -261,7 +262,7 @@ class ITK_TEMPLATE_EXPORT ShapeUniqueLabelMapFilter : public InPlaceLabelMapFilt // keep the previous one. If the previous line fully overlap the // current one, // the current one is fully discarded. - if (prevIdx[0] + prevLength > idx[0] + length) + if (prevIdx[0] + prevLength >= idx[0] + length) { // discarding the current line - just do nothing } @@ -270,9 +271,14 @@ class ITK_TEMPLATE_EXPORT ShapeUniqueLabelMapFilter : public InPlaceLabelMapFilt IndexType newIdx = idx; newIdx[0] = prevIdx[0] + prevLength; OffsetValueType newLength = idx[0] + length - newIdx[0]; - l.line.SetIndex(newIdx); - l.line.SetLength(newLength); - lines.push_back(l); + if (newLength > 0) + { + l.line.SetIndex(newIdx); + l.line.SetLength(newLength); + // The front of this line is trimmed, it may occur after a line in the queue + // so the queue is used for the proper ordering. + priorityQueue.push(l); + } } } } From 790af9eba01a8dc9187daf8898cee07c2efe373b Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Thu, 16 May 2024 12:01:55 -0400 Subject: [PATCH 3/4] BUG: Remove dilation output in test --- Modules/Filtering/LabelMap/test/CMakeLists.txt | 8 -------- .../itkStatisticsUniqueLabelMapFilterTest1.cxx | 18 ++++-------------- 2 files changed, 4 insertions(+), 22 deletions(-) diff --git a/Modules/Filtering/LabelMap/test/CMakeLists.txt b/Modules/Filtering/LabelMap/test/CMakeLists.txt index dac23bd4f7c..32008f30ebc 100644 --- a/Modules/Filtering/LabelMap/test/CMakeLists.txt +++ b/Modules/Filtering/LabelMap/test/CMakeLists.txt @@ -1569,14 +1569,10 @@ itk_add_test( --compare DATA{Baseline/cthead1Label-label-statistics-unique-labelmap-baseline1.png} ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterTest1.png - --compare - DATA{Baseline/cthead1Label-label-statistics-unique-labelmap-dilate-baseline1.png} - ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterDilationStability1.png itkStatisticsUniqueLabelMapFilterTest1 DATA{${ITK_DATA_ROOT}/Input/cthead1Label.png} DATA{${ITK_DATA_ROOT}/Input/cthead1.png} ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterTest1.png - ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterDilationStability1.png 0 100) itk_add_test( @@ -1587,16 +1583,12 @@ itk_add_test( --compare DATA{Baseline/cthead1Label-label-statistics-unique-labelmap-baseline2.png} ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterTest2.png - --compare - DATA{Baseline/cthead1Label-label-statistics-unique-labelmap-dilate-baseline2.png} - ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterDilationStability2.png --compareNumberOfPixelsTolerance 35 itkStatisticsUniqueLabelMapFilterTest1 DATA{${ITK_DATA_ROOT}/Input/cthead1Label.png} DATA{${ITK_DATA_ROOT}/Input/cthead1.png} ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterTest2.png - ${ITK_TEST_OUTPUT_DIR}/itkStatisticsUniqueLabelMapFilterDilationStability2.png 1 100) diff --git a/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx b/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx index ccb162f83b7..5d0a660fd1e 100644 --- a/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx +++ b/Modules/Filtering/LabelMap/test/itkStatisticsUniqueLabelMapFilterTest1.cxx @@ -65,13 +65,11 @@ CheckLabelMapOverlap(TLabelMap * labelMap) int itkStatisticsUniqueLabelMapFilterTest1(int argc, char * argv[]) { - // ToDo: remove dilationOutput once the JIRA issue 3370 has been solved - // Then, argc != 6 - if (argc != 7) + if (argc != 6) { std::cerr << "Missing parameters." << std::endl; std::cerr << "Usage: " << itkNameOfTestExecutableMacro(argv); - std::cerr << " input feature output dilationOutput"; + std::cerr << " input feature output"; std::cerr << " reverseOrdering attribute"; std::cerr << std::endl; return EXIT_FAILURE; @@ -79,9 +77,8 @@ itkStatisticsUniqueLabelMapFilterTest1(int argc, char * argv[]) const char * inputImage = argv[1]; const char * featureImage = argv[2]; const char * outputImage = argv[3]; - const char * dilationOutput = argv[4]; - const bool reverseOrdering = std::stoi(argv[5]); - const unsigned int attribute = std::stoi(argv[6]); + const bool reverseOrdering = std::stoi(argv[4]); + const unsigned int attribute = std::stoi(argv[5]); constexpr unsigned int Dimension = 2; @@ -177,13 +174,6 @@ itkStatisticsUniqueLabelMapFilterTest1(int argc, char * argv[]) ITK_TRY_EXPECT_NO_EXCEPTION(writer->Update()); - - // WARNING: TEMPORARY: JIRA ISSUE 3370 - // Writing an additional output of just the dilated label - writer->SetInput(grayscaleDilateFilter->GetOutput()); - writer->SetFileName(dilationOutput); - writer->UseCompressionOn(); - ITK_TRY_EXPECT_NO_EXCEPTION(writer->Update()); return exitCode; From 47eec5a2a2b9638035aba1c4f697aef037d48daa Mon Sep 17 00:00:00 2001 From: Bradley Lowekamp Date: Fri, 17 May 2024 15:16:09 -0400 Subject: [PATCH 4/4] ENH: A GTest for LabelUniqueLabelMapFilter --- .../LabelMap/ITKKWStyleOverwrite.txt | 1 + .../Filtering/LabelMap/test/CMakeLists.txt | 4 +- .../test/itkUniqueLabelMapFiltersGTest.cxx | 309 ++++++++++++++++++ 3 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 Modules/Filtering/LabelMap/test/itkUniqueLabelMapFiltersGTest.cxx diff --git a/Modules/Filtering/LabelMap/ITKKWStyleOverwrite.txt b/Modules/Filtering/LabelMap/ITKKWStyleOverwrite.txt index 833fbdf5696..3ea0f336e71 100644 --- a/Modules/Filtering/LabelMap/ITKKWStyleOverwrite.txt +++ b/Modules/Filtering/LabelMap/ITKKWStyleOverwrite.txt @@ -2,4 +2,5 @@ test/*\.cxx Namespace Disable test/itkAttributePositionLabelMapFilterTest1.cxx Namespace Disable test/itkShapeLabelMapFilterGTest.cxx Namespace Disable test/itkStatisticsLabelMapFilterGTest.cxx Namespace Disable +test/itkUniqueLabelMapFiltersGTest.cxx Namespace Disable test/itkShapeLabelObjectAccessorsTest1.cxx SemicolonSpace Disable diff --git a/Modules/Filtering/LabelMap/test/CMakeLists.txt b/Modules/Filtering/LabelMap/test/CMakeLists.txt index 32008f30ebc..0e15f959ea8 100644 --- a/Modules/Filtering/LabelMap/test/CMakeLists.txt +++ b/Modules/Filtering/LabelMap/test/CMakeLists.txt @@ -1592,6 +1592,8 @@ itk_add_test( 1 100) -set(ITKLabelMapGTests itkShapeLabelMapFilterGTest.cxx itkStatisticsLabelMapFilterGTest.cxx) +set(ITKLabelMapGTests itkShapeLabelMapFilterGTest.cxx + itkStatisticsLabelMapFilterGTest.cxx + itkUniqueLabelMapFiltersGTest.cxx) creategoogletestdriver(ITKLabelMap "${ITKLabelMap-Test_LIBRARIES}" "${ITKLabelMapGTests}") diff --git a/Modules/Filtering/LabelMap/test/itkUniqueLabelMapFiltersGTest.cxx b/Modules/Filtering/LabelMap/test/itkUniqueLabelMapFiltersGTest.cxx new file mode 100644 index 00000000000..ae1c3957cfb --- /dev/null +++ b/Modules/Filtering/LabelMap/test/itkUniqueLabelMapFiltersGTest.cxx @@ -0,0 +1,309 @@ +/*========================================================================= + * + * Copyright NumFOCUS + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0.txt + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + *=========================================================================*/ + +#include "itkGTest.h" + +#include "itkImage.h" +#include "itkLabelImageToStatisticsLabelMapFilter.h" +#include "itkImageFileWriter.h" +#include + + +#include "itkLabelImageToLabelMapFilter.h" +#include "itkObjectByObjectLabelMapFilter.h" +#include "itkShapeLabelObjectAccessors.h" +#include "itkFlatStructuringElement.h" +#include "itkBinaryDilateImageFilter.h" +#include "itkLabelUniqueLabelMapFilter.h" +#include "itkTestingHashImageFilter.h" +#include "itksys/SystemTools.hxx" +#include "itkTestDriverIncludeRequiredFactories.h" + + +namespace +{ + +class UniqueLabelMapFixture : public ::testing::Test +{ +public: + UniqueLabelMapFixture() = default; + ~UniqueLabelMapFixture() override = default; + +protected: + void + SetUp() override + { + RegisterRequiredFactories(); + } + void + TearDown() override + {} + + std::string + GetTestName() const + { + return ::testing::UnitTest::GetInstance()->current_test_info()->name(); + } + + template + struct FixtureUtilities + { + static const unsigned int Dimension = D; + + using LabelPixelType = TPixelType; + using LabelImageType = itk::Image; + using IndexType = typename LabelImageType::IndexType; + + using LabelObjectType = itk::StatisticsLabelObject; + + using LabelMapType = itk::LabelMap>; + + + static typename LabelImageType::Pointer + CreateLabelImage(const std::vector & indices) + { + const size_t size = 25; + auto image = LabelImageType::New(); + + typename LabelImageType::SizeType imageSize; + imageSize.Fill(size); + image->SetRegions(typename LabelImageType::RegionType(imageSize)); + image->Allocate(); + image->FillBuffer(0); + + for (LabelPixelType id = 0; id < indices.size(); ++id) + { + image->SetPixel(indices[id], id + 1); + } + return image; + } + + static typename LabelMapType::Pointer + LabelMapFromLabelImage(const LabelImageType * image, unsigned int dilateRadius = 0) + { + using ToLabelMapType = itk::LabelImageToLabelMapFilter; + auto toLabelMap = ToLabelMapType::New(); + toLabelMap->SetInput(image); + + if (dilateRadius == 0) + { + toLabelMap->Update(); + return toLabelMap->GetOutput(); + } + + using KernelType = itk::FlatStructuringElement; + using DilateType = itk::BinaryDilateImageFilter; + auto dilate = DilateType::New(); + typename KernelType::SizeType rad; + rad.Fill(dilateRadius); + dilate->SetKernel(KernelType::Ball(rad)); + + + using OIType = itk::ObjectByObjectLabelMapFilter; + auto oi = OIType::New(); + oi->SetInput(toLabelMap->GetOutput()); + oi->SetFilter(dilate); + oi->SetPadSize(rad); + + oi->Update(); + + return oi->GetOutput(); + } + }; + + template + static typename itk::Image::Pointer + LabelMapToLabelImage(TLabelMap * labelMap) + { + using ImageType = itk::Image; + using L2IType = itk::LabelMapToLabelImageFilter; + auto l2i = L2IType::New(); + l2i->SetInput(labelMap); + l2i->Update(); + return l2i->GetOutput(); + } + + + template + void + CheckLabelMapOverlap(TLabelMap * labelMap) + { + for (auto & labelObject : labelMap->GetLabelObjects()) + { + // Manually check each label object against all other label objects, to ensure that no two label objects share an + // index. + for (itk::SizeValueType lineNumber = 0; lineNumber < labelObject->GetNumberOfLines(); ++lineNumber) + { + auto line = labelObject->GetLine(lineNumber); + auto idx = line.GetIndex(); + ASSERT_LE(line.GetLength(), labelObject->Size()); + for (itk::SizeValueType lengthIndex = 0; lengthIndex < line.GetLength(); ++lengthIndex) + { + for (auto & checkObject : labelMap->GetLabelObjects()) + { + if (checkObject != labelObject) + { + EXPECT_FALSE(checkObject->HasIndex(idx)) + << "Label: " << int(labelObject->GetLabel()) << " and " << int(checkObject->GetLabel()) << " has index " + << idx << std::endl; + } + } + ++idx[0]; + } + } + } + } + + template + static std::string + MD5Hash(const TImageType * image) + { + + using HashFilter = itk::Testing::HashImageFilter; + auto hasher = HashFilter::New(); + hasher->SetInput(image); + hasher->InPlaceOff(); + hasher->Update(); + return hasher->GetHash(); + } +}; +} // namespace + + +TEST_F(UniqueLabelMapFixture, EmptyImage) +{ + const std::vector::IndexType> indices = {}; + auto image = FixtureUtilities<2>::CreateLabelImage(indices); + auto labelMap = FixtureUtilities<2>::LabelMapFromLabelImage(image.GetPointer(), 15); + + auto filter = itk::LabelUniqueLabelMapFilter::New(); + filter->SetInput(labelMap); + + CheckLabelMapOverlap(filter->GetOutput()); + filter->Update(); + + auto out = LabelMapToLabelImage(filter->GetOutput()); + + // check the hash of out, should be all zeros + EXPECT_EQ(MD5Hash(out.GetPointer()), "393017b9101a884b66d64849d99a7d05"); +} + + +TEST_F(UniqueLabelMapFixture, OneLabel) +{ + const std::vector::IndexType> indices = { { 10, 10 } }; + auto image = FixtureUtilities<2>::CreateLabelImage(indices); + auto labelMap = FixtureUtilities<2>::LabelMapFromLabelImage(image.GetPointer(), 0); + + auto filter = itk::LabelUniqueLabelMapFilter::New(); + filter->SetInput(labelMap); + filter->Update(); + + CheckLabelMapOverlap(filter->GetOutput()); + + auto out = LabelMapToLabelImage(filter->GetOutput()); + + EXPECT_EQ(MD5Hash(out.GetPointer()), MD5Hash(image.GetPointer())); + EXPECT_EQ(MD5Hash(out.GetPointer()), "9c8ee8f2fe887fd6d2393d6416df3fb6"); +} + + +TEST_F(UniqueLabelMapFixture, OnesLabel) +{ + const std::vector::IndexType> indices = { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 }, + { 0, 4 }, { 2, 4 }, { 0, 5 } }; + auto image = FixtureUtilities<2>::CreateLabelImage(indices); + auto labelMap = FixtureUtilities<2>::LabelMapFromLabelImage(image.GetPointer(), 0); + + auto filter = itk::LabelUniqueLabelMapFilter::New(); + filter->SetInput(labelMap); + filter->Update(); + + CheckLabelMapOverlap(filter->GetOutput()); + + auto out = LabelMapToLabelImage(filter->GetOutput()); + + EXPECT_EQ(MD5Hash(out.GetPointer()), MD5Hash(image.GetPointer())); + EXPECT_EQ(MD5Hash(out.GetPointer()), "220048b56395d98a8f20a5b1733bdde6"); +} + + +TEST_F(UniqueLabelMapFixture, Dilate1) +{ + const std::vector::IndexType> indices = { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 }, + { 0, 4 }, { 2, 4 }, { 0, 10 } }; + auto image = FixtureUtilities<2>::CreateLabelImage(indices); + auto labelMap = FixtureUtilities<2>::LabelMapFromLabelImage(image.GetPointer(), 1); + + auto filter = itk::LabelUniqueLabelMapFilter::New(); + filter->SetInput(labelMap); + filter->InPlaceOff(); + filter->ReverseOrderingOff(); + filter->Update(); + + CheckLabelMapOverlap(filter->GetOutput()); + + auto out = LabelMapToLabelImage(filter->GetOutput()); + + EXPECT_EQ(MD5Hash(out.GetPointer()), "8bfc8570ee203fa68be18fe055cf389d"); + itk::WriteImage(out.GetPointer(), GetTestName() + "_off.png"); + + filter->ReverseOrderingOn(); + filter->Update(); + + CheckLabelMapOverlap(filter->GetOutput()); + + out = LabelMapToLabelImage(filter->GetOutput()); + + EXPECT_EQ(MD5Hash(out.GetPointer()), "bef76c79168969548f1a8090d46b5f7e"); + itk::WriteImage(out.GetPointer(), GetTestName() + "_on.png"); +} + + +TEST_F(UniqueLabelMapFixture, Dilate2) +{ + const std::vector::IndexType> indices = { { 0, 0 }, { 1, 0 }, { 2, 0 }, { 3, 0 }, + { 0, 4 }, { 2, 4 }, { 0, 5 } }; + auto image = FixtureUtilities<2>::CreateLabelImage(indices); + auto labelMap = FixtureUtilities<2>::LabelMapFromLabelImage(image.GetPointer(), 2); + + auto filter = itk::LabelUniqueLabelMapFilter::New(); + filter->SetInput(labelMap); + filter->InPlaceOff(); + filter->ReverseOrderingOff(); + filter->Update(); + + CheckLabelMapOverlap(filter->GetOutput()); + + auto out = LabelMapToLabelImage(filter->GetOutput()); + + EXPECT_EQ(MD5Hash(out.GetPointer()), "02ec62c193413dd82cb0feaee1ee0b12"); + itk::WriteImage(out.GetPointer(), GetTestName() + "_off.png"); + + + filter->ReverseOrderingOn(); + filter->Update(); + + CheckLabelMapOverlap(filter->GetOutput()); + + out = LabelMapToLabelImage(filter->GetOutput()); + + EXPECT_EQ(MD5Hash(out.GetPointer()), "1c6901199b01ac095511792f447578a3"); + + itk::WriteImage(out.GetPointer(), GetTestName() + "_on.png"); +}