c++ - tabla - Calcule las normales de superficie a partir de la imagen de profundidad utilizando píxeles vecinos producto cruzado
superficie corporal solo con peso (1)
Realmente no necesita usar el producto cruzado para esto, pero vea abajo.
Considere que su rango de imagen es una función z (x, y).
La normal a la superficie está en la dirección (-dz / dx, -dz / dy, 1). (Donde por dz / dx quiero decir el diferencial: la tasa de cambio de z con x). Y luego las normales se normalizan convencionalmente a la longitud de la unidad.
Por cierto, si te preguntas de dónde viene eso (-dz / dx, -dz / dy, 1) ... si tomas los 2 vectores tangentes ortogonales en el plano paralelo a los ejes xey, esos son (1 , 0, dzdx) y (0,1, dzdy). La normalidad es perpendicular a las tangentes, por lo que debe ser (1,0, dzdx) X (0,1, dzdy) - donde ''X'' es producto cruzado - que es (-dzdx, -dzdy, 1). De modo que su producto derivado es normal, pero hay poca necesidad de calcularlo tan explícitamente en el código cuando puede usar directamente la expresión resultante para el normal.
El seudocódigo para calcular una unidad de longitud normal en (x, y) sería algo así como
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
Dependiendo de lo que intente hacer, podría tener más sentido reemplazar los valores de NaN con solo un número grande.
Usando ese enfoque, desde tu imagen de rango, puedo obtener esto:
(Entonces estoy usando las direcciones normales calculadas para hacer un sombreado simple, tenga en cuenta la apariencia "steppy" debido a la cuantización de la imagen de rango, idealmente tendría una precisión mayor que 8 bits para los datos de rango real).
Lo sentimos, no es código OpenCV o C ++, sino solo por completar: el código completo que produjo esa imagen (GLSL incrustado en un archivo Qt QML, se puede ejecutar con qmlscene de Qt5) está a continuación. El pseudocódigo anterior se puede encontrar en la función main()
del sombreador de fragmentos:
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);
}"
}
}
Como el título dice, quiero calcular las normales de superficie de una imagen de profundidad dada usando el producto cruzado de los píxeles vecinos. Me gustaría utilizar Opencv para eso y evitar el uso de PCL, sin embargo, realmente no entiendo el procedimiento, ya que mi conocimiento es bastante limitado en el tema. Por lo tanto, le agradecería que alguien pudiera dar algunos consejos. Para mencionar aquí que no tengo ninguna otra información, excepto la imagen de profundidad y la imagen rgb correspondiente, por lo que no hay información de la matriz K
cámara.
Por lo tanto, digamos que tenemos la siguiente imagen de profundidad:
y quiero encontrar el vector normal en un punto correspondiente con un valor de profundidad correspondiente como en la siguiente imagen:
¿Cómo puedo hacer eso usando el producto cruzado de los píxeles vecinos? No me importa si las normales no son muy precisas.
Gracias.
Actualizar:
Ok, estaba tratando de seguir la respuesta de @ timday y portar su código a Opencv. Con el siguiente código:
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);
Estoy obteniendo el siguiente resultado correcto (tuve que reemplazar el double
con float
y el Vecd
a Vecf
, no sé por qué eso haría la diferencia)