initial commit

This commit is contained in:
2026-08-07 20:26:59 +05:30
commit ec1796cc9e
2 changed files with 171 additions and 0 deletions
+14
View File
@@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 2.8)
project(FollicleSeparator)
find_package(ITK REQUIRED)
include(${ITK_USE_FILE})
file(GLOB_RECURSE HEADERS "src/*.h")
file(GLOB_RECURSE INLINES "src/*.inl")
file(GLOB_RECURSE SOURCES "src/*.cpp")
add_executable(FollicleSeparator ${HEADERS} ${INLINES} ${SOURCES})
target_link_libraries(FollicleSeparator ${ITK_LIBRARIES})
+157
View File
@@ -0,0 +1,157 @@
#pragma warning(push)
#pragma warning(disable:4996)
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkBinaryThresholdImageFilter.h>
#include <itkMaskNegatedImageFilter.h>
#include <itkMinimumMaximumImageCalculator.h>
#include <itkIntensityWindowingImageFilter.h>
#include <itkSignedMaurerDistanceMapImageFilter.h>
#include <itkMorphologicalWatershedImageFilter.h>
#include <itkTimeProbe.h>
#pragma warning(pop)
#include <iostream>
using namespace std;
///
/// Binary object splitting using
/// watershed segmentation applied to the distance transform
///
int main(int argc, char* argv[])
{
try
{
const int DIM = 3;
typedef itk::Image<unsigned char, DIM> UInt8Volume; // Binary Mask
typedef itk::Image<short, DIM> Int16Volume; // Input Volume
typedef itk::Image<unsigned short, DIM> UInt16Volume; // Label Volume
typedef itk::Image<float, DIM> Float32Volume; // Distance
// Read labeled volume
itk::ImageFileReader<Int16Volume>::Pointer pReader = itk::ImageFileReader<Int16Volume>::New();
{
itk::TimeProbe clock;
clock.Start();
cout << "Read input from labeledVolume.vtk" << endl;
pReader->SetFileName("labeledVolume.vtk");
pReader->Update();
clock.Stop();
cout << "ImageFileReader: " << clock.GetTotal() << "s" << endl;
}
// Binarize input volume: Follicles = 0, Background = 255
itk::BinaryThresholdImageFilter<Int16Volume,UInt8Volume>::Pointer pBinarizationFilter =
itk::BinaryThresholdImageFilter<Int16Volume,UInt8Volume>::New();
{
itk::TimeProbe clock;
clock.Start();
pBinarizationFilter->SetInput(pReader->GetOutput());
pBinarizationFilter->SetLowerThreshold(0);
pBinarizationFilter->SetUpperThreshold(0);
pBinarizationFilter->SetInsideValue(255);
pBinarizationFilter->SetOutsideValue(0);
pBinarizationFilter->Update();
clock.Stop();
cout << "BinaryThresholdImageFilter: " << clock.GetTotal() << "s" << endl;
}
// Compute signed distance from background pixels
// Inside the follicle: positive
// Outside of the follicle: negative
itk::SignedMaurerDistanceMapImageFilter<UInt8Volume,Float32Volume>::Pointer pSignedMaurer =
itk::SignedMaurerDistanceMapImageFilter<UInt8Volume,Float32Volume>::New();
{
itk::TimeProbe clock;
clock.Start();
pSignedMaurer->SetInput(pBinarizationFilter->GetOutput());
pSignedMaurer->Update();
clock.Stop();
cout << "SignedMaurerDistanceMapImageFilter: " << clock.GetTotal() << "s" << endl;
}
// Invert distance map: [0 ... max distance] -> [255 ... 0]
// background pixels will have intensity 255
// pixels inside the follicle far away from the border will have low intensities
itk::IntensityWindowingImageFilter<Float32Volume,UInt8Volume>::Pointer pConvertToUInt8 =
itk::IntensityWindowingImageFilter<Float32Volume,UInt8Volume>::New();
{
itk::TimeProbe clock;
clock.Start();
itk::MinimumMaximumImageCalculator<Float32Volume>::Pointer pMinMaxDistance =
itk::MinimumMaximumImageCalculator<Float32Volume>::New();
pMinMaxDistance->SetImage(pSignedMaurer->GetOutput());
pMinMaxDistance->ComputeMaximum();
float maxDistance = pMinMaxDistance->GetMaximum();
pConvertToUInt8->SetInput(pSignedMaurer->GetOutput());
pConvertToUInt8->SetWindowMinimum(0.0);
pConvertToUInt8->SetWindowMaximum(maxDistance);
pConvertToUInt8->SetOutputMinimum(255);
pConvertToUInt8->SetOutputMaximum(0);
pConvertToUInt8->Update();
clock.Stop();
cout << "ConvertToUInt8: " << clock.GetTotal() << "s" << endl;
itk::ImageFileWriter<UInt8Volume>::Pointer pWriter = itk::ImageFileWriter<UInt8Volume>::New();
pWriter->SetInput(pConvertToUInt8->GetOutput());
pWriter->SetFileName("labeledVolume.distanceMap.vtk");
pWriter->Update();
}
// Watershed transformation of the distance map
itk::MorphologicalWatershedImageFilter<UInt8Volume,UInt16Volume>::Pointer pWatershed = itk::MorphologicalWatershedImageFilter<UInt8Volume,UInt16Volume>::New();
{
itk::TimeProbe clock;
clock.Start();
pWatershed->SetLevel(25); // high level -> low Number of regions
pWatershed->SetMarkWatershedLine(false);
pWatershed->SetInput(pConvertToUInt8->GetOutput());
pWatershed->Update();
clock.Stop();
cout << "WatershedImageFilter: " << clock.GetTotal() << "s" << endl;
}
// Mask out background voxels from the watershed result
itk::MaskNegatedImageFilter<UInt16Volume, UInt8Volume, UInt16Volume>::Pointer pMaskImageFilter =
itk::MaskNegatedImageFilter<UInt16Volume, UInt8Volume, UInt16Volume>::New();
{
pMaskImageFilter->SetInput(pWatershed->GetOutput());
pMaskImageFilter->SetMaskImage(pBinarizationFilter->GetOutput());
pMaskImageFilter->Update();
}
itk::MinimumMaximumImageCalculator<UInt16Volume>::Pointer pMaxLabelCalculator =
itk::MinimumMaximumImageCalculator<UInt16Volume>::New();
pMaxLabelCalculator->SetImage(pMaskImageFilter->GetOutput());
pMaxLabelCalculator->ComputeMaximum();
cout << "Number of labels: " << pMaxLabelCalculator->GetMaximum() << endl;
cout << "Write result to labeledVolume.watershed.vtk" << endl;
itk::ImageFileWriter<UInt16Volume>::Pointer pWriter = itk::ImageFileWriter<UInt16Volume>::New();
pWriter->SetInput(pMaskImageFilter->GetOutput());
pWriter->SetFileName("labeledVolume.watershed.vtk");
pWriter->Update();
}
catch (const itk::ExceptionObject& ex)
{
std::cerr << ex.GetDescription() << std::endl;
return 1;
}
return 0;
}