web-dev-qa-db-ja.com

隣接ピクセルのクロス積を使用して、深度画像から表面法線を計算します

タイトルのとおり、隣接するピクセルのクロス積を使用して、特定の深度画像の表面法線を計算したいと思います。私はそのためにOpencvを使用し、PCLの使用を避けたいと思っていますが、私の知識はかなり限定されているので、手順は本当にわかりません。したがって、私は誰かがいくつかのヒントを提供できることを感謝しています。ここでは、深度画像と対応するRGB画像以外の情報がないので、Kカメラマトリックス情報はありません。

したがって、次の深度画像があるとします。

enter image description here

次の画像のように、対応する深さの値を持つ対応する点で法線ベクトルを見つけたいです。

enter image description here

隣接するピクセルの外積を使用してどうすればよいですか?法線が非常に正確でなくてもかまいません。

ありがとう。


更新:

わかりました、@ 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);

私は正しい次の結果を得ています(doublefloatに置き換え、VecdVecfに置き換える必要がありましたが、なぜそれが違いをもたらすのかわかりませんただし):

enter image description here

23
ThT

このためにクロス積を使用する必要は実際にはありませんが、以下を参照してください。

距離画像が関数z(x、y)であるとします。

サーフェスの法線は方向(-dz/dx、-dz/dy、1)です。 (dz/dxとは微分を意味します:xに対するzの変化率)。そして、法線は通常、単位長に正規化されます。

ちなみに、(-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=(-dzdx,-dzdy,1.0)
magnitude=sqrt(direction.x**2 + direction.y**2 + direction.z**2)
normal=direction/magnitude
_

何をしようとしているのかによっては、NaN値をいくつかの大きな数値に置き換える方が理にかなっている場合があります。

そのアプローチを使用して、あなたの距離画像から、私はこれを得ることができます:

---(enter image description here

(次に、計算された法線方向を使用して単純なシェーディングを行います。距離画像の量子化による「ステップ状」の外観に注意してください。実際の範囲データでは、8ビットよりも高い精度が理想的です)。

申し訳ありませんが、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);
     }"
  }
}
_
24
timday