gun*_*yim 20 3d graphics matlab opencv computer-vision
我有深度图像,我使用3D CAD数据生成.该深度图像也可以从诸如Kinect或立体相机的深度成像传感器获取.所以基本上它是在成像视图中可见的点的深度图.换句话说,它是来自特定视图的对象的分段点云.
我想确定(估计也将)每个点的表面法线,然后找到该点的切线平面.
我怎样才能做到这一点?我做了一些研究并找到了一些技术,但对它们并不了解(我无法实现).更重要的是我如何在Matlab或OpenCV中做到这一点?我无法使用surfnorm命令设法做到这一点.AFAIK它需要单个表面,我的深度图像中有部分表面.
这是一个示例深度图像.
[编辑]
我想要做的是,在每个点获得表面法线后,我将在这些点处创建切线平面.然后使用这些切平面通过将相邻点的距离之和与切平面相乘来确定该点是否来自平坦区域.
[编辑]
所以在你的问题中有一些未定义的东西,但我会尽力概述答案.
您想要做的基本思路是采用图像的渐变,然后将变换应用于渐变以获得法线向量.在matlab中采用渐变很容易:
[m, g] = imgradient(d);
Run Code Online (Sandbox Code Playgroud)
给出了每个点上图像的梯度(相对于水平和以度为单位测量m)的幅度()和方向(g).例如,如果我们显示图像的渐变幅度,它看起来像这样:

现在,更难的部分是获取有关渐变的信息并将其转换为法线向量.为了正确地做到这一点,我们需要知道如何从图像坐标转换为世界坐标.对于像您这样的CAD生成的图像,此信息包含在用于制作图像的投影变换中.对于像Kinect那样的真实世界图像,您必须查找图像捕获设备的规格.
我们需要的关键信息是:实际坐标中的每个像素有多宽?对于非正交投影(如真实世界图像捕获设备所使用的那些),我们可以通过假设每个像素代表现实世界的固定角度内的光来近似这一点.如果我们知道这个角度(称它为p和弧度测量它),然后通过一个像素所覆盖的真实世界的距离仅仅是sin(p) .* d,或大约p .* d哪里d是图像的每个像素的深度.
现在,如果我们有这个信息,我们可以构造法向量的3个组成部分:
width = p .* d;
gradx = m .* cos(g) * width;
grady = m .* sin(g) * width;
normx = - gradx;
normy = - grady;
normz = 1;
len = sqrt(normx .^ 2 + normy .^ 2 + normz .^ 2);
x = normx ./ len;
y = normy ./ len;
z = normz ./ len;
Run Code Online (Sandbox Code Playgroud)
mattnewport建议的是可以在像素着色器中完成.在每个像素着色器中,您可以计算两个向量A和B,向量的叉积将为您提供法线.计算两个向量的方式如下:
float2 du //values sent to the shader based on depth image's width and height
float2 dv //normally du = float2(1/width, 0) and dv = float2(0, 1/height)
float D = sample(depthtex, uv)
float D1 = sample(depthtex, uv + du)
float D2 = sample(depthtex, uv + dv)
float3 A = float3(du*width_of_image, 0, D1-D)
float3 B = float3(0, dv*height_of_image, D2-D)
float3 normal = AXB
return normal
Run Code Online (Sandbox Code Playgroud)
当深度值不连续时,这将中断.
为了计算像素着色器中的表面是否是平坦的,可以使用二阶偏导数.计算二阶导数的方法是计算有限差分并找出差异,如下:
float D = sample(depthtex, uv)
float D1 = sample(depthtex, uv + du)
float D3 = sample(depthtex, uv - du)
float dx1 = (D1 - D)/du
float dx2 = (D - D3)/du
float dxx = (dx2 - dx1)/du
Run Code Online (Sandbox Code Playgroud)
以同样的方式你必须计算dyy, dxy and dyx.如果,表面是平的dxx = dyy = dxy = dyx = 0.
通常,您选择du和dv为深度图像的1 /宽度和1 /高度.
所有这些都发生在GPU上,这使得一切都非常快.但是如果你不关心它,你也可以在CPU中运行这个方法.唯一的问题是你可以替换类似的函数sample并实现自己的版本.它将深度图像和u,v值作为输入,并在采样点返回深度值.
编辑:
这是一个假设的采样函数,它在CPU上进行最近邻采样.
float Sample(const Texture& texture, vector_2d uv){
return texture.data[(int)(uv.x * texture.width + 0.5)][(int)(uv.y * texture.height + 0.5];
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6742 次 |
| 最近记录: |