【问题标题】:How to estimate/determine surface normals and tangent planes at points of a depth image?如何估计/确定深度图像点处的表面法线和切平面?
【发布时间】:2014-07-16 15:49:52
【问题描述】:

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

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

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

这是一个示例深度图像。

[编辑]

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

【问题讨论】:

  • 如何定义深度不连续点的表面法线?
  • 哦,你的意思是像边缘这样的点,或者在这张图片中在杯子内部可见的底部?好吧,如果它存在不连续性,那么表面法线对我来说并不重要。所以我不需要定义它。
  • 你试过只取相邻像素的有限差分吗?结果可能会很嘈杂,需要进行一些平滑处理,而且您需要做一些事情来处理不连续性,但它应该为您提供足够好的照明效果。你需要法线做什么?
  • 不知道什么是有限差分,所以没试过。我会调查的。我不会对闪电使用法线,在获得法线后,我将计算那个点的切线表面。然后使用该表面通过取相邻点到该表面的距离之和来确定该点是否平坦。简单地说,我试图了解对象上选择的点是否来自平面。
  • 这个想法是您需要定义可以计算表面法线的区域。通常在计算机图形学中,三角形是最常用的多边形,其中法线由cross product of the two non-parallel sides 计算。但是,出于实施目的,我建议您查看this code from OpenGL forums。 HTH

标签: matlab opencv graphics 3d computer-vision


【解决方案1】:

您可以参考《高效结合位置和法线以获得精确的 3D 几何》中的(5)、(6)和(8):

【讨论】:

    【解决方案2】:

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

    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
    

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

    要在像素着色器中计算表面是否平坦,可以使用二阶偏导数。计算二阶导数的方法是通过计算有限差分并找到差分:

    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 上进行最近邻采样。

    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];
    }
    

    【讨论】:

    • 仅供参考 - this wiki 页面对此有更多信息。您还可以阅读与此相关的Hessian matrices
    • 您能再解释一下您的解决方案吗?哪个变量代表什么?这是什么sample 函数?请给我详细信息。
    • 您需要阅读一些有关纹理采样的信息才能计算出参数。 Here's 纹理采样文档。如果您不在着色器上执行此操作,则必须自己在 CPU 上实现采样功能。
    【解决方案3】:

    我会从概念上描述我认为你必须做的事情,并提供 opencv 相关部分的链接,

    确定点云中给定 (3d) 点的法线:

    1. 创建点云的 kd-tree 或(balltree?)表示,以便您可以有效地计算 k 个最近邻。您对 k 的选择应取决于数据的密度。 http://docs.opencv.org/trunk/modules/flann/doc/flann_fast_approximate_nearest_neighbor_search http://physics.nyu.edu/grierlab/manuals/opencv/classcv_1_1KDTree.html

    2. 查询给定点 p 的 k 最近邻后,使用它们找到最佳拟合平面。您可以使用 PCA 来执行此操作。设置 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

    4. 中找到如何计算的信息

    【讨论】:

      【解决方案4】:

      因此,您的问题中有一些未定义的内容,但我会尽力概述答案。

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

      [m, g] = imgradient(d);
      

      为我们提供图像在每个点的梯度(相对于水平方向,以度为单位)的幅度 (m) 和方向 (g)。例如,如果我们显示图像的渐变幅度,它看起来像这样:

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

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

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

      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;
      

      【讨论】:

      • 好吧,你可以问我未定义的部分吗?我以为我已经定义了所有必要的信息。我无法尝试您提供的内容,因为我的 matlab 版本不包含成分运算符,但我很快就会有新版本,所以我可以试试。但是在您的解决方案中有这部分我不明白。您说过表面法线取决于视角,凭直觉我可以说表面法线与视角无关,并且应该始终相同。我错了吗?
      • 未定义部分是您实际使用这些向量的目的,以及生成图像的设备。如果您不想等待,您可能可以实现自己的imgradient 函数。表面法线不取决于视角,而是取决于用于生成图像的投影:如果您使用正交投影而不是透视投影来生成图像,尽管真实法线相同,但您会得到不同的图像在两者中,因此您需要知道恢复法线的投影。
      • 你能提供一些我可以更好地阅读和理解的文件吗?
      • 了解哪一部分?
      • 使用渐变和视角恢复法线。我不明白它是如何完成的。我的意思是它背后的理论。
      猜你喜欢
      • 1970-01-01
      • 2013-12-23
      • 1970-01-01
      • 2015-04-15
      • 2016-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多