正如标题所说,我想通过使用相邻像素的叉积来计算给定深度图像的表面法线.我想使用Opencv并避免使用PCL,但是我并不真正理解这个过程,因为我的知识在这个主题上非常有限.因此,如果有人可以提供一些提示,我将不胜感激.这里要提到的是除了深度图像和相应的rgb图像之外我没有任何其他信息,所以没有K
相机矩阵信息.
因此,假设我们有以下深度图像:
并且我想在相应的点找到具有相应深度值的法向量,如下图所示:
如何使用相邻像素的叉积来做到这一点?我不介意法线是否高度准确.
谢谢.
更新:
好吧,我试图关注@ timday的答案并将他的代码移植到Opencv.使用以下代码:
Mat depth =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 (x+1, y) - depth.at (x-1, y)) / 2.0; float dzdy = (depth.at (x, y+1) - depth.at (x, y-1)) / 2.0; Vec3f d(-dzdx, -dzdy, 1.0f); Vec3f n = normalize(d); normals.at (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)来自哪里......如果你将平面parellel中的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); }" } }