ITK/VTK实现聚类

本文介绍如何使用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();  // 开始渲染交互

渲染效果

相关推荐
zhangpz_几秒前
c ++零基础可视化——vector
c++·算法
理论最高的吻1 小时前
98. 验证二叉搜索树【 力扣(LeetCode) 】
数据结构·c++·算法·leetcode·职场和发展·二叉树·c
沈小农学编程1 小时前
【LeetCode面试150】——202快乐数
c++·python·算法·leetcode·面试·职场和发展
ZZZ_O^O1 小时前
【动态规划-卡特兰数——96.不同的二叉搜索树】
c++·学习·算法·leetcode·动态规划
一只小透明啊啊啊啊2 小时前
Leetcode100子串
算法
木向2 小时前
leetcode:114. 二叉树展开为链表
算法·leetcode·链表
sky_smile_Allen2 小时前
[C#] 关于数组的详细解释以及使用注意点
开发语言·算法·c#
希望有朝一日能如愿以偿2 小时前
力扣题解(新增道路查询后的最短距离II)
算法
我感觉。2 小时前
【机器学习chp6】对数几率回归
算法·机器学习·逻辑回归·分类模型·对数几率回归
无限大.3 小时前
力扣题解3248 矩阵中的蛇(简单)
算法·leetcode·矩阵