diff --git a/Modules/Classification/CLActiveLearning/include/mitkActiveLearningSuggestRegionFilter.h b/Modules/Classification/CLActiveLearning/include/mitkActiveLearningSuggestRegionFilter.h index 4113c8d293..9615570182 100644 --- a/Modules/Classification/CLActiveLearning/include/mitkActiveLearningSuggestRegionFilter.h +++ b/Modules/Classification/CLActiveLearning/include/mitkActiveLearningSuggestRegionFilter.h @@ -1,115 +1,149 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkActiveLearningSuggestRegionFilter_h #define mitkActiveLearningSuggestRegionFilter_h #include // ITK #include #include #include #include #include +#include #include +#include // MITK #include #include namespace mitk { /** \class ActiveLearningSuggestRegionFilter - * \brief Filter that takes an image of prediction uncertainties and outputs an image containing only the most uncertain region + * \brief Filter that takes an image of prediction uncertainties and outputs an image (mask) containing only the most uncertain region * * This is just a composite filter using itkThresholdImageFilter and mitkSelectHighestUncertaintyRegionFilter */ - template - class MITKCLACTIVELEARNING_EXPORT ActiveLearningSuggestRegionFilter : public itk::ImageToImageFilter + template + class MITKCLACTIVELEARNING_EXPORT ActiveLearningSuggestRegionFilter : public itk::ImageToImageFilter { public: - typedef itk::ImageToImageFilter SuperClassName; + typedef itk::ImageToImageFilter SuperClassName; mitkClassMacroItkParent(ActiveLearningSuggestRegionFilter, SuperClassName) itkNewMacro(Self) typedef typename InputImageType::PixelType InputImagePixelType; + typedef typename OutputImageType::PixelType OutputImagePixelType; // We're not holding threshold as a member, so no set and get macro void SetThreshold(InputImagePixelType threshold) { - if (m_ThresholdFilter->GetLower() != threshold) + if (m_ThresholdFilter->GetLowerThreshold() != threshold) { - m_ThresholdFilter->SetLower(threshold); + m_ThresholdFilter->SetLowerThreshold(threshold); this->Modified(); } } InputImagePixelType GetThreshold() const { - return m_ThresholdFilter->GetLower(); + return m_ThresholdFilter->GetLowerThreshold(); } protected: ActiveLearningSuggestRegionFilter() { - m_ThresholdFilter = itk::ThresholdImageFilter::New(); - m_RegionFilter = mitk::SelectHighestUncertaintyRegionFilter::New(); - - m_ThresholdFilter->SetLower(0.5); - m_RegionFilter->SetInput(m_ThresholdFilter->GetOutput()); + m_ThresholdFilter = itk::BinaryThresholdImageFilter::New(); + m_CCFilter = itk::ConnectedComponentImageFilter::New(); + m_ThresholdOutputFilter = itk::BinaryThresholdImageFilter::New(); + + m_ThresholdFilter->SetLowerThreshold(0.5); + m_ThresholdFilter->SetOutsideValue(0); + m_ThresholdFilter->SetInsideValue(1); + m_ThresholdOutputFilter->SetOutsideValue(0); + m_ThresholdOutputFilter->SetInsideValue(1); } ~ActiveLearningSuggestRegionFilter(){} virtual void GenerateData() override { // Let this filter's input be threshold filter's input auto input = InputImageType::New(); input->Graft(const_cast(this->GetInput())); m_ThresholdFilter->SetInput(input); m_ThresholdFilter->Modified(); - // Region filter takes this filter's output, updates, gives it back - m_RegionFilter->GraftOutput(this->GetOutput()); - m_RegionFilter->Update(); - this->GraftOutput(m_RegionFilter->GetOutput()); + // Run pipeline to get image with connected components + m_CCFilter->SetInput(m_ThresholdFilter->GetOutput()); + m_CCFilter->Update(); + + // Find sum of uncertainties for all connected components + int ccCount = static_cast(m_CCFilter->GetObjectCount()); + std::map ccSize; + for (int a=1; a<=ccCount; ++a) {ccSize[a] = 0.0;} + auto inputIt = itk::ImageRegionConstIterator(input, input->GetLargestPossibleRegion()); + auto outputIt = itk::ImageRegionConstIterator(m_CCFilter->GetOutput(), m_CCFilter->GetOutput()->GetLargestPossibleRegion()); + while (!outputIt.IsAtEnd()) + { + OutputImagePixelType ccVal = outputIt.Get(); + InputImagePixelType pixelVal = inputIt.Get(); + if (ccVal != 0) {ccSize[ccVal] += pixelVal;} + ++outputIt; + ++inputIt; + } + + // Find component with largest sum + typedef typename std::map::value_type PairType; + auto maxComponent = std::max_element(ccSize.begin(), ccSize.end(), [](const PairType& lhs, const PairType& rhs){return lhs.second < rhs.second;}); + + // Create mask + m_ThresholdOutputFilter->SetLowerThreshold(maxComponent->first); + m_ThresholdOutputFilter->SetUpperThreshold(maxComponent->first); + m_ThresholdOutputFilter->SetInput(m_CCFilter->GetOutput()); + m_ThresholdOutputFilter->GraftOutput(this->GetOutput()); + m_ThresholdOutputFilter->Update(); + this->GraftOutput(m_ThresholdOutputFilter->GetOutput()); } // virtual void PrintSelf(std::ostream & os, itk::Indent indent) const // { // Superclass::PrintSelf(os, indent); // m_ThresholdFilter->PrintSelf(os, indent); // m_RegionFilter->PrintSelf(os, indent); // } private: // ITK examples do this... ActiveLearningSuggestRegionFilter(const Self&); void operator=(const Self&); - typename itk::ThresholdImageFilter::Pointer m_ThresholdFilter; - typename mitk::SelectHighestUncertaintyRegionFilter::Pointer m_RegionFilter; + typename itk::BinaryThresholdImageFilter::Pointer m_ThresholdFilter; + typename itk::ConnectedComponentImageFilter::Pointer m_CCFilter; + typename itk::BinaryThresholdImageFilter::Pointer m_ThresholdOutputFilter; }; } #endif diff --git a/Modules/Classification/CLActiveLearning/test/mitkActiveLearningTest.cpp b/Modules/Classification/CLActiveLearning/test/mitkActiveLearningTest.cpp index 4f7a7bffa1..9cb7db4754 100644 --- a/Modules/Classification/CLActiveLearning/test/mitkActiveLearningTest.cpp +++ b/Modules/Classification/CLActiveLearning/test/mitkActiveLearningTest.cpp @@ -1,147 +1,190 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // ITK #include #include #include +#include +#include +#include +#include // MITK #include #include #include #include // To be tested #include #include #include #include #include class mitkActiveLearningTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkActiveLearningTestSuite); MITK_TEST(SuggestRegionFilterTest); CPPUNIT_TEST_SUITE_END(); public: typedef itk::Image ImageType; + typedef itk::Image BinaryImageType; typedef typename ImageType::PixelType PixelType; typedef typename ImageType::IndexType IndexType; typedef typename ImageType::RegionType RegionType; void setUp() { // Create random test image int dim = 100; itk::Size<3> size; size.Fill(dim); auto randImageSource = itk::RandomImageSource::New(); randImageSource->SetNumberOfThreads(1); // so results are not random randImageSource->SetSize(size); randImageSource->Update(); m_TestImageRandom = randImageSource->GetOutput(); // Create gradient test image dim = 100; size.Fill(dim); RegionType region; region.SetSize(size); m_TestImageGradient = ImageType::New(); m_TestImageGradient->SetRegions(region); m_TestImageGradient->Allocate(); float val = 0.0; itk::ImageRegionIterator it(m_TestImageGradient, region); while (!it.IsAtEnd()) { auto index = it.GetIndex(); float max = static_cast(dim); float sum = index[0] + index[1] + index[2]; // Linear gradient [0, 1] // val = sum / (3*max); // soft spheres if ((index[0] / max) > 0.5 && (index[1] / max) > 0.5 && (index[2] / max) > 0.5) { - val = -((std::sin(2 * 3.14 * index[0] / max) * std::sin(2 * 3.14 * index[1] / max) * std::sin(2 * 3.14 * index[2] / max) + 1) / 2.); + val = (-std::sin(2 * 3.14 * index[0] / max) * std::sin(2 * 3.14 * index[1] / max) * std::sin(2 * 3.14 * index[2] / max) + 1) / 2.; } else { val = (std::sin(4 * 3.14 * index[0] / max) * std::sin(4 * 3.14 * index[1] / max) * std::sin(4 * 3.14 * index[2] / max) + 1) / 2.; } it.Set(static_cast(val)); ++it; } } void tearDown() { m_TestImageRandom = nullptr; m_TestImageGradient = nullptr; m_TestImageVector = nullptr; } void SuggestRegionFilterTest() { - PixelType threshold = 0.5; + PixelType threshold = 0.7; // Save gradient image auto writer = itk::ImageFileWriter::New(); writer->SetFileName("/home/jens/Desktop/ALtest/gradientImage.nrrd"); writer->SetInput(m_TestImageGradient); writer->Update(); - // Save random image - writer->SetFileName("/home/jens/Desktop/ALtest/randomImage.nrrd"); - writer->SetInput(m_TestImageRandom); - writer->Update(); - // Threshold filter - auto thresholdFilter = itk::ThresholdImageFilter::New(); - thresholdFilter->SetLower(threshold); + auto thresholdFilter = itk::BinaryThresholdImageFilter::New(); + thresholdFilter->SetOutsideValue(0); + thresholdFilter->SetInsideValue(1); + thresholdFilter->SetLowerThreshold(threshold); thresholdFilter->SetInput(m_TestImageGradient); thresholdFilter->Update(); // Save thresholded image - writer->SetFileName("/home/jens/Desktop/ALtest/gradientImageThresholded.nrrd"); - writer->SetInput(thresholdFilter->GetOutput()); - writer->Update(); - - // Create suggestion filter - auto suggestionFilter = mitk::ActiveLearningSuggestRegionFilter::New(); - suggestionFilter->SetThreshold(threshold); - suggestionFilter->SetInput(m_TestImageGradient); - suggestionFilter->Update(); - - // Save result - writer->SetFileName("/home/jens/Desktop/ALtest/gradientImageSuggestion.nrrd"); - writer->SetInput(suggestionFilter->GetOutput()); - writer->Update(); + auto binaryWriter = itk::ImageFileWriter::New(); + binaryWriter->SetFileName("/home/jens/Desktop/ALtest/gradientImageThresholded.nrrd"); + binaryWriter->SetInput(thresholdFilter->GetOutput()); + binaryWriter->Update(); + + // Connected component filter + auto ccFilter = itk::ConnectedComponentImageFilter::New(); + ccFilter->SetInput(thresholdFilter->GetOutput()); + ccFilter->Update(); + + // Save image with connected components + binaryWriter->SetFileName("/home/jens/Desktop/ALtest/gradientImageCC.nrrd"); + binaryWriter->SetInput(ccFilter->GetOutput()); + binaryWriter->Update(); + + // Select the one with greatest uncertainty sum + int ccCount = static_cast(ccFilter->GetObjectCount()); + MITK_INFO << "Number of components: " << ccCount; + std::map componentSize; + for (int a=1; a<=ccCount; ++a) {componentSize[a] = 0.0;} + auto imageIterator = itk::ImageRegionConstIterator(m_TestImageGradient, m_TestImageGradient->GetLargestPossibleRegion()); + auto binaryImageIterator = itk::ImageRegionConstIterator(ccFilter->GetOutput(), ccFilter->GetOutput()->GetLargestPossibleRegion()); + while (!binaryImageIterator.IsAtEnd()) + { + unsigned short binVal = binaryImageIterator.Get(); + float val = imageIterator.Get(); + if (binVal != 0) {componentSize[binVal] += val;} + ++binaryImageIterator; + ++imageIterator; + } + using PairType = decltype(componentSize)::value_type; + auto maxComp = std::max_element(componentSize.begin(), componentSize.end(), [](const PairType& lhs, const PairType& rhs){return lhs.second < rhs.second;}); + + auto thresholdFilter2 = itk::BinaryThresholdImageFilter::New(); + thresholdFilter2->SetOutsideValue(0); + thresholdFilter2->SetInsideValue(1); + thresholdFilter2->SetLowerThreshold(maxComp->first); + thresholdFilter2->SetUpperThreshold(maxComp->first); + thresholdFilter2->SetInput(ccFilter->GetOutput()); + thresholdFilter2->Update(); + + // Save optimal region + binaryWriter->SetFileName("/home/jens/Desktop/ALtest/gradientImageSuggestedRegionReference.nrrd"); + binaryWriter->SetInput(thresholdFilter2->GetOutput()); + binaryWriter->Update(); + + // Doe the same with suggestion filter + auto suggestFilter = mitk::ActiveLearningSuggestRegionFilter::New(); + suggestFilter->SetThreshold(threshold); + suggestFilter->SetInput(m_TestImageGradient); + suggestFilter->Update(); + + // Save suggested region + binaryWriter->SetFileName("/home/jens/Desktop/ALtest/gradientImageSuggestedRegion.nrrd"); + binaryWriter->SetInput(suggestFilter->GetOutput()); + binaryWriter->Update(); } - typename ImageType::Pointer m_TestImageRandom; typename ImageType::Pointer m_TestImageGradient; typename ImageType::Pointer m_TestImageVector; }; MITK_TEST_SUITE_REGISTRATION(mitkActiveLearning)