Как видно из заголовка, я хочу вычислить нормали поверхности данного изображения глубины, используя перекрестное произведение соседних пикселей. Я хотел бы использовать 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);
Я получаю правильный следующий результат (мне пришлось заменить double
с float
а также Vecd
в Vecf
Я не знаю, почему это будет иметь какое-либо значение, хотя)
Ты не очень необходимость использовать перекрестный продукт для этого, но см. ниже.
Считайте, что ваш диапазон изображения является функцией z (x, y).
Нормаль к поверхности находится в направлении (-dz / dx, -dz / dy, 1). (Где под dz / dx я подразумеваю дифференциал: скорость изменения z с x). И тогда нормали условно нормируются на единицу длины.
Кстати, если вам интересно, откуда это (-dz / dx, -dz / dy, 1) … если вы возьмете 2 ортогональных касательных вектора в плоскости parellel к осям x и y, то это (1 , 0, dzdx) и (0,1, dzd). Нормаль перпендикулярна касательным, поэтому должно быть (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 просто большим числом.
Используя этот подход, из вашего изображения диапазона, я могу получить это:
(Затем я использую обычные направления, рассчитанные для некоторого простого затенения; обратите внимание на «крутой» внешний вид из-за квантования изображения диапазона; в идеале вы должны иметь более высокую точность, чем 8-битные, для данных реального диапазона).
Извините, но не код OpenCV или C ++, но только для полноты: полный код, создавший это изображение (GLSL, встроенный в файл Qt QML; может быть запущен с помощью qmlscene Qt5), приведен ниже. Приведенный выше псевдокод можно найти в фрагменте шейдера. 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);
}"}
}
Других решений пока нет …