点云delaunay三角化PCL and C++

本文使用了PCL点云库来作可视化。结果如下图:

算法改编自一个JS程序。好像是github上的项目,但是时间比较长,不记得是哪个了。

最终结果和源程序基本一样。因为是出于兴趣写的,没有很严谨,无法保证算法的百分百正确。

感兴趣的可以将源码进行规范和优化。

下边是调用:

pcl::visualization::PCLVisualizer viewer("rgb");
	std::vector<int> res = delaunayTransform(ptr);
	viewer.addPointCloud(ptr, "cloud");
	cout << res.size();
	for (int i =0; i < res.size(); i+=2) {
		viewer.addLine(ptr->points[res[i]], ptr->points[res[i+1]], 1, 0, 0, to_string(i));
}

 源码:


typedef pcl::PointXYZ PointT;
typedef pcl::PointCloud<PointT> PointCloudT;

static const float EPSILON = std::numeric_limits<float>::epsilon();
static const float PI = 3.1415926;
static const float cos30 = cos(PI / 6);
static const float cos60 = cos(PI / 3);

/*
 * Triangle
 */
struct Triangle
{
	PointT pa, pb, pc;
};

/*
 * index of triangle
 */
struct TriangleIndex
{
	int pa, pb, pc;
};

/*
 * Triangle
 */
struct Circumcircle
{
	float r = 0;
	TriangleIndex index;
	PointT center;
};

/*
 * t_p1,t_p2 are the indexes of edges corresponding to vertex p1,p2
 */
struct Edges
{
	int p1 = -1, p2 = -1;
	int amount = 1;
	int index = -1;
	int t_p1 = -1;
	int t_p2 = -1;
	std::list<int> top;
};

struct Line
{
	int p0 = -1, p1 = -1;
	bool isGroup = false;
};

/*
 * Get the  super triangle of point cloud
 */
Triangle getSuperTranigle(PointCloudT::Ptr ptr);

/*
 * Get circumcircle of each triangle
 * If three points are in a straight line,it will  return {}
 */
Circumcircle getCircumcircle(PointCloudT::Ptr ptr, TriangleIndex &index);

/*
 * Remove those identical edges
 * |it = edge.erase(it)| is necessary
 */
void removeEdge(std::list<int> &edge);

/*
 * Delanay transform
 * return sets of index of triangle vertices
 */
std::vector<int> delaunayTransform(PointCloudT::Ptr ptr);

float getDistance(PointT &a, PointT &b);

std::vector<PointT> getCircleCenter(PointT &a, PointT &b, float r);

bool equals(Edges &e1, Edges &e2);

void removeOutside(int &i, std::vector<Edges> &total, std::queue<Edges> &outside, int &top);

/*
 * Alpha Shape
 * 1. Construction of the Delaunay triangular network
 * 2. Remove triangles that do not satisfy the alpha shape algorithm:
 *	 (1) One side of the current triangle is longer than 2*alpha
 *	 (2) The current side length is AB, constructs a circle with an alpha radius (usually has two circles), and one circle
 *	     contains other points (excluding A, B)
 * 3. Count the number of edges of the remaining triangular network
 * 4. If the edge is unique, that is, the quantity is one, the edge is the boundary and the endpoint is the boundary point
 *
 *  y
 *   ↑   ·  ·
 *   |  · ·   ·
 *   | ·  · ·   ·
 *	 |  ·  ·  
 * —|——————→ x
 */

std::vector<int> delaunayAlphaShape(std::vector<int> &triangle, PointCloudT::Ptr ptr, double alpha);






Triangle getSuperTranigle(PointCloudT::Ptr ptr) {
	PointT p_max, p_min, mid;
	pcl::getMinMax3D(*ptr, p_min, p_max);
	float dx = p_max.x - p_min.x;
	float dy = p_max.y - p_min.y;
	float dmax = dx > dy ? dx : dy;
	Triangle super_triangle;
	mid.x = p_min.x + dx * 0.5;
	mid.y = p_min.y + dy * 0.5;
	super_triangle.pa = { mid.x - 20 * dmax,mid.y - dmax,0 };
	super_triangle.pb = { mid.x,mid.y + 20 * dmax,0 };
	super_triangle.pc = { mid.x + 20 * dmax,mid.y - dmax ,0 };
	return super_triangle;
}

Circumcircle getCircumcircle(PointCloudT::Ptr ptr, TriangleIndex& index) {
	using namespace std;
	Circumcircle circumcircle;
	circumcircle.index = index;
	PointT p1 = ptr->points[index.pa], p2 = ptr->points[index.pb], p3 = ptr->points[index.pc];
	float xc, yc, m1, m2, mx1, mx2, my1, my2, dx, dy;
	float delta_y1 = abs(p1.y - p2.y);
	float delta_y2 = abs(p2.y - p3.y);
	if (delta_y1 < EPSILON && delta_y2 < EPSILON) {
		return circumcircle;
	}
	if (delta_y1 < EPSILON) {
		m2 = -((p3.x - p2.x) / (p3.y - p2.y));
		mx2 = (p2.x + p3.x) * 0.5;
		my2 = (p2.y + p3.y) * 0.5;
		xc = (p1.x + p2.x) * 0.5;
		yc = m2 * (xc - mx2) + my2;
	}

	else if (delta_y2 < EPSILON) {
		m1 = -((p2.x - p1.x) / (p2.y - p1.y));
		mx1 = (p1.x + p2.x) * 0.5;
		my1 = (p1.y + p2.y) * 0.5;
		xc = (p3.x + p2.x) * 0.5;
		yc = m1 * (xc - mx1) + my1;
	}

	else {
		m1 = -((p2.x - p1.x) / (p2.y - p1.y));
		m2 = -((p3.x - p2.x) / (p3.y - p2.y));
		mx1 = (p1.x + p2.x) * 0.5;
		mx2 = (p2.x + p3.x) * 0.5;
		my1 = (p1.y + p2.y) * 0.5;
		my2 = (p2.y + p3.y) * 0.5;
		xc = (m1 * mx1 - m2 * mx2 + my2 - my1) / (m1 - m2);
		yc = (delta_y1 > delta_y2) ? m1 * (xc - mx1) + my1 : m2 * (xc - mx2) + my2;
	}
	dx = p2.x - xc;
	dy = p2.y - yc;
	circumcircle.center.x = xc;
	circumcircle.center.y = yc;
	circumcircle.center.z = 0;
	circumcircle.r = dx * dx + dy * dy;
	return circumcircle;
}

void removeEdge(std::list<int>& edge) {
	int a = -1, b = -1, c = -1, d = -1;
	for (auto it = edge.end(); it != edge.begin(); )
	{
		a = *(--it);
		b = *(--it);
		for (auto it2 = it; it2 != edge.begin();) {
			c = *(--it2);
			d = *(--it2);
			if ((a == c && b == d) || (a == d && b == c)) {
				it = edge.erase(it); it = edge.erase(it);
				it2 = edge.erase(it2); it2 = edge.erase(it2);
				break;
			}
		}
	}
	return;
}


bool equals(Edges& e1, Edges& e2) {
	if (e1.p1 == e2.p1 && e1.p2 == e2.p2) return true;
	if (e1.p1 == e2.p2 && e1.p2 == e2.p1)return true;
	return false;
}


std::vector<int> delaunayTransform(PointCloudT::Ptr ptr) {
	std::vector<int> res;
	if (ptr->size() < 3) return  res;
	int amount = ptr->size();
	std::vector<int> indices(amount);
	for (int i = 0; i < amount; ++i) indices[i] = i;
	sort(indices.begin(), indices.end(), [ptr](int i, int j)->int {
		return  ptr->points[j].x != ptr->points[i].x ? (ptr->points[j].x < ptr->points[i].x) : (i - j > 0 ? 0 : 1);
		});
	Triangle triangle = getSuperTranigle(ptr);
	TriangleIndex ti = { amount ,amount + 1,amount + 2 };
	ptr->points.emplace_back(triangle.pa); ptr->points.emplace_back(triangle.pb); ptr->points.emplace_back(triangle.pc);
	Circumcircle cir = getCircumcircle(ptr, ti);
	std::list<Circumcircle> open = { cir };
	std::list<Circumcircle>closed = {};
	std::list<int> edge = {};
	int m = 0;
	float dx = 0, dy = 0;
	for (int i = indices.size(); i;) {
		m = indices[--i];
		edge.clear();
		for (auto j = open.end(); j != open.begin();) {
			--j;
			dx = ptr->points[m].x - j->center.x;
			if (dx > 0.0 && dx * dx > j->r) {
				closed.emplace_back(*j);
				j = open.erase(j);
				continue;
			}
			dy = ptr->points[m].y - j->center.y;
			if (dx * dx + dy * dy - j->r > EPSILON) continue;
			edge.emplace_back(j->index.pa);
			edge.emplace_back(j->index.pb);
			edge.emplace_back(j->index.pb);
			edge.emplace_back(j->index.pc);
			edge.emplace_back(j->index.pc);
			edge.emplace_back(j->index.pa);
			j = open.erase(j);
		}
		removeEdge(edge);
		for (auto j = edge.end(); j != edge.begin();) {
			ti.pb = *(--j);
			ti.pa = *(--j);
			ti.pc = m;
			open.emplace_back(getCircumcircle(ptr, ti));
		}
	}
	closed.insert(closed.end(), open.begin(), open.end());
	ptr->points.pop_back(); ptr->points.pop_back(); ptr->points.pop_back();

	for (auto j = closed.end(); j != closed.begin(); ) {
		--j;
		if (j->index.pa < amount && j->index.pb < amount && j->index.pc < amount) {
			res.emplace_back(j->index.pa);
			res.emplace_back(j->index.pb);
			res.emplace_back(j->index.pc);
		}
	}
	std::vector<int> edges;
	int  a = -1, b = -1, c = -1;
	int pa = -1, pb = -1, pc = -1;
	Edges e[4];
	for (int i = res.size(); i;) {
		a = res[--i];
		b = res[--i];
		c = res[--i];
		e[0] = { a,b };
		e[1] = { b,c };
		e[2] = { c,a };
		pa = -1, pb = -1, pc = -1;
		for (int j = edges.size(); j;) {
			e[3] = { edges[--j],edges[--j] };
			if (pa == -1 && equals(e[0], e[3])) pa = j;
			if (pb == -1 && equals(e[1], e[3])) pb = j;
			if (pc == -1 && equals(e[2], e[3])) pc = j;
		}
		if (pa == -1) { edges.emplace_back(a); edges.emplace_back(b); }
		if (pb == -1) { edges.emplace_back(b); edges.emplace_back(c); }
		if (pc == -1) { edges.emplace_back(c); edges.emplace_back(a); }
	}
	return edges;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Delaunay三角剖分是计算机图形学中常用的方法,它是将点云为无重叠的三角形集合的过程。对于三维点云而言,我们可以利用C++语言编写Delaunay三角剖分的源代码。 具体而言,我们需要借助第三方库来完成这个过程。例如,我们可以使用CGAL库中的Delaunay_triangulation_3类来实现三维点云Delaunay三角剖分。在使用该类之前,我们需要将点云为一系列顶点,将顶点作为参数传入Delaunay_triangulation_3类的对象中。 在通过Delaunay_triangulation_3类计算Delaunay三角剖分后,我们可以通过遍历三角形集合,计算每个三角形的顶点坐标和法向量,从而得到三维点云的表面重建结果。 需要注意的是,Delaunay三角剖分的结果可能会产生“拟合问题”,即存在一些三角形的边缘与点云的表面重建结果不完全吻合。为了解决这个问题,我们可以使用一些优方法,例如对三角形的边缘进行局部调整,以提高重建结果的精度。 总之,通过编写三维点云Delaunay三角剖分的源代码,我们可以将点云为一系列无重叠的三角形,从而实现三维模型的重建。 ### 回答2: 三维点云 delaunay 三角剖分是一种将无序的三维点云数据转三角形面片的方法,可以在三维建模、地质勘探等领域中应用。其源代码一般采用 C++ 编写,下面简单介绍其实现。 三维点云 delaunay 三角剖分主要分为以下几步: 1. 构建超级三角形。为了保证所有点都在三角剖分内部,需要在点云的边界之外添加一个超级三角形(一般为一个比点云面积大的等边三角形),保证所有点都在其内部。 2. 将点逐一插入。从点云中随机选取一个点开始,将其插入到当前三角剖分中。插入过程中会检查新插入点与其它三角形的关系,同时进行三角形翻转和边的反转等操作以维护 delaunay 三角剖分的特性。 3. 剖分收敛。当所有点都插入完成后,需要对剩余的三角形进行处理,将所有与超级三角形相交的三角形删除,以得到最终的三角剖分结果。 其源代码主要包括点的数据结构定义、超级三角形的构建、插入点和剖分收敛等函数的实现。在实现中需要注意,对于边界点或重复点等特殊情况需要进行处理,同时可根据具体应用场景做出一些优。 ### 回答3: 三维点云Delaunay三角剖分源代码是一个算法实现,可以将一个三维点云数据集转为一组无重叠三角形的连接。这个算法通常由C++实现,并且主要包含以下步骤: 首先,需要定义一个三维点云数据结构,用于存储所有的点。然后,通过半边数据结构来表示三角形的连接关系,并创建一个起始三角形,该三角形的外接圆可以囊括所有的点。 接下来,使用一个扫描线算法来生成三角剖分。该算法主要通过在扫描线上移动,并利用拐角点的概念来不断更新Delaunay三角形网格。在每个点上,都会查找在当前点的左侧和右侧的最高顶点,并通过一个旋转操作来更新三角形的连接关系。 通过以上步骤,就可以生成一个Delaunay三角剖分,其中每个三角形都与它的外接圆不包含任何点。这个算法在计算几何和计算机图形学领域十分流行,并且有多种优和扩展,可以更好地满足具体应用的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值