在进行图像处理时,当需要分割图像中的较大连通区域时,可以使用ITK的连通区域提取功能。比如从人体脑部CT提取与脑骨连接的骨头区域而去除周围噪点信息。
global.h
//global.h
#pragma once
#include
#include
#include
#include "ImportLibITK5_0_1.h"
#include "itksys/SystemTools.hxx"
namespace dcmHandler
{
typedef signed short PixelType;
typedef itk::Image< PixelType, 2U > DcmImage2DType;
typedef itk::Image< PixelType, 3U > DcmImage3DType;
}
#define DCMHANDER_NAMESPACE_BEGIN namespace dcmHandler{
#define DCMHANDER_NAMESPACE_END }
分割线
//DcmExtractingConnectedRegions.h
#pragma once
#include "global.h"
#include "itkConnectedComponentImageFilter.h"
#include "itkLabelShapeKeepNObjectsImageFilter.h"
#include "itkRescaleIntensityImageFilter.h"
#include "itkLabelImageToShapeLabelMapFilter.h"
#include "itkBinaryThresholdImageFilter.h"
/**
* Abstract connected region acoording to "NUMBER_OF_PIXELS"
* of single region.
*/
DCMHANDER_NAMESPACE_BEGIN
class ConnectedRegionsAbstract
{
public:
ConnectedRegionsAbstract();
~ConnectedRegionsAbstract();
private:
using LabelType = unsigned short;
using ShapeLabelObjectType = itk::ShapeLabelObject< LabelType, 3U >;
using LabelMapType = itk::LabelMap< ShapeLabelObjectType >;
using I2LType =
itk::LabelImageToShapeLabelMapFilter< DcmImage3DType, LabelMapType>;
using RescaleFilterType =
itk::RescaleIntensityImageFilter<DcmImage3DType, DcmImage3DType>;
using ConnectedComponentImageFilterType =
itk::ConnectedComponentImageFilter <DcmImage3DType, DcmImage3DType >;
using LabelShapeKeepNObjectsImageFilterType =
itk::LabelShapeKeepNObjectsImageFilter< DcmImage3DType >;
using IteratorType3D = itk::ImageRegionIterator<DcmImage3DType>;
using FilterType = itk::BinaryThresholdImageFilter
<dcmHandler::DcmImage3DType, dcmHandler::DcmImage3DType>;
/** deep copy ITK image data*/
template<typename TPixel, unsigned int Dim>
void deepCopyItkImageData(const itk::Image<TPixel, Dim>* src,
itk::Image<TPixel, Dim>* dst)
{
if (!src || !dst) {
return;
}
dst->CopyInformation(src);
dst->SetMetaDataDictionary(src->GetMetaDataDictionary());
dst->SetRegions(src->GetLargestPossibleRegion());
dst->Allocate();
std::copy_n(src->GetBufferPointer(),
src->GetPixelContainer()->Size(), dst->GetBufferPointer());
}
template<typename TPixel, unsigned int Dim>
typename itk::Image<TPixel, Dim>::Pointer deepCopyItkImageData(
const itk::Image<TPixel, Dim>* data)
{
using ImageT = itk::Image<TPixel, Dim>;
typename ImageT::Pointer ret = ImageT::New();
deepCopyItkImageData(data, ret.GetPointer());
return ret;
}
public:
void setInputData(DcmImage3DType::Pointer inputData);
void setBinaryLowerThresholdValue(
const DcmImage3DType::PixelType& lowerValue );
void setRegionsNumberTokeep(const unsigned int num);
/** set the factory to multiply maximum size of single connected region
* to exclude the region which much smaller than maximum connected region size.
*/
void setFactoryToMaxSizeToKeep(const double factory = 0.0);
/** set the munber of pixel size to delete,the region size smaller than
* this value will be dropped.
*/
void setSizeToDelete(const unsigned long long pixelNum);
bool updata();
DcmImage3DType::Pointer getResult();
public:
DcmImage3DType::Pointer m_dcmData;
DcmImage3DType::PixelType m_binaryLowerThresholdValue;
DcmImage3DType::Pointer m_resultData;
unsigned int m_RegionNumberToKeep;
double m_factoryToMaxSizeToKeep;
unsigned long long m_numberOfMinSizeToDelete;
};
DCMHANDER_NAMESPACE_END
分割线 ================
//DcmExtractingConnectedRegions.cpp
#include "DcmExtractingConnectedRegions.h"
DCMHANDER_NAMESPACE_BEGIN
ConnectedRegionsAbstract::ConnectedRegionsAbstract()
{
}
ConnectedRegionsAbstract::~ConnectedRegionsAbstract()
{
}
void ConnectedRegionsAbstract::setInputData(DcmImage3DType::Pointer inputData)
{
m_dcmData = inputData;
}
void ConnectedRegionsAbstract::setBinaryLowerThresholdValue(
const DcmImage3DType::PixelType& lowerValue)
{
m_binaryLowerThresholdValue = lowerValue;
}
void ConnectedRegionsAbstract::setRegionsNumberTokeep(const unsigned int num)
{
m_RegionNumberToKeep = num;
}
void ConnectedRegionsAbstract::setFactoryToMaxSizeToKeep(const double factory)
{
m_factoryToMaxSizeToKeep = factory;
}
void ConnectedRegionsAbstract::setSizeToDelete(const unsigned long long pixelNum)
{
m_numberOfMinSizeToDelete = pixelNum;
}
bool ConnectedRegionsAbstract::updata()
{
auto data = deepCopyItkImageData(m_dcmData.GetPointer());
FilterType::Pointer BinaryFilter = FilterType::New();
BinaryFilter->SetInput(data);
BinaryFilter->SetOutsideValue(0);
BinaryFilter->SetInsideValue(3000);
BinaryFilter->SetUpperThreshold(4096);
BinaryFilter->SetLowerThreshold(m_binaryLowerThresholdValue);
ConnectedComponentImageFilterType::Pointer connected
= ConnectedComponentImageFilterType::New();
connected->SetInput(BinaryFilter->GetOutput());
std::cout << "connected->GetObjectCount() " <<
connected->GetObjectCount() << std::endl;
LabelShapeKeepNObjectsImageFilterType::Pointer labelShapeKeepNObjectsImageFilter
= LabelShapeKeepNObjectsImageFilterType::New();
labelShapeKeepNObjectsImageFilter->SetInput(connected->GetOutput());
labelShapeKeepNObjectsImageFilter->SetNumberOfObjects(m_RegionNumberToKeep);
labelShapeKeepNObjectsImageFilter->SetAttribute(
LabelShapeKeepNObjectsImageFilterType::LabelObjectType::NUMBER_OF_PIXELS);
I2LType::Pointer i2l = I2LType::New();
i2l->SetInput(labelShapeKeepNObjectsImageFilter->GetOutput());
i2l->SetComputePerimeter(true);
try
{
i2l->Update();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
LabelMapType* labelMap = i2l->GetOutput();
std::cout << " has " << labelMap->GetNumberOfLabelObjects() << " labels." << std::endl;
for (unsigned int n = 0; n < labelMap->GetNumberOfLabelObjects(); ++n)
{
std::cout << "number " << n << ": " << std::endl<<
" label: "<< labelMap->GetNthLabelObject(n)->GetLabel() << std::endl<<
" size: " << labelMap->GetNthLabelObject(n)->GetNumberOfPixels() << std::endl<< std::endl;
}
int maxsize = 0;
for (unsigned int n = 0; n < labelMap->GetNumberOfLabelObjects(); ++n)
{
ShapeLabelObjectType* labelObject = labelMap->GetNthLabelObject(n);
if (labelObject->GetNumberOfPixels() > maxsize)
{
maxsize = labelObject->GetNumberOfPixels();
}
}
std::cout << "maxsize= " << maxsize << std::endl;
unsigned int countToDel = 0;
for (unsigned int n = 0; n < labelMap->GetNumberOfLabelObjects(); ++n)//注意越界
{
std::cout << "label " << n << " " <<
labelMap->GetNthLabelObject(n)->GetNumberOfPixels() << std::endl;
ShapeLabelObjectType* labelObject = labelMap->GetNthLabelObject(n);
if (labelObject->GetNumberOfPixels() < m_factoryToMaxSizeToKeep * maxsize
|| labelObject->GetNumberOfPixels() < m_numberOfMinSizeToDelete)
{
++countToDel;
std::cout << "delete label..." <<
labelObject->GetLabel() << std::endl<< std::endl;
}
}
std::cout << std::endl << "deleted " <<
countToDel << " labels totally" << std::endl;
std::cout << "keep region number at last: " <<
m_RegionNumberToKeep - countToDel << std::endl;
labelShapeKeepNObjectsImageFilter->
SetNumberOfObjects(m_RegionNumberToKeep - countToDel);
RescaleFilterType::Pointer rescaleFilter = RescaleFilterType::New();
rescaleFilter->SetOutputMinimum(0);
rescaleFilter->SetOutputMaximum(3000);
rescaleFilter->SetInput(labelShapeKeepNObjectsImageFilter->GetOutput());
try
{
rescaleFilter->Update();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
return false;
}
auto resultData = deepCopyItkImageData(m_dcmData.GetPointer());
auto resclaedImage = rescaleFilter->GetOutput();
DcmImage3DType::RegionType
imageRegion = resclaedImage->GetBufferedRegion();
IteratorType3D dataItSrc(resclaedImage, imageRegion);
dataItSrc.GoToBegin();
while (!dataItSrc.IsAtEnd())
{
if (dataItSrc.Get() <= 0)
{
resultData->SetPixel(dataItSrc.GetIndex(), 0);
}
++dataItSrc;
}
m_resultData = resultData;
return true;
}
DcmImage3DType::Pointer ConnectedRegionsAbstract::getResult()
{
return m_resultData;
}
DCMHANDER_NAMESPACE_END
没弄清为什么保留的连通区域个数经常比设定的数目多一个,后面再研究。
完整代码及测试数据集(为spine web开源数据集)下载:
https://download.csdn.net/download/assjaa/14930215