使用k-means对3D网格模型进行分割

由于一些原因,最近在做网格分割的相关工作。网格分割的方法有很多,如Easy mesh cutting、K-means、谱分割、基于SDF的分割等。根据对分割要求的不同,选取合适的分割方法。本文中使用了较为简单的k-means对网格进行分割。

K-means原理

K-means是一种简单的聚类方法,聚类属于无监督学习,聚类的样本中却没有给定y,只有特征x,比如假设宇宙中的星星可以表示成三维空间中的点集(x,y,z)。聚类的目的是找到每个样本x潜在的类别y,并将同类别y的样本x放在一起。对于上述的星星,聚类后结果是一个个星团,星团里面的点相互距离比较近,不同星团间的星星距离就比较远了。

算法描述

(1)从数据集中随机抽取k个质心作为初始聚类的中心;
(2)计算数据集中所有的点到这k个点的距离,将点归到离其最近的聚类里;
(3)调整聚类中心,即将聚类的中心移动到聚类的几何中心(即平均值)处;
(4)重复第2步和第3步,直到聚类的中心不再移动,此时算法收敛。

matlab代码如下

function cluster_labels = k_means(data, centers, num_clusters)
%K_MEANS Euclidean k-means clustering algorithm.
%
% Input : data : N-by-D data matrix, where N is the number of data,
% D is the number of dimensions
% centers : K-by-D matrix, where K is num_clusters, or
% 'random', random initialization, or
% [], empty matrix, orthogonal initialization
% num_clusters : Number of clusters
%
% Output : cluster_labels : N-by-1 vector of cluster assignment % Parameter setting
%
iter = 0;
qold = inf;
threshold = 0.001; %
% Check if with initial centers
%
if strcmp(centers, 'random')
disp('Random initialization...');
centers = random_init(data, num_clusters);
elseif isempty(centers)
disp('Orthogonal initialization...');
centers = orth_init(data, num_clusters);
end %
% Double type is required for sparse matrix multiply
%
data = double(data);
centers = double(centers); %
% Calculate the distance (square) between data and centers
%
n = size(data, 1);
x = sum(data.*data, 2)';
X = x(ones(num_clusters, 1), :);
y = sum(centers.*centers, 2);
Y = y(:, ones(n, 1));
P = X + Y - 2*centers*data'; %
% Main program
%
while 1
iter = iter + 1; % Find the closest cluster for each data point
[val, ind] = min(P, [], 1);
% Sum up data points within each cluster
P = sparse(ind, 1:n, 1, num_clusters, n);
centers = P*data;
% Size of each cluster, for cluster whose size is 0 we keep it empty
cluster_size = P*ones(n, 1);
% For empty clusters, initialize again
zero_cluster = find(cluster_size==0);
if length(zero_cluster) > 0
disp('Zero centroid. Initialize again...');
centers(zero_cluster, :)= random_init(data, length(zero_cluster));
cluster_size(zero_cluster) = 1;
end
% Update centers
centers = spdiags(1./cluster_size, 0, num_clusters, num_clusters)*centers; % Update distance (square) to new centers
y = sum(centers.*centers, 2);
Y = y(:, ones(n, 1));
P = X + Y - 2*centers*data'; % Calculate objective function value
qnew = sum(sum(sparse(ind, 1:n, 1, size(P, 1), size(P, 2)).*P));
mesg = sprintf('Iteration %d:\n\tQold=%g\t\tQnew=%g', iter, full(qold), full(qnew));
disp(mesg); % Check if objective function value is less than/equal to threshold
if threshold >= abs((qnew-qold)/qold)
mesg = sprintf('\nkmeans converged!');
disp(mesg);
break;
end
qold = qnew;
end cluster_labels = ind'; %-----------------------------------------------------------------------------
function init_centers = random_init(data, num_clusters)
%RANDOM_INIT Initialize centroids choosing num_clusters rows of data at random
%
% Input : data : N-by-D data matrix, where N is the number of data,
% D is the number of dimensions
% num_clusters : Number of clusters
%
% Output: init_centers : K-by-D matrix, where K is num_clusters
rand('twister', sum(100*clock));
init_centers = data(ceil(size(data, 1)*rand(1, num_clusters)), :); function init_centers = orth_init(data, num_clusters)
%ORTH_INIT Initialize orthogonal centers for k-means clustering algorithm.
%
% Input : data : N-by-D data matrix, where N is the number of data,
% D is the number of dimensions
% num_clusters : Number of clusters
%
% Output: init_centers : K-by-D matrix, where K is num_clusters %
% Find the num_clusters centers which are orthogonal to each other
%
Uniq = unique(data, 'rows'); % Avoid duplicate centers
num = size(Uniq, 1);
first = ceil(rand(1)*num); % Randomly select the first center
init_centers = zeros(num_clusters, size(data, 2)); % Storage for centers
init_centers(1, :) = Uniq(first, :);
Uniq(first, :) = [];
c = zeros(num-1, 1); % Accumalated orthogonal values to existing centers for non-centers
% Find the rest num_clusters-1 centers
for j = 2:num_clusters
c = c + abs(Uniq*init_centers(j-1, :)');
[minimum, i] = min(c); % Select the most orthogonal one as next center
init_centers(j, :) = Uniq(i, :);
Uniq(i, :) = [];
c(i) = [];
end
clear c Uniq;

网格分割

对网格使用K-means进行聚类,首先要构造用于聚类的特征,也就是要构造data、center和num_clusters
如果只使用网格顶点坐标作为聚类特征,并使用'random'初始化聚类中心,聚类结果如下:

模型顶点:53054,面:106104,聚类特征:顶点坐标,聚类数目:4
       

如果将顶点法向也加入聚类的特征中,得到的结果如下:
      

附获取顶点法向的代码如下:

/*获取所有顶点的法向*/
_mesh->request_face_normals();
_mesh->request_vertex_normals();
_mesh->update_normals();
int vnidx = 0;
for (auto vit = _mesh->vertices_begin(); vit != _mesh->vertices_end(); ++vit,vnidx++)
{
auto vertex = vit.handle();
OpenMesh::Vec3d v_normal;
v_normal = _mesh->normal(vertex);
Vnx[vnidx] = v_normal.data()[0];
Vny[vnidx] = v_normal.data()[1];
Vnz[vnidx] = v_normal.data()[2];
}
/*一部分参考YQ,一部分参考OpenMesh入门程序介绍*/

使用k-means对3D网格模型进行分割的更多相关文章

  1. pcl曲面网格模型的三种显示方式

    pcl网格模型有三种可选的显示模式,分别是面片模式(surface)显示,线框图模式(wireframe)显示,点模式(point)显示.默认为面片模式进行显示.设置函数分别为: void pcl:: ...

  2. 基于HTML5 Canvas 点击添加 2D 3D 机柜模型

    今天又返回好好地消化了一下我们的数据容器 DataModel,这里给新手做一个典型的数据模型事件处理的例子作为参考.这个例子看起来很简单,实际上结合了数据模型中非常重要的三个事件处理的部分:属性变化事 ...

  3. 3D max模型导入unity 3D中注意事项

    一.单位,比例统一   在建模型前先设置好单位,在同一场景中会用到的模型的单位设置必须一样,模型与模型之间的比例要正确,和程序的导入单位一致,即便到程序需要缩放也可以统一调整缩放比例.统一单位为米. ...

  4. 用基于WebGL的BabylonJS来共享你的3D扫描模型

    转自:http://www.geekfan.net/6578/ 用基于WebGL的BabylonJS来共享你的3D扫描模型 杰克祥子 2014 年 2 月 26 日 0 条评论 标签:3D扫描 , B ...

  5. WPF 3D 平移模型+动画(桥梁检测系统)

    原文:WPF 3D 平移模型+动画(桥梁检测系统) 关于WPF 3D,网上有很多旋转的例子,但是关于平移的例子并不是太多.本文并非WPF 3D扫盲篇,因此需要对WPF 3D有一定了解,至少知道View ...

  6. WPF在3D Cad模型中利用TextureCoordinates实现颜色渐变显示偏差值的变化

    原文:WPF在3D Cad模型中利用TextureCoordinates实现颜色渐变显示偏差值的变化 注:最近在做3D机械模型重建方面的软件,需要根据光栅传感器采集的数据绘制3D图形,并显示出色差以及 ...

  7. iNeuOS工业互联网操作系统,三维(3D)模型在线编辑应用和实时数据统计(和值、均值、众数、方差、中位数等)

    目       录 1.      概述... 1 2.      三维(3D)模型在线编辑与应用... 2 3.      实时数据统计... 4 1.   概述 此次,iNeuOS工业互联网操作系 ...

  8. 新上线!3D单模型轻量化硬核升级,G级数据轻松拿捏!

    "3D模型体量过大.面数过多.传输展示困难",用户面对这样的3D数据,一定不由得皱起眉头.更便捷.快速处理三维数据,是每个3D用户对高效工作的向往. 在老子云最新上线的单模型轻量化 ...

  9. 点云3d检测模型pointpillar

    PointPillars 一个来自工业界的模型.https://arxiv.org/abs/1812.05784 3D目标检测通常做法 3d卷积 投影到前平面 在bird-view上操作 处理思路依然 ...

随机推荐

  1. 李洪强iOS开发之零基础学习iOS开发【02-C语言】03-关键字、标识符、注释

    上一讲中已经创建了第一个C语言程序,知道了C程序是由函数构成的,这讲继续学习C语言的一些基本语法.C语言属于一门高级语言,其实,所有的高级语言的基本语法组成部分都是一样的,只是表现形式不太一样.就好像 ...

  2. java retention注解

    Retention注解 Retention(保留)注解说明,这种类型的注解会被保留到那个阶段. 有三个值:1.RetentionPolicy.SOURCE —— 这种类型的Annotations只在源 ...

  3. React-用Jest测试

    一. 1.目录结构 二.代码 1.CheckboxWithLabel.jsx var React = require('react/addons'); var CheckboxWithLabel = ...

  4. shell脚本 -d 是目录文件,那么-e,-f等说明

    -e filename 如果 filename存在,则为真 -d filename 如果 filename为目录,则为真 -f filename 如果 filename为常规文件,则为真 -L fil ...

  5. 【c++】中文设置

    #include <iostream> #include <string> #include<locale> using namespace std; ; int ...

  6. Entity Framework学习 - 1.连接数据库

    1.添加项,选择ADO.NET实体数据模型 2.选择从数据库生成 3.建立数据库连接 4.选择要关联的表 点击完成,数据库已连接完毕,并生成了对应的Model

  7. Windows 7更改SVN账户密码

    首先说明下我的系统是Windows7 今天更改了SVN账号和密码,然后想要更改一下Eclipse的SVN登录用户名和密码 但是网上找了一大推说什么客户端的,靠净扯淡. 本人亲测最有效的方法是删除C盘下 ...

  8. NDK(17)让ndk支持完整C++,exception,rtti,

    C++ Support The Android platform provides a very minimal C++ runtime support library (/system/lib/li ...

  9. 1741. Communication Fiend(dp)

    刷个简单的DP缓缓心情 1A #include <iostream> #include<cstdio> #include<cstring> #include< ...

  10. LA 3295 (计数 容斥原理) Counting Triangles

    如果用容斥原理递推的办法,这道题确实和LA 3720 Highway很像. 看到大神们写的博客,什么乱搞啊,随便统计一下,这真的让小白很为难,于是我决定用比较严格的语言来写这篇题解. 整体思路很简单: ...