如何从球坐标系中的点云创建体积?

时间:2018-08-27 10:20:50

标签: numpy computational-geometry spherical-coordinate scipy-spatial

我在球坐标系中有两组离散点,每组离散点代表一个对象的顶部和底部表面。

illustration for the point cloud

我试图从这些点到位于对象内部和外部的单独点创建体积。有什么建议在哪里寻找或使用哪个库?

蓝色和红色点分别表示顶部和底部表面。红点是通过使顶面径向向下移动一定半径而产生的。

2 个答案:

答案 0 :(得分:0)

如果我是对的,则蓝色和红色表面是网格状的(并且是水密的)。因此,对于每个点,您都可以从球体中心绘制直线,并寻找与网格的交点。这是通过找到两个三角形使直线穿过它们而完成的(可以通过使用三角形中的点公式仅查看角坐标来完成),然后找到相交点。然后,很容易将点分类为红色表面之前,蓝色之后或中间。

详尽搜索三角形可能会很昂贵。例如,您可以使用边界框或类似设备的层次结构来加快速度。

答案 1 :(得分:0)

这是一种常规的修修补补方法,该方法可以在以下条件下起作用:原始表面中点之间的平均距离远小于体积的厚度以及表面轮廓上的不规则处。换句话说,有很多描述蓝色表面的点。

import matplotlib.pylab as plt

import numpy as np
from scipy.spatial import KDTree

# Generate a test surface:
theta = np.linspace(3, 1, 38)
phi = np.zeros_like(theta)
r = 1 + 0.1*np.sin(8*theta)

surface_points = np.stack((r, theta, phi), axis=1)  #  n x 3 array

# Generate test points:
x_span, y_span = np.linspace(-1, 0.7, 26), np.linspace(0.1, 1.2, 22)
x_grid, y_grid = np.meshgrid(x_span, y_span)

r_test = np.sqrt(x_grid**2 + y_grid**2).ravel()
theta_test = np.arctan2(y_grid, x_grid).ravel()
phi_test = np.zeros_like(theta_test)

test_points = np.stack((r_test, theta_test, phi_test), axis=1)  #  n x 3 array

# Determine if the test points are in the volume:

volume_thickness = 0.2  # Distance between the two surfaces
angle_threshold = 0.05  # Angular threshold to determine for a point 
                        # if the line from the origin to the point 
                        # go through the surface

# Get the nearest point: (replace the interpolation)
get_nearest_points = KDTree(surface_points[:, 1:]) # keep only the angles
# This is based on the cartesian distance,
# and therefore not enterily valid for the angle between points on a sphere
# It could be better to project the points on a unit shpere, and convert 
# all coordinates in cartesian frame in order to do the nearest point seach...

distance, idx = get_nearest_points.query(test_points[:, 1:])

go_through = distance < angle_threshold

nearest_surface_radius = surface_points[idx, 0]

is_in_volume = (go_through) & (nearest_surface_radius > test_points[:, 0]) \
                & (nearest_surface_radius - volume_thickness < test_points[:, 0])

not_in_volume = np.logical_not(is_in_volume)

# Graph;
plt.figure(figsize=(10, 7))
plt.polar(test_points[is_in_volume, 1], test_points[is_in_volume, 0], '.r',
          label='in volume');
plt.polar(test_points[not_in_volume, 1], test_points[not_in_volume, 0], '.k',
          label='not in volume', alpha=0.2);
plt.polar(test_points[go_through, 1], test_points[go_through, 0], '.g',
          label='go through', alpha=0.2);
plt.polar(surface_points[:, 1], surface_points[:, 0], '.b',
          label='surface');
plt.xlim([0, np.pi]); plt.grid(False);plt.legend();

对于2D情况,结果图为:

example test

其想法是通过仅考虑方向而不考虑半径来寻找每个测试点在表面上最接近的点。找到该“相同方向”的点后,就可以测试该点是否位于沿径向方向(volume_thickness)的体积内,以及是否使用参数angle_threshold足够接近曲面。 / p>

我认为最好对蓝色表面进行网格化(非凸)并执行适当的插值,但是我不知道为此使用Scipy方法。