2016-01-06 5 views
13

Wie der Titel sagt, möchte ich die Oberflächennormalen eines gegebenen Tiefenbildes berechnen, indem ich das Kreuzprodukt benachbarter Pixel verwende. Ich würde gerne Opencv dafür verwenden und die Verwendung von PCL vermeiden, aber ich verstehe das Verfahren nicht wirklich, da mein Wissen in dem Thema ziemlich begrenzt ist. Daher wäre ich dankbar, wenn jemand einige Hinweise geben könnte. Zu erwähnen ist hier, dass ich außer dem Tiefenbild und dem entsprechenden RGB-Bild keine weiteren Informationen habe, also keine K Kameramatrix-Informationen.Berechnen von Oberflächennormalen aus dem Tiefenbild mit benachbarten Pixeln cross product

So kann sagen, dass wir die folgende Tiefenbild haben:

enter image description here

und ich mag mit einem entsprechenden Tiefenwert den Normalvektor an einem entsprechenden Punkt zu finden, wie im folgende Bild:

enter image description here

Wie kann ich das mit dem Kreuzprodukt der benachbarten Pixel tun? Es macht mir nichts aus, wenn die Normalen nicht sehr genau sind.

Danke.


Update:

Ok, ich habe versucht, @ timday Antwort und Port seinen Code zu OpenCV zu folgen. Mit dem folgenden Code:

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); 

ich das richtige folgende Ergebnis bekommen (ich hatte double mit float und Vecd zu Vecf ersetzen, ich weiß nicht, warum das einen Unterschied allerdings machen würde):

enter image description here

+0

Hängt was OpenCV tut, wenn Sie ein Bild von Vektoren Dump. Es sieht nicht so aus, als ob XYZ auf RGB mappt, und der positive/negative Komponentenbereich kann möglicherweise nicht ohne Skalierung auf Werte von 0-255 Pixel abgebildet werden. Deshalb enthält mein Code auch ein einfaches Schattierungsmodell, um ein Graustufenbild von den Normalen zu erzeugen. – timday

+0

Hi @timday Ich denke nicht, dass es ein Opencv-Problem ist, denn wenn ich die Normalen von einem Matlab-Skript zu Opencv und 'imshow()' lade, dann bekomme ich ein schönes Bild im Vergleich zu dem oben genannten. – ThT

+1

Sie könnten [this] (http://stackoverflow.com/q/31217122/2571705) nützlich finden. – dhanushka

Antwort

14

Sie brauchen nicht wirklich brauchen, um das Cross-Produkt für diese verwenden, aber siehe unten.

Betrachten Sie Ihr Bereich Bild ist eine Funktion z (x, y).

Die Normale zur Oberfläche ist in der Richtung (-dz/dx, -dz/dy, 1). (Wo bei dz/dx meine ich das Differential: die Änderungsrate von z mit x). Und dann werden Normalen üblicherweise auf Einheitslänge normiert.

Übrigens kann, wenn man sich fragen, wo das (-DZ/dx, -DZ/dy, 1) kommt von ... Wenn Sie nehmen die 2 orthogonalen Tangentenvektoren in der Ebene parellel zu den x- und y-Achsen, das sind (1,0, dzdx) und (0,1, dzdy). Die Normale ist senkrecht zu den Tangenten, also sollte (1,0, dzdx) X (0,1, dzdy) sein - wobei 'X' ein Kreuzprodukt ist - was (-dzdx, -dzdy, 1) ist. Ihr Cross-Produkt ist also normal abgeleitet, aber es ist wenig nötig, es so explizit im Code zu berechnen, wenn Sie den resultierenden Ausdruck einfach direkt für den Normalwert verwenden können.

Pseudocode eine Einheit Länge Normal bei (x, y) zu berechnen, wäre so etwas wie

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 

Je nachdem, was Sie zu tun versuchen, könnte es mehr Sinn machen, die NaN-Werte mit ersetzen nur eine große Anzahl.

diesen Ansatz verwenden, aus dem Bereich Bild, kann ich diese:

enter image description here

(Ich verwende dann die normalen Richtungen einige einfache Schattierung zu tun berechnet, beachten Sie die „steppy“ Aussehen durch zur Quantisierung des Bereichsbildes, idealerweise hätte man eine höhere Genauigkeit als 8-Bit für die realen Bereichsdaten.

Sorry, nicht OpenCV oder C++ - Code, aber nur der Vollständigkeit halber: der vollständige Code, der dieses Bild erzeugte (GLSL eingebettet in eine Qt QML-Datei; kann mit Qt5's qmlscene ausgeführt werden) ist unten. Der Pseudo-Code oben in den Fragment-Shader des main() Funktion zu finden:

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); 
    }" 
    } 
} 
+0

danke für die Antwort Ich versuche jetzt, Ihren Code nach opencv zu portieren. Allerdings würde ich das gleiche tun, indem ich das Cross-Produkt verwende, da ich es aber so machen muss. – ThT

+0

in Ihrem Beispiel verwenden Sie 4-Nachbarn-Cross-Produkt, richtig? Wenn ich 8-Nachbarn verwenden wollte, dann sollte ich das gleiche Verfahren für die diagonalen Pixel anwenden, richtig? – ThT

+0

@theodore: Nun eine Möglichkeit, daran zu denken ist, dass die "Eingabe" zum Kreuzprodukt zwei Tangentenvektoren ist und das Kreuzprodukt die Senkrechtnormale erzeugt. Oben verwende ich effektiv 4 Nachbarn als die Endpunkte von zwei Tangentenvektoren. Es ist nicht klar, wie man das auf 8 benachbarte Punkte ausdehnt ... aber ich weiß, dass es fortschrittlichere Methoden gibt, die Splines an mehr Punkte in der Nachbarschaft anpassen und dann die Normale zur Spline-Oberfläche verwenden (Beispiel unter https: // bib .irb.hr/datoteka/150807.1-0239.pdf). – timday

Verwandte Themen