2016-01-06 9 views
13

Come dice il titolo, voglio calcolare le normali di superficie di una determinata immagine di profondità utilizzando il prodotto incrociato dei pixel vicini. Mi piacerebbe usare Opencv per questo ed evitare l'uso di PCL tuttavia, non capisco davvero la procedura, dal momento che la mia conoscenza è piuttosto limitata nell'argomento. Pertanto, sarei grato se qualcuno potesse fornire alcuni suggerimenti. Per citare qui che non ho altre informazioni tranne l'immagine di profondità e l'immagine rgb corrispondente, quindi nessuna informazione sulla matrice della telecamera K.Calcola le normali della superficie dall'immagine di profondità utilizzando i pixel adiacenti traversi del prodotto

Così, permette di dire che abbiamo la seguente immagine di profondità:

enter image description here

e voglio trovare il vettore normale in un punto corrispondente con un valore di profondità corrispondente, come nell'immagine seguente:

enter image description here

Come posso farlo utilizzando il prodotto incrociato dei pixel adiacenti? Non mi importa se le normali non sono molto accurate.

Grazie.


Aggiornamento:

Ok, stavo cercando di seguire @ risposta e la porta il suo codice di timday a Opencv. Con il seguente codice:

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

sto ottenendo il risultato corretto seguente (ho dovuto sostituire double con float e Vecd a Vecf, non so il motivo per cui sarebbe alcuna differenza però):

enter image description here

+0

dipende da cosa OpenCV fa quando si dump un'immagine di vettori. Non sembra che la tua XYZ stia mappando su RGB, e la gamma di componenti positiva/negativa potrebbe non essere ben mappata a valori di 0-255 pixel senza alcun ridimensionamento. Ecco perché il mio codice include anche un semplice modello di ombreggiatura per produrre un'immagine in scala di grigi dalle normali. – timday

+0

Salve @timday Non penso che sia un problema di Opencv perché se caricassi le normali da uno script Matlab a Opencv e 'imshow()' loro allora sto ottenendo una bella immagine rispetto a quella sopra. – ThT

+1

È possibile trovare [questo] (http://stackoverflow.com/q/31217122/2571705) utile. – dhanushka

risposta

14

In realtà non è necessario utilizzare questo prodotto incrociato, ma vedere di seguito.

Considerare che l'immagine dell'intervallo è una funzione z (x, y).

La normale alla superficie è nella direzione (-dz/dx, -dz/dy, 1). (Dove per dz/dx intendo il differenziale: il tasso di variazione di z con x). E poi le normali sono convenzionalmente normalizzate alla lunghezza unitaria.

Per inciso, se vi state chiedendo da che parte (-dz/dx, -dz/dy, 1) proviene da ... se prendete i 2 vettori tangenti ortogonali nel piano parellel agli assi xey, quelli sono (1,0, dzdx) e (0,1, dzdy). Il normale è perpendicolare alle tangenti, quindi dovrebbe essere (1,0, dzdx) X (0,1, dzdy) - dove 'X' è un prodotto incrociato - che è (-dzdx, -dzdy, 1). Quindi il tuo prodotto incrociato è derivato normale, ma c'è poco bisogno di calcolarlo in modo così esplicito nel codice quando puoi semplicemente usare direttamente l'espressione risultante per la norma.

Pseudocodice per calcolare un'unità di lunghezza normale (x, y) sarebbe qualcosa di simile

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 

seconda di cosa si sta cercando di fare, potrebbe avere più senso per sostituire i valori della NaN con solo un gran numero.

Utilizzando questo approccio, dall'immagine gamma, posso ottenere questo:

enter image description here

(sto quindi usando le indicazioni normali calcolate per fare qualche semplice ombreggiatura; notare la comparsa "Steppy" a causa alla quantizzazione dell'immagine della gamma, idealmente avresti una precisione maggiore di 8 bit per i dati della gamma reale).

Spiacente, non codice OpenCV o C++, ma solo per completezza: il codice completo che ha prodotto quell'immagine (GLSL incorporato in un file QML Qt; può essere eseguito con qmlscene di Qt5) è riportato di seguito. Il pseudocodice di cui sopra può essere trovata nella funzione dello shader frammento 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); 
    }" 
    } 
} 
+0

grazie per la risposta Sto provando ora a portare il tuo codice su opencv. Tuttavia, come farei lo stesso usando il prodotto incrociato poiché ho bisogno di farlo ma in quel modo. – ThT

+0

nell'esempio si utilizza il prodotto incrociato 4-neighbors, giusto? Se volessi usare 8-neighbor, dovrei applicare la stessa procedura per i pixel diagonali, giusto? – ThT

+0

@theodore: un modo per pensarci è che l '"input" per il prodotto incrociato è costituito da due vettori tangenti e il prodotto incrociato genera la normale perpendicolare. Sopra, sto effettivamente usando 4 vicini come punti finali di due vettori tangenti. Non è ovvio come estenderlo a 8 punti vicini ... tuttavia so che ci sono metodi più avanzati che adattano le spline a più punti nel vicinato e quindi usano la superficie normale alla superficie scanalata (esempio in https: // pettorale .irb.hr/datoteka/150807.1-0239.pdf). – timday

Problemi correlati