我正在使用谷歌探戈平板电脑获取点云数据和RGB相机图像。我想创建房间的3D扫描。为此,我需要将2D图像像素映射到点云点。我将用很多点云和相应的图像来做这个。因此我需要编写一个代码脚本,它有两个输入1.点云和2.从相同方向的同一点采集的图像,脚本应该输出彩色点云。我应该如何处理这个&哪些平台使用起来非常简单?
答案 0 :(得分:5)
以下是将3D点v
映射到相机图像中的2D像素空间的数学运算(假设v
已经包含了外在相机的位置和方向,请参阅底部的注释*):< / p>
// Project to tangent space.
vec2 imageCoords = v.xy/v.z;
// Apply radial distortion.
float r2 = dot(imageCoords, imageCoords);
float r4 = r2*r2;
float r6 = r2*r4;
imageCoords *= 1.0 + k1*r2 + k2*r4 + k3*r6;
// Map to pixel space.
vec3 pixelCoords = cameraTransform*vec3(imageCoords, 1);
cameraTransform
是3x3矩阵的地方:
[ fx 0 cx ]
[ 0 fy cy ]
[ 0 0 1 ]
来自{{3}的fx
,fy
,cx
,cy
,k1
,k2
,k3
}。
pixelCoords
被声明为vec3
,但在齐次坐标中实际上是2D。第三个坐标始终为1,因此可以忽略实际目的。
请注意,如果您想要纹理坐标而不是像素坐标,那么这只是另一个可以提前预乘cameraTransform
的线性变换(从上到下与从下到上)扫描线寻址)。
至于什么“平台”(我松散地解释为“语言”)是最简单的,TangoCameraIntrinsics
似乎是最直接的方式来获取相机像素,虽然看起来人们也成功了使用native API和Unity。
* Java提供的点已经包含深度相机外部变换。从技术上讲,因为当前的开发者平板电脑在深度和彩色图像采集之间共享相同的硬件,所以除非您的设备和场景都是静止的,否则您将无法获得完全匹配的彩色图像。幸运的是,在实践中,大多数应用程序可能会假设相机姿势和场景在一帧时间内都没有足够的变化来显着影响颜色查找。
答案 1 :(得分:2)
这个答案不是原创的,它只是为了方便Unity用户,他们想要正确答案,正如@rhashimoto所提供的那样,为他们解决了问题。我的贡献(希望)是提供代码,通过丢弃所有零结果,将正常的16次乘法和12次加法(给定Unity仅执行4x4矩阵)减少到2次乘法和2次加法。我通过测试运行了不到一百万分,每次检查我的计算与基本矩阵计算一致 - 定义为两个结果之间的绝对差值小于机器epsilon - 我对此感到满意我可以知道@rhashimoto可能会出现并在其中戳一个巨大的洞: - )
如果要来回切换,请记住这是C#,因此USEMATRIXMATH定义必须出现在文件的开头。
鉴于目前只有一个Tango设备,我假设内在函数在所有设备上都是常量,我只是将它们作为常量转储,这样
fx = 1042.73999023438
fy = 1042.96997070313
cx = 637.273986816406
cy = 352.928985595703
k1 = 0.228532999753952
k2 = -0.663019001483917
k3 = 0.642908990383148
是的,它们可以作为常量转储,这会使事情更具可读性,C#可能足够聪明,可以优化它 - 但是,我花了太多时间在Agner Fogg的东西,并且会永远是偏执狂。
底部注释掉的代码用于测试差异,如果您愿意的话。如果要测试结果,您必须取消注释其他内容,并注释掉退货。
再次感谢@rhashimoto,这远比我的好得多
我一直坚持他的逻辑,记住这些是像素坐标,而不是UV坐标 - 他是正确的,你可以预先变换转换以获得标准化的UV值,但是因为他已经训练过我一次,我会坚持在我摆弄太多之前他完全是数学: - )
static public Vector2 PictureUV(Vector3 tangoDepthPoint)
{
Vector2 imageCoords = new Vector2(tangoDepthPoint.x / tangoDepthPoint.z, tangoDepthPoint.y / tangoDepthPoint.z);
float r2 = Vector2.Dot(imageCoords, imageCoords);
float r4 = r2*r2;
float r6 = r2*r4;
imageCoords *= 1.0f + 0.228532999753952f*r2 + -0.663019001483917f*r4 + 0.642908990383148f*r6;
Vector3 ic3 = new Vector3(imageCoords.x,imageCoords.y,1);
#if USEMATRIXMATH
Matrix4x4 cameraTransform = new Matrix4x4();
cameraTransform.SetRow(0,new Vector4(1042.73999023438f,0,637.273986816406f,0));
cameraTransform.SetRow(1, new Vector4(0, 1042.96997070313f, 352.928985595703f, 0));
cameraTransform.SetRow(2, new Vector4(0, 0, 1, 0));
cameraTransform.SetRow(3, new Vector4(0, 0, 0, 1));
Vector3 pixelCoords = cameraTransform * ic3;
return new Vector2(pixelCoords.x, pixelCoords.y);
#else
//float v1 = 1042.73999023438f * imageCoords.x + 637.273986816406f;
//float v2 = 1042.96997070313f * imageCoords.y + 352.928985595703f;
//float v3 = 1;
return new Vector2(1042.73999023438f * imageCoords.x + 637.273986816406f,1042.96997070313f * imageCoords.y + 352.928985595703);
#endif
//float dx = Math.Abs(v1 - pixelCoords.x);
//float dy = Math.Abs(v2 - pixelCoords.y);
//float dz = Math.Abs(v3 - pixelCoords.z);
//if (dx > float.Epsilon || dy > float.Epsilon || dz > float.Epsilon)
// UnityEngine.Debug.Log("Well, that didn't work");
//return new Vector2(v1, v2);
}
最后要注意的是,请注意他提供的代码是GLSL - 如果您只是将它用于漂亮的图片,请使用它 - 这适用于那些实际需要执行其他处理的人。