Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/opencv/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 使用相邻像素叉积从深度图像计算曲面法线_C++_Opencv_Depth_Normals_Cross Product - Fatal编程技术网

C++ 使用相邻像素叉积从深度图像计算曲面法线

C++ 使用相邻像素叉积从深度图像计算曲面法线,c++,opencv,depth,normals,cross-product,C++,Opencv,Depth,Normals,Cross Product,正如标题所说,我想通过使用相邻像素的叉积来计算给定深度图像的曲面法线。我希望使用Opencv来实现这一点,并避免使用PCL。然而,我并不真正理解这个过程,因为我在这方面的知识非常有限。因此,如果有人能提供一些提示,我将不胜感激。这里要提到的是,除了深度图像和相应的rgb图像之外,我没有任何其他信息,因此没有K相机矩阵信息 因此,假设我们有以下深度图像: 我想找到对应点的法向量,对应深度值,如下图所示: 如何使用相邻像素的叉积来实现这一点?我不介意法线是否不是很精确 谢谢 更新: 好的,我试

正如标题所说,我想通过使用相邻像素的叉积来计算给定深度图像的曲面法线。我希望使用Opencv来实现这一点,并避免使用PCL。然而,我并不真正理解这个过程,因为我在这方面的知识非常有限。因此,如果有人能提供一些提示,我将不胜感激。这里要提到的是,除了深度图像和相应的rgb图像之外,我没有任何其他信息,因此没有
K
相机矩阵信息

因此,假设我们有以下深度图像:

我想找到对应点的法向量,对应深度值,如下图所示:

如何使用相邻像素的叉积来实现这一点?我不介意法线是否不是很精确

谢谢


更新:

好的,我试着按照@timday的答案,将他的代码移植到Opencv。使用以下代码:

Mat depth = <my_depth_image> of type CV_32FC1
Mat normals(depth.size(), CV_32FC3);

for(int x = 0; x < depth.rows; ++x)
{
    for(int y = 0; y < depth.cols; ++y)
    {

        float dzdx = (depth.at<float>(x+1, y) - depth.at<float>(x-1, y)) / 2.0;
        float dzdy = (depth.at<float>(x, y+1) - depth.at<float>(x, y-1)) / 2.0;

        Vec3f d(-dzdx, -dzdy, 1.0f);
        Vec3f n = normalize(d);

        normals.at<Vec3f>(x, y) = n;
    }
}

imshow("depth", depth / 255);
imshow("normals", normals);
Mat depth=类型CV_32FC1
垫法线(深度大小(),CV_32FC3);
对于(int x=0;x
我得到了正确的以下结果(我必须用
float
替换
double
,并将
Vecd
替换为
Vecf
,但我不知道为什么会有任何不同):

您实际上不需要为此使用叉积,但请参见下文

假设你的距离像是一个函数z(x,y)

曲面的法线方向为(-dz/dx,-dz/dy,1)。(式中,dz/dx表示微分:z与x的变化率)。然后法线按常规标准化为单位长度

顺便说一下,如果你想知道(-dz/dx,-dz/dy,1)是从哪里来的。。。如果在平行于x轴和y轴的平面上取两个正交切线向量,它们是(1,0,dzdx)和(0,1,dzdy)。法线垂直于切线,因此应为(1,0,dzdx)X(0,1,dzdy)-其中“X”为叉积-即(-dzdx,-dzdy,1)。这里有一个由叉积导出的法线,但是当您可以直接使用法线的结果表达式时,几乎不需要在代码中如此显式地计算它

计算(x,y)处单位长度法线的伪代码如下

dzdx=(z(x+1,y)-z(x-1,y))/2.0;
dzdy=(z(x,y+1)-z(x,y-1))/2.0;
direction=(-dzdx,-dzdy,1.0)
magnitude=sqrt(direction.x**2 + direction.y**2 + direction.z**2)
normal=direction/magnitude
根据您尝试执行的操作,用一些大数字替换NaN值可能更有意义

使用这种方法,从您的范围图像中,我可以得到:

(然后我使用计算的法线方向进行一些简单的着色;注意距离图像的量化导致的“steppy”外观;理想情况下,对于真实距离数据,您的精度将高于8位)

对不起,不是OpenCV或C++代码,而是为了完整性:下面是完整的生成该图像的代码(GLSL嵌入Qt QML文件中,可以用Qt5的QMLVIEW运行)。上面的伪代码可以在片段着色器的

main()
函数中找到:

import QtQuick 2.2

Image {
  source: 'range.png'  // The provided image

  ShaderEffect {
    anchors.fill: parent
    blending: false

    property real dx: 1.0/parent.width
    property real dy: 1.0/parent.height
    property variant src: parent

    vertexShader: "
      uniform highp mat4 qt_Matrix;
      attribute highp vec4 qt_Vertex;
      attribute highp vec2 qt_MultiTexCoord0;
      varying highp vec2 coord;
      void main() {
        coord=qt_MultiTexCoord0;
        gl_Position=qt_Matrix*qt_Vertex;
      }"

   fragmentShader: "
     uniform highp float dx;
     uniform highp float dy;
     varying highp vec2 coord;
     uniform sampler2D src;
     void main() {
       highp float dzdx=( texture2D(src,coord+vec2(dx,0.0)).x - texture2D(src,coord+vec2(-dx,0.0)).x )/(2.0*dx);
       highp float dzdy=( texture2D(src,coord+vec2(0.0,dy)).x - texture2D(src,coord+vec2(0.0,-dy)).x )/(2.0*dy);
       highp vec3 d=vec3(-dzdx,-dzdy,1.0);
       highp vec3 n=normalize(d);
       highp vec3 lightDirection=vec3(1.0,-2.0,3.0);
       highp float shading=0.5+0.5*dot(n,normalize(lightDirection));
       gl_FragColor=vec4(shading,shading,shading,1.0);
     }"
  }
}
我认为代码(矩阵计算)是正确的:

def normalization(data):
   mo_chang =np.sqrt(np.multiply(data[:,:,0],data[:,:,0])+np.multiply(data[:,:,1],data[:,:,1])+np.multiply(data[:,:,2],data[:,:,2]))
   mo_chang = np.dstack((mo_chang,mo_chang,mo_chang))
   return data/mo_chang

x,y=np.meshgrid(np.arange(0,width),np.arange(0,height))
x=x.reshape([-1])
y=y.reshape([-1])
xyz=np.vstack((x,y,np.ones_like(x)))
pts_3d=np.dot(np.linalg.inv(K),xyz*img1_depth.reshape([-1]))
pts_3d_world=pts_3d.reshape((3,height,width))
f= pts_3d_world[:,1:height-1,2:width]-pts_3d_world[:,1:height-1,1:width-1]
t= pts_3d_world[:,2:height,1:width-1]-pts_3d_world[:,1:height-1,1:width-1]
normal_map=np.cross(f,l,axisa=0,axisb=0)
normal_map=normalization(normal_map)
normal_map=normal_map*0.5+0.5
alpha = np.full((height-2,width-2,1), (1.), dtype="float32")
normal_map=np.concatenate((normal_map,alpha),axis=2)
  • 我们应该使用名为“K”的摄影机内部函数。我认为f和t的值是基于摄像机坐标系中的3D点

  • 对于法向量,-1,-1100)和(255255100)在8位图像中是相同的颜色,但它们是完全不同的法向量。所以我们应该通过
    normal\u-map=normal\u-map*0.5+0.5
    将法线值映射到(0,1)


  • 欢迎使用通讯。

    感谢您的回答,我现在正尝试将您的代码移植到opencv。但是,既然我需要做叉积,那么我如何使用叉积来做同样的事情。在你的例子中,你使用的是4邻域叉积,对吗?如果我想使用8-邻域,那么我应该对对角线像素应用相同的过程,对吗?@theodore:一种方法是,叉积的“输入”是两个切线向量,叉积生成垂直法线。上面,我有效地使用了4个邻居作为两个切线向量的端点。不清楚如何将其扩展到8个相邻点。。。然而,我知道有更先进的方法,将样条曲线拟合到邻域中的更多点,然后使用样条曲线曲面的法线(例如)。谢谢,我明白了。实际上我脑子里的想法是这样的:
    dzdx=(z(x+1,y)-z(x-1,y))/2.0;dzdy=(z(x,y+1)-z(x,y-1))/2.0;方向=(-dxdz,-dydz,1.0)幅值=sqrt(方向x**2+方向y**2+方向z**2)dzdx1=(z(x+1,y+1)-z(x-1,y-1))/2.0;dzdy1=(z(x-1,y+1)-z(x+1,y-1))/2.0;方向1=(-dxdz1,-dydz1,1.0)震级1=sqrt(方向1.x**2+方向1.y**2+方向1.z**2)法线=(方向/震级)+(方向1/震级1)
    ,但我不知道这有多正确。@先生,因为如果例如dxdz是1.0(而dydz是0.0),然后是45度的坡度,你想要形成一个直角等腰三角形(因此z=1)。法线方向沿三角形的斜边,并且您希望三角形具有单位长度,因此后续的归一化步骤取决于OpenCV在转储向量图像时所做的操作。看起来XYZ没有映射到RGB,如果不进行一些缩放,正/负分量范围可能无法很好地映射到0-255像素值。这就是为什么我的代码还包括一个简单的着色模型,用于从法线生成灰度图像。Hi@timday我不认为这是Opencv问题,因为如果我将法线从Matlab脚本加载到Opencv并
    imshow()