您实际上并不需要为此使用叉积,但请参见下文。
考虑你的范围图像是一个函数 z(x,y)。
表面的法线方向为 (-dz/dx,-dz/dy,1)。(这里的 dz/dx 是指微分:z 与 x 的变化率)。然后法线通常被归一化为单位长度。
顺便说一句,如果您想知道 (-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 值替换为一些较大的数字可能更有意义。
使用这种方法,从您的范围图像中,我可以得到:
(然后我使用计算出的法线方向进行一些简单的着色;注意由于距离图像的量化而导致的“阶梯式”外观;理想情况下,对于实际距离数据,您的精度会高于 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);
}"
}
}