Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: ENH: Add watershed with distance map example. #86

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/Segmentation/Watersheds/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@

add_example(SegmentWithWatershedAndDistanceMap)
compare_to_baseline(EXAMPLE_NAME SegmentWithWatershedAndDistanceMap
TEST_NAME SegmentWithWatershedAndDistanceMapTest01BaselineComparison
BASELINE_PREFIX SegmentWithWatershedAndDistanceMapTest01Baseline
DEPENDS SegmentWithWatershedAndDistanceMapTest01
TEST_IMAGE SegmentWithWatershedAndDistanceMapTest01.png
)

add_example(SegmentWithWatershedImageFilter)

foreach( i RANGE 1 5 )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0d5403fda7b3c94d3229bf5e42b523a4a5bb2cd84a45973011520daf5b3001192e74ff3d290b43c0feb0ebd84c17a328f4646a7887408c1f63e52927676f2360
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
cmake_minimum_required(VERSION 3.10.2)

project(SegmentWithWatershedAndDistanceMap)

find_package(ITK REQUIRED)
include(${ITK_USE_FILE})


add_executable(SegmentWithWatershedAndDistanceMap Code.cxx)
target_link_libraries(SegmentWithWatershedAndDistanceMap ${ITK_LIBRARIES})

install(TARGETS SegmentWithWatershedAndDistanceMap
DESTINATION bin/ITKExamples/Segmentation/Watersheds
COMPONENT Runtime
)

install(FILES Code.cxx CMakeLists.txt Code.py
DESTINATION share/ITKExamples/Code/Segmentation/Watersheds/SegmentWithWatershedAndDistanceMap/
COMPONENT Code
)


enable_testing()
set(input_image ${CMAKE_CURRENT_BINARY_DIR}/PlateauBorder.tif)
set(binarizingRadius01 2)
set(majorityThreshold01 2)
set(watershedThreshold01 0.01)
set(watershedLevel01 0.5)
set(cleaningStructuringElementRadius01 3)

add_test(NAME SegmentWithWatershedAndDistanceMapTest01
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/SegmentWithWatershedAndDistanceMap
${input_image}
ReversedInputImageTest01.tif
DistanceMapImageTest01.tif
WatershedImageTest01.tif
SegmentWithWatershedAndDistanceMapTest01.tif
${binarizingRadius01}
${majorityThreshold01}
${watershedThreshold01}
${watershedLevel01}
${cleaningStructuringElementRadius01}
)

if(ITK_WRAP_PYTHON)
add_test(NAME SegmentWithWatershedAndDistanceMapTest01Python
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Code.py
${input_image}
ReversedInputImageTest01.tif
DistanceMapImageTest01.tif
WatershedImageTest01.tif
SegmentWithWatershedAndDistanceMapTest01Python.tif
${binarizingRadius01}
${majorityThreshold01}
${watershedThreshold01}
${watershedLevel01}
${cleaningStructuringElementRadius01}
)
endif()
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*=========================================================================
*
* 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
*
* http://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 "itkBinaryBallStructuringElement.h"
#include "itkBinaryMorphologicalOpeningImageFilter.h"
#include "itkImageFileReader.h"
#include "itkImageFileWriter.h"
#include "itkScalarToRGBColormapImageFilter.h"
#include "itkSignedMaurerDistanceMapImageFilter.h"
#include "itkVotingBinaryIterativeHoleFillingImageFilter.h"
#include "itkWatershedImageFilter.h"

// Run with:
// ./SegmentWithWatershedAndDistanceMap <inputImageFile>
// <reversedInputImageFile> <distanceMapOutputImageFile>
// <watershedOutputFileName> <segmentationResultOutputImageFile>
// binarizingRadius majorityThreshold watershedThreshold watershedLevel
// cleaningStructuringElementRadius
// e.g.
// ./SegmentWithWatershedAndDistanceMap PlateauBorder.tif
// reversedInputImage.tif distanceMap.tif watershed.tif segmentationResult.tif
// 2 2 0.01 0.5 3
// (A rule of thumb is to set the threshold to be about 1 / 100 of the level.)

int
main(int argc, char * argv[])
{
if (argc < 10)
{
std::cerr << "Missing parameters." << std::endl;
std::cerr << "Usage: " << argv[0] << " inputImageFile"
<< " reversedInputImageFile"
<< " distanceMapOutputImageFile"
<< " watershedOutputFileName"
<< " segmentationResultOutputImageFile"
<< " binarizingRadius"
<< " majorityThreshold"
<< " watershedThreshold"
<< " watershedLevel"
<< " cleaningStructuringElementRadius" << std::endl;
return EXIT_FAILURE;
}

constexpr unsigned int Dimension = 3;

using UnsignedCharPixelType = unsigned char;
using FloatPixelType = float;

using InputImageType = itk::Image<UnsignedCharPixelType, Dimension>;
using FloatImageType = itk::Image<FloatPixelType, Dimension>;
using RGBPixelType = itk::RGBPixel<UnsignedCharPixelType>;
using RGBImageType = itk::Image<RGBPixelType, Dimension>;
using LabeledImageType = itk::Image<itk::IdentifierType, Dimension>;


using FileReaderType = itk::ImageFileReader<InputImageType>;
FileReaderType::Pointer reader = FileReaderType::New();
reader->SetFileName(argv[1]);
reader->Update();


// Create bubble image: get a binarized version of the input image
using VotingBinaryIterativeHoleFillingImageFilterType =
itk::VotingBinaryIterativeHoleFillingImageFilter<InputImageType>;
VotingBinaryIterativeHoleFillingImageFilterType::Pointer votingBinaryHoleFillingImageFilter =
VotingBinaryIterativeHoleFillingImageFilterType::New();
votingBinaryHoleFillingImageFilter->SetInput(reader->GetOutput());

const unsigned int binarizingRadius = std::stoi(argv[6]);

InputImageType::SizeType indexRadius;
indexRadius.Fill(binarizingRadius);

votingBinaryHoleFillingImageFilter->SetRadius(indexRadius);

votingBinaryHoleFillingImageFilter->SetBackgroundValue(0);
votingBinaryHoleFillingImageFilter->SetForegroundValue(255);

const unsigned int majorityThreshold = std::stoi(argv[7]);
votingBinaryHoleFillingImageFilter->SetMajorityThreshold(majorityThreshold);

votingBinaryHoleFillingImageFilter->Update();

using FileWriterType = itk::ImageFileWriter<InputImageType>;
FileWriterType::Pointer reversedImageWriter = FileWriterType::New();
reversedImageWriter->SetFileName(argv[2]);
reversedImageWriter->SetInput(votingBinaryHoleFillingImageFilter->GetOutput());
reversedImageWriter->Update();


// Get the distance map of the input image
using SignedMaurerDistanceMapImageFilterType =
itk::SignedMaurerDistanceMapImageFilter<InputImageType, FloatImageType>;
SignedMaurerDistanceMapImageFilterType::Pointer distanceMapImageFilter =
SignedMaurerDistanceMapImageFilterType::New();
distanceMapImageFilter->SetInput(votingBinaryHoleFillingImageFilter->GetOutput());

distanceMapImageFilter->SetInsideIsPositive(false);
distanceMapImageFilter->Update();


using DistanceMapFileWriterType = itk::ImageFileWriter<FloatImageType>;
DistanceMapFileWriterType::Pointer distanceMapWriter = DistanceMapFileWriterType::New();
distanceMapWriter->SetFileName(argv[3]);
distanceMapWriter->SetInput(distanceMapImageFilter->GetOutput());
distanceMapWriter->Update();


// Apply the watershed segmentation
using WatershedFilterType = itk::WatershedImageFilter<FloatImageType>;
WatershedFilterType::Pointer watershed = WatershedFilterType::New();

const float watershedThreshold = std::stod(argv[8]);
const float watershedLevel = std::stod(argv[9]);

watershed->SetThreshold(watershedThreshold);
watershed->SetLevel(watershedLevel);

watershed->SetInput(distanceMapImageFilter->GetOutput());
watershed->Update();


using RGBFilterType = itk::ScalarToRGBColormapImageFilter<LabeledImageType, RGBImageType>;
RGBFilterType::Pointer colormapImageFilter = RGBFilterType::New();
colormapImageFilter->SetColormap(RGBFilterType::RGBColormapFilterEnum::Jet);
colormapImageFilter->SetInput(watershed->GetOutput());
colormapImageFilter->Update();

using WatershedFileWriterType = itk::ImageFileWriter<RGBImageType>;
WatershedFileWriterType::Pointer watershedWriter = WatershedFileWriterType::New();
watershedWriter->SetFileName(argv[4]);
watershedWriter->SetInput(colormapImageFilter->GetOutput());
watershedWriter->Update();


// Clean the segmentation image: remove small objects by performing an
// opening morphological operation
using StructuringElementType =
itk::BinaryBallStructuringElement<LabeledImageType::PixelType, LabeledImageType::ImageDimension>;
StructuringElementType structuringElement;

const unsigned int cleaningStructuringElementRadius = std::stoi(argv[10]);
structuringElement.SetRadius(cleaningStructuringElementRadius);
structuringElement.CreateStructuringElement();

using BinaryMorphologicalOpeningImageFilterType =
itk::BinaryMorphologicalOpeningImageFilter<LabeledImageType, LabeledImageType, StructuringElementType>;
BinaryMorphologicalOpeningImageFilterType::Pointer openingFilter = BinaryMorphologicalOpeningImageFilterType::New();
openingFilter->SetInput(watershed->GetOutput());
openingFilter->SetKernel(structuringElement);
openingFilter->Update();


using SegmentationFileWriterType = itk::ImageFileWriter<RGBImageType>;
SegmentationFileWriterType::Pointer segmentationWriter = SegmentationFileWriterType::New();
segmentationWriter->SetFileName(argv[5]);
segmentationWriter->SetInput(colormapImageFilter->GetOutput());
segmentationWriter->Update();


return EXIT_SUCCESS;
}
Loading