二维ITK连通区域提取
二維ITK連通區域提取
- 說明
- 主要函數介紹
- 細節分析
- 檢測圖像
- 背景色
- 編號順序
- 完整代碼
說明
本文介紹2維ITK圖像連通區域的提取,對如下圖所示的二維圖像,進行連通區域提取,并打上不同的標簽,染上不同的顏色。這里以按連通區域像素個數大小進行排序,保留前幾個最大的連通區域。
提取前:
提取著色后
.
.
.
主要函數介紹
itk::ConnectedComponentImageFilter<ImageType, OutputImageType>;
該filter值檢測連通區域,不編號,不排序。
itk::LabelShapeKeepNObjectsImageFilter;
該filter對連通區域進行排序,然后只保留最大的幾個連通區域。
itk::LabelImageToShapeLabelMapFilter< OutputImageType, LabelMapType> ;
為保留的每個連通區域打上標簽(標號),編號順序為找到不同區域的順序.
雖然LabelShapeKeepNObjectsImageFilter調用SetAttribute 之后,按照attribute對連通區域大小進行了排序,但該排序并不反映在label上。即大的label并不一定對應大的size。反映在 SetNumberOfObjects(n) 設定保留區域之后,只保留排序后前n個區域的label。
itk::RescaleIntensityImageFilter<OutputImageType, ImageType>;
上一步圖像只有標簽,圖像上沒有直接的區別,本步對保留下來的不同連通區域進行不同的染色,以示區分。
細節分析
檢測圖像
itk::ConnectedComponentImageFilter,
根據該filter的描述,只對二值圖進行檢測,零值為背景色,非零值為前景色,即待檢測物體。
背景色
labelShapeKeepNObjectsImageFilter->SetBackgroundValue(0);
該函數設置背景色,只影響輸出的圖像背景色。此處黑色像素視為背景色。
使用該函數需要注意,默認值為該像素值類型的非正最小值NumericTraits::NonpositiveMin(),自定義使用其他值可能造成結果非預期(原因尙未探知,知者望告知),一般設為默認即可。
編號順序
對上圖進行檢測,保留5個連通區域,從大到小排序應該是ABCDE,但是編號順序是按照圖像像素遍歷順序進行編號,此處行優先進行遍歷,找到的連通區域的編號1->A, 2->B,3->D,4->C,5->E。反映在著色順序上,按照12345的順序由深色到白色進行著色。
注意著色時
rescaleFilter->SetOutputMinimum(0);
rescaleFilter->SetOutputMaximum(255);
設置著色的范圍,范圍設置太廣,可能造成部分標簽染色與背景色太接近而看不清。
完整代碼
#include "itkImage.h" #include "itkImageFileReader.h" #include "itkConnectedComponentImageFilter.h" #include "itkLabelShapeKeepNObjectsImageFilter.h" #include "itkRescaleIntensityImageFilter.h" #include "itksys/SystemTools.hxx" #include <sstream> #include "itkPNGImageIOFactory.h" #include "itkLabelImageToShapeLabelMapFilter.h" #include "itkSimilarity3DTransform.h" #include "itkResampleImageFilter.h" # define ENABLE_QUICKVIEW 1#ifdef ENABLE_QUICKVIEW # include "QuickView.h" #endifint main(int argc, char* argv[]) {itk::PNGImageIOFactory::RegisterOneFactory();constexpr unsigned int Dimension = 2;using PixelType = unsigned short;using ImageType = itk::Image<PixelType, Dimension>;using ReaderType = itk::ImageFileReader<ImageType>;ReaderType::Pointer reader = ReaderType::New();std::string fileName = R"(E:\Yinyang30rotate.png)";reader->SetFileName(fileName);try{reader->Update();}catch (const std::exception& e){std::cout << e.what() << std::endl;}using OutputImageType = itk::Image<unsigned short, Dimension>;using ConnectedComponentImageFilterType = itk::ConnectedComponentImageFilter<ImageType, OutputImageType>;ConnectedComponentImageFilterType::Pointer connected = ConnectedComponentImageFilterType::New();connected->SetInput(reader->GetOutput());connected->Update();std::cout << "Number of objects: " << connected->GetObjectCount() << std::endl;using LabelType =unsigned short;using ShapeLabelObjectType = itk::ShapeLabelObject< LabelType, Dimension >;using LabelMapType = itk::LabelMap< ShapeLabelObjectType >;//標簽using LabelShapeKeepNObjectsImageFilterType = itk::LabelShapeKeepNObjectsImageFilter<OutputImageType>;LabelShapeKeepNObjectsImageFilterType::Pointer labelShapeKeepNObjectsImageFilter =LabelShapeKeepNObjectsImageFilterType::New();labelShapeKeepNObjectsImageFilter->SetInput(connected->GetOutput());labelShapeKeepNObjectsImageFilter->SetBackgroundValue(0);labelShapeKeepNObjectsImageFilter->SetNumberOfObjects(5);//labelShapeKeepNObjectsImageFilter->SetReverseOrdering(true);labelShapeKeepNObjectsImageFilter->SetAttribute(LabelShapeKeepNObjectsImageFilterType::LabelObjectType::NUMBER_OF_PIXELS);labelShapeKeepNObjectsImageFilter->Update();//轉換maptypedef itk::LabelImageToShapeLabelMapFilter< OutputImageType, LabelMapType> I2LType;I2LType::Pointer i2l = I2LType::New();i2l->SetInput(labelShapeKeepNObjectsImageFilter->GetOutput());i2l->SetComputePerimeter(true);i2l->Update();LabelMapType* labelMap = i2l->GetOutput();std::cout << "File " << "\"" << fileName << "\""<< " has " << labelMap->GetNumberOfLabelObjects() << " labels." << std::endl;// Retrieve all attributesfor (unsigned int n = 0; n < labelMap->GetNumberOfLabelObjects(); ++n){ShapeLabelObjectType* labelObject = labelMap->GetNthLabelObject(n);std::cout << n<<" Label: "<< itk::NumericTraits<LabelMapType::LabelType>::PrintType(labelObject->GetLabel()) << std::endl; std::cout << " NumberOfPixels: "<< labelObject->GetNumberOfPixels() << std::endl;std::cout << " BoundingBox: "<< labelObject->GetBoundingBox() << std::endl;std::cout << " PhysicalSize: "<< labelObject->GetPhysicalSize() << std::endl;std::cout << " Centroid: "<< labelObject->GetCentroid() << std::endl;std::cout << " NumberOfPixelsOnBorder: "<< labelObject->GetNumberOfPixelsOnBorder() << std::endl;std::cout << " PerimeterOnBorder: "<< labelObject->GetPerimeterOnBorder() << std::endl;std::cout << " FeretDiameter: "<< labelObject->GetFeretDiameter() << std::endl;std::cout << " PrincipalMoments: "<< labelObject->GetPrincipalMoments() << std::endl;std::cout << " PrincipalAxes: "<< labelObject->GetPrincipalAxes() << std::endl;std::cout << " Elongation: "<< labelObject->GetElongation() << std::endl;std::cout << " Perimeter: "<< labelObject->GetPerimeter() << std::endl;std::cout << " Roundness: "<< labelObject->GetRoundness() << std::endl;std::cout << " EquivalentSphericalRadius: "<< labelObject->GetEquivalentSphericalRadius() << std::endl;std::cout << " EquivalentSphericalPerimeter: "<< labelObject->GetEquivalentSphericalPerimeter() << std::endl;std::cout << " EquivalentEllipsoidDiameter: "<< labelObject->GetEquivalentEllipsoidDiameter() << std::endl; std::cout << " OrientedBoundingBoxOrigin: "<< labelObject->GetOrientedBoundingBoxOrigin() << std::endl;std::cout << " Flatness: "<< labelObject->GetFlatness() << std::endl;std::cout << " PerimeterOnBorderRatio: "<< labelObject->GetPerimeterOnBorderRatio() << std::endl<<std::endl<<std::endl;std::cout<< std::endl << std::endl << std::endl;}//查找最大sizeint maxsize = 0;for (unsigned int n = 0; n < 5; ++n)//注意越界{ShapeLabelObjectType* labelObject = labelMap->GetNthLabelObject(n);if (labelObject->GetNumberOfPixels() > maxsize){maxsize = labelObject->GetNumberOfPixels();}}std::cout << "maxsize= " << maxsize << std::endl;//小于0.1倍最大size的丟棄int countToDel = 0;for (unsigned int n = 0; n < 5; ++n)//注意越界{ShapeLabelObjectType* labelObject = labelMap->GetNthLabelObject(n);if (labelObject->GetNumberOfPixels() < 0.1* maxsize){++countToDel;std::cout << "delete..."<<labelObject->GetLabel() << std::endl;}}labelShapeKeepNObjectsImageFilter->SetNumberOfObjects(5-countToDel);//著色using RescaleFilterType = itk::RescaleIntensityImageFilter<OutputImageType, ImageType>;RescaleFilterType::Pointer rescaleFilter = RescaleFilterType::New();rescaleFilter->SetOutputMinimum(0);rescaleFilter->SetOutputMaximum(255);rescaleFilter->SetInput(labelShapeKeepNObjectsImageFilter->GetOutput());#ifdef ENABLE_QUICKVIEWQuickView viewer;viewer.AddImage(reader->GetOutput(), true, itksys::SystemTools::GetFilenameName(fileName));std::stringstream desc;desc << "Largest object of " << connected->GetObjectCount() << " objects";viewer.AddImage(rescaleFilter->GetOutput(), true, desc.str());viewer.Visualize();#endifreturn EXIT_SUCCESS; }
對左邊圖像提取最大的5個連通區域,并排除size小于0.1最大size的連通區域的結果,只剩下3個連通區域。
總結
以上是生活随笔為你收集整理的二维ITK连通区域提取的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 史上最全ThreadLocal 详解(一
- 下一篇: Redhawk解析PAD / IOPAD