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();  // 开始渲染交互

渲染效果

相关推荐
zyhomepage9 分钟前
科技的成就(六十四)
开发语言·人工智能·科技·算法·内容运营
想做白天梦28 分钟前
多级反馈队列
java·windows·算法
潇雷29 分钟前
算法Day12|226-翻转二叉树;101-对称二叉树;104-二叉树最大深度;111-二叉树最小深度
java·算法·leetcode
爱编程— 的小李1 小时前
开关灯问题(c语言)
c语言·算法·1024程序员节
韭菜盖饭1 小时前
LeetCode每日一题3211---生成不含相邻零的二进制字符串
数据结构·算法·leetcode
极客代码2 小时前
C/C++ 随机数生成方法
c语言·开发语言·c++·算法
甜甜向上呀2 小时前
【数据结构】快速排序(三种实现方式)
算法·排序算法
旋转的油纸伞2 小时前
大模型,多模态大模型面试【LoRA,分类,动静态数据类型,DDPM,ControlNet,IP-Adapter, Stable Diffusion】
算法·leetcode·面试·职场和发展·散列表
XUE_DING_E2 小时前
Educational Codeforces Round 171
算法
Patience to do2 小时前
Android Studio项目(算法计算器)
android·算法·android studio