首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何估计/确定深度图像点处的曲面法线和切平面?

如何估计/确定深度图像点处的曲面法线和切平面?
EN

Stack Overflow用户
提问于 2014-05-28 00:23:26
回答 4查看 8.1K关注 0票数 21

我有一个深度图像,它是我用3DCAD数据生成的。此深度图像也可以从深度成像传感器(如Microsoft Kinect或任何其他立体相机)获取。因此,基本上它是成像视图中可见点的深度图。换句话说,它是从特定视图中分割的对象的点云。

我想确定(估计也会做)每个点的曲面法线,然后找到该点的切平面。

我该怎么做呢?我做了一些研究,发现了一些技术,但我不能很好地理解它们(我无法实现它)。更重要的是,我如何在Matlab或OpenCV中做到这一点?我无法使用surfnorm命令做到这一点。AFAIK它需要一个单一的表面,我在我的深度图像中有部分表面。

这是一个深度图像示例。

编辑

我想做的是,在我得到每个点的曲面法线后,我将在这些点处创建切平面。然后使用这些切线平面,通过取相邻点到切线平面的距离之和来确定该点是否来自平坦区域。

EN

回答 4

Stack Overflow用户

发布于 2014-05-28 03:36:01

所以在你的问题中有几个东西是没有定义的,但我会尽我最大的努力来概述一下答案。

你想做的事情的基本思想是取图像的梯度,然后对梯度应用变换以获得法向量。在matlab中获取梯度很容易:

代码语言:javascript
复制
[m, g] = imgradient(d);

为我们提供图像在每个点的梯度(相对于水平线和度数)的大小(m)和方向(g)。例如,如果我们为你的图像显示梯度的大小,它看起来像这样:

现在,更难的部分是获取我们所拥有的关于梯度的信息,并将其转换为法向量。为了正确地做到这一点,我们需要知道如何从图像坐标转换到世界坐标。对于像您这样的CAD生成的图像,此信息包含在用于生成图像的投影变换中。对于从Kinect获得的真实图像,您必须查找图像捕获设备的规格。

我们需要的关键信息是:在现实世界的坐标中,每个像素到底有多宽?对于非正交投影(如真实世界图像捕获设备使用的投影),我们可以通过假设每个像素表示真实世界固定角度内的光来近似这一点。如果我们知道这个角度(称为p并以弧度测量它),那么像素覆盖的实际距离就是sin(p) .* d,或者大约是p .* d,其中d是图像在每个像素上的深度。

现在,如果我们有了这个信息,我们就可以构造法向量的3个分量:

代码语言:javascript
复制
width = p .* d;
gradx = m .* cos(g) * width;
grady = m .* sin(g) * width;

normx = - gradx;
normy = - grady;
normz = 1;

len = sqrt(normx .^ 2 + normy .^ 2 + normz .^ 2);
x = normx ./ len;
y = normy ./ len;
z = normz ./ len;
票数 8
EN

Stack Overflow用户

发布于 2014-05-31 01:14:32

mattnewport建议的是可以在像素着色器中完成。在每个像素着色器中,计算两个向量A和B,向量的叉积将为您提供法线。计算这两个向量的方法如下所示:

代码语言:javascript
复制
float2 du //values sent to the shader based on depth image's width and height
float2 dv //normally du = float2(1/width, 0) and dv = float2(0, 1/height)
float D = sample(depthtex, uv)
float D1 = sample(depthtex, uv + du)
float D2 = sample(depthtex, uv + dv)
float3 A = float3(du*width_of_image, 0, D1-D)
float3 B = float3(0, dv*height_of_image, D2-D)
float3 normal = AXB
return normal

当深度值中存在不连续时,这将中断。

要计算一个表面是否在像素着色器中是平的,可以使用二阶偏导数。计算二阶导数的方法是通过计算有限差分,然后求出差分,就像这样:

代码语言:javascript
复制
float D = sample(depthtex, uv)
float D1 = sample(depthtex, uv + du)
float D3 = sample(depthtex, uv - du)

float dx1 = (D1 - D)/du
float dx2 = (D - D3)/du
float dxx = (dx2 - dx1)/du

用同样的方法,你必须计算dyy, dxy and dyx。如果为dxx = dyy = dxy = dyx = 0.,则曲面是平坦的

通常,您会选择du和dv为深度图像的1/width和1/height。

所有这些事情都发生在GPU上,这使得一切都变得非常快。但是如果你不关心这一点,你也可以在CPU中运行这个方法。唯一的问题是你要替换一个像sample这样的函数,并实现你自己的版本。它将深度图像和u,v值作为输入,并返回采样点的深度值。

编辑:

这是一个假设的采样函数,它在CPU上执行最近邻采样。

代码语言:javascript
复制
float Sample(const Texture& texture, vector_2d uv){
    return texture.data[(int)(uv.x * texture.width + 0.5)][(int)(uv.y * texture.height + 0.5];
}
票数 5
EN

Stack Overflow用户

发布于 2014-06-06 02:47:22

我将从概念上描述你需要做的事情,并提供opencv相关部分的链接。

要确定点云中给定(3d)点的法线,请执行以下操作:

  1. 创建kd-tree或(tree tree?)表示您的点云,以便您可以高效地计算k个最近的邻居。K的选择应该取决于数据的密度。http://physics.nyu.edu/grierlab/manuals/opencv/classcv_1_1KDTree.html
  2. After http://docs.opencv.org/trunk/modules/flann/doc/flann_fast_approximate_nearest_neighbor_search查询给定点p的k个最近邻点,使用它们来找到最佳拟合平面。您可以使用PCA来完成此操作。Set maxComponents=2。http://physics.nyu.edu/grierlab/manuals/opencv/classcv_1_1PCA.html https://github.com/Itseez/opencv/blob/master/samples/cpp/pca.cpp
  3. 步骤2应该返回两个定义您感兴趣的平面的特征向量。这两个向量的叉积应该是你想要的法向量(估计)。你可以在opencv (Mat::cross) http://docs.opencv.org/modules/core/doc/basic_structures.html

中找到如何计算这个值的信息

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23894389

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档