本文介绍如何使用ITK进行K均值聚类并通过VTK进行可视化。
头文件引入
ITK用于进行聚类分析,VTK用于可视化。itkVector用于定义点的坐标向量,itkListSample用来存储数据样本。vtkActor和vtkPolyDataMapper等类用于渲染和展示聚类后的点云。
#include <itkDecisionRule.h>
#include <itkVector.h>
#include <itkListSample.h>
#include <itkKdTree.h>
#include <itkWeightedCentroidKdTreeGenerator.h>
#include <itkKdTreeBasedKmeansEstimator.h>
#include <itkMinimumDecisionRule.h>
#include <itkEuclideanDistanceMetric.h>
#include <itkDistanceToCentroidMembershipFunction.h>
#include <itkSampleClassifierFilter.h>
#include <itkNormalVariateGenerator.h>
#include <vtkVersion.h>
#include <vtkActor.h>
#include <vtkInteractorStyleTrackballCamera.h>
#include <vtkPolyData.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkVertexGlyphFilter.h>
生成样本点集
使用ITK的NormalVariateGenerator生成两个正态分布的点集。每个点是一个三维向量,两个点集的均值分别为100和200,标准差为30。
typedef itk::Vector< double, 3 > MeasurementVectorType; // 定义三维向量
typedef itk::Statistics::ListSample< MeasurementVectorType > SampleType; // 定义点集样本
SampleType::Pointer sample = SampleType::New(); // 创建样本实例
typedef itk::Statistics::NormalVariateGenerator NormalGeneratorType;
NormalGeneratorType::Pointer normalGenerator = NormalGeneratorType::New();
normalGenerator->Initialize(101); // 初始化随机生成器
MeasurementVectorType mv;
double mean = 100;
double standardDeviation = 30;
// 生成第一个分布的点集
for (unsigned int i = 0; i < 100; ++i)
{
mv[0] = (normalGenerator->GetVariate() * standardDeviation) + mean;
mv[1] = (normalGenerator->GetVariate() * standardDeviation) + mean;
mv[2] = (normalGenerator->GetVariate() * standardDeviation) + mean;
sample->PushBack(mv); // 将生成的点加入样本
}
// 生成第二个分布的点集
normalGenerator->Initialize(3024);
mean = 200;
standardDeviation = 30;
for (unsigned int i = 0; i < 100; ++i)
{
mv[0] = (normalGenerator->GetVariate() * standardDeviation) + mean;
mv[1] = (normalGenerator->GetVariate() * standardDeviation) + mean;
mv[2] = (normalGenerator->GetVariate() * standardDeviation) + mean;
sample->PushBack(mv);
}
Kd树生成与K均值聚类
使用ITK的Kd树来加速聚类计算。首先,使用WeightedCentroidKdTreeGenerator生成Kd树,接着用KdTreeBasedKmeansEstimator进行K均值聚类。
typedef itk::Statistics::WeightedCentroidKdTreeGenerator< SampleType > TreeGeneratorType;
TreeGeneratorType::Pointer treeGenerator = TreeGeneratorType::New();
treeGenerator->SetSample(sample); // 设置样本
treeGenerator->SetBucketSize(16); // 设置桶大小,控制Kd树的深度
treeGenerator->Update(); // 生成Kd树
typedef itk::Statistics::KdTreeBasedKmeansEstimator<TreeGeneratorType::KdTreeType> EstimatorType;
EstimatorType::Pointer estimator = EstimatorType::New();
// 初始聚类中心的参数设置
EstimatorType::ParametersType initialMeans(6);
initialMeans[0] = 0.0; initialMeans[1] = 0.0; initialMeans[2] = 0.0; // 第一类中心
initialMeans[3] = 5.0; initialMeans[4] = 5.0; initialMeans[5] = 5.0; // 第二类中心
estimator->SetParameters(initialMeans); // 设置初始中心
estimator->SetKdTree(treeGenerator->GetOutput());
estimator->SetMaximumIteration(200); // 设置最大迭代次数
estimator->SetCentroidPositionChangesThreshold(0.0); // 设置聚类终止条件
estimator->StartOptimization(); // 开始K均值聚类
EstimatorType::ParametersType estimatedMeans = estimator->GetParameters(); // 获取聚类后的中心点
分类与决策规则
使用分类器对点集进行分类,通过SampleClassifierFilter和MinimumDecisionRule将样本根据聚类结果分为两类。
typedef itk::Statistics::DistanceToCentroidMembershipFunction< MeasurementVectorType > MembershipFunctionType;
typedef itk::Statistics::MinimumDecisionRule DecisionRuleType;
DecisionRuleType::Pointer decisionRule = DecisionRuleType::New();
typedef itk::Statistics::SampleClassifierFilter< SampleType > ClassifierType;
ClassifierType::Pointer classifier = ClassifierType::New();
classifier->SetDecisionRule(decisionRule);
classifier->SetInput(sample); // 输入样本
classifier->SetNumberOfClasses(2); // 设置分类数量
classifier->Update(); // 执行分类操作
VTK可视化
vtkSmartPointer<vtkPoints> points1 = vtkSmartPointer<vtkPoints>::New();
vtkSmartPointer<vtkPoints> points2 = vtkSmartPointer<vtkPoints>::New();
// 根据分类结果将点分配到两个点集中
auto iter = classifier->GetOutput()->Begin();
while (iter != classifier->GetOutput()->End())
{
if (iter.GetClassLabel() == 100) {
points1->InsertNextPoint(iter.GetMeasurementVector()[0],
iter.GetMeasurementVector()[1],
iter.GetMeasurementVector()[2]);
} else {
points2->InsertNextPoint(iter.GetMeasurementVector()[0],
iter.GetMeasurementVector()[1],
iter.GetMeasurementVector()[2]);
}
++iter;
}
// 渲染分类后的点集
vtkSmartPointer<vtkPolyData> polyData1 = vtkSmartPointer<vtkPolyData>::New();
polyData1->SetPoints(points1);
vtkSmartPointer<vtkPolyDataMapper> mapper1 = vtkSmartPointer<vtkPolyDataMapper>::New();
mapper1->SetInputConnection(glyphFilter1->GetOutputPort());
vtkSmartPointer<vtkActor> actor1 = vtkSmartPointer<vtkActor>::New();
actor1->GetProperty()->SetColor(0, 1, 0); // 绿色点表示第一类
actor1->GetProperty()->SetPointSize(3);
// 同样设置第二类的点集和渲染属性
vtkSmartPointer<vtkPolyData> polyData2 = vtkSmartPointer<vtkPolyData>::New();
polyData2->SetPoints(points2);
vtkSmartPointer<vtkPolyDataMapper> mapper2 = vtkSmartPointer<vtkPolyDataMapper>::New();
vtkSmartPointer<vtkActor> actor2 = vtkSmartPointer<vtkActor>::New();
actor2->GetProperty()->SetColor(1, 0, 0); // 红色点表示第二类
actor2->GetProperty()->SetPointSize(3);
// 创建渲染窗口并展示
vtkSmartPointer<vtkRenderWindow> renderWindow = vtkSmartPointer<vtkRenderWindow>::New();
vtkSmartPointer<vtkRenderer> renderer = vtkSmartPointer<vtkRenderer>::New();
renderWindow->AddRenderer(renderer);
renderer->AddActor(actor1); // 添加第一类的点集
renderer->AddActor(actor2); // 添加第二类的点集
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Start(); // 开始渲染交互