c++ - 使用相鄰像素交叉積來計算來自深度圖像的表面法線



opencv depth (1)

您並不需要為此使用交叉產品,但請參閱下面的內容。

考慮你的距離圖像是一個函數z(x,y)。

表面法線方向(-dz / dx,-dz / dy,1)。 (其中dz / dx是指差分:z與x的變化率)。 然後,法線按照慣例常態化為單位長度。

順便說一句,如果你想知道(-dz / dx,-dz / dy,1)是從哪裡來的...如果你把平面上的2個正交的切向量與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=(-dxdz,-dydz,1.0)
magnitude=sqrt(direction.x**2 + direction.y**2 + direction.z**2)
normal=direction/magnitude

根據你想要做的事情,用更大的數字代替NaN值可能更有意義。

使用這種方法,從你的距離圖像,我可以得到這個:

(然後我使用計算的正常方向做一些簡單的陰影;由於距離圖像的量化,請注意“steppy”外觀;理想情況下,實際距離數據的精度要高於8-bit)。

對不起,不是OpenCV或C ++代碼,只是為了完整性:生成該圖像的完整代碼(嵌入在Qt QML文件中的GLSL;可以在Qt5的qmlscene中運行)如下。 上面的偽代碼可以在片段著色器的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);
     }"
  }
}

正如標題所說,我想通過使用相鄰像素的叉積來計算給定深度圖像的表面法線。 我想用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);

我得到了正確的以下結果(我不得不用floatVecd替換double Vecf ,我不知道為什麼會有所作為,雖然):





cross-product