为什么Kinect彩色图像和深度图像无法正确对齐?

5
我已经花了很长时间解决这个问题,但创意已经用尽,希望有人能指点我正确的方向。我一直在使用Kinect并试图将数据捕获到MATLAB中。幸运的是,有很多方法可以做到这一点(我目前正在使用http://www.mathworks.com/matlabcentral/fileexchange/30242-kinect-matlab)。当我试图将捕获的数据投影到三维时,我的传统方法给出了差劲的重建结果。 简而言之,我编写了一个Kinect SDK包装器,用于MATLAB执行重建和对齐操作。重建非常完美,但是对齐存在许多问题,如下所示:
请不要太仔细地观察模型:(。
可以看到,对齐是不正确的。我不确定为什么会出现这种情况。我阅读了很多论坛,在同样的方法中,其他人似乎比我更成功。
我目前的流程是使用Kinect Matlab(使用Openni)来捕获数据,使用Kinect SDK进行重建,然后使用Kinect SDK进行对齐(通过NuiImageGetColorPixelCoordinateFrameFromDepthPixelFrameAtResolution)。我怀疑这可能是由于Openni引起的,但我在创建使用Kinect SDK进行捕获的mex函数调用方面取得了很少成功。
如果有人能指点我应该深入研究的方向,那将不胜感激。
编辑:
我想发布一些代码。这是我用于对齐的代码:
    /* The matlab mex function */
    void mexFunction( int nlhs, mxArray *plhs[], int nrhs, 
            const mxArray *prhs[] ){

        if( nrhs < 2 )
        {
            printf( "No depth input or color image specified!\n" );
            mexErrMsgTxt( "Input Error" );
        }

        int width = 640, height = 480;

        // get input depth data

        unsigned short *pDepthRow = ( unsigned short* ) mxGetData( prhs[0] );
        unsigned char *pColorRow = ( unsigned char* ) mxGetData( prhs[1] );

        // compute the warping

        INuiSensor *sensor = CreateFirstConnected();
        long colorCoords[ 640*480*2 ];
        sensor->NuiImageGetColorPixelCoordinateFrameFromDepthPixelFrameAtResolution(
                NUI_IMAGE_RESOLUTION_640x480, NUI_IMAGE_RESOLUTION_640x480, 
                640*480, pDepthRow, 640*480*2, colorCoords );
        sensor->NuiShutdown();
        sensor->Release();

        // create matlab output; it's a column ordered matrix ;_;

        int Jdimsc[3];
        Jdimsc[0]=height;
        Jdimsc[1]=width;
        Jdimsc[2]=3;

        plhs[0] = mxCreateNumericArray( 3, Jdimsc, mxUINT8_CLASS, mxREAL );
        unsigned char *Iout = ( unsigned char* )mxGetData( plhs[0] );

        for( int x = 0; x < width; x++ )
            for( int y = 0; y < height; y++ ){

                int idx = ( y*width + x )*2;
                long c_x = colorCoords[ idx + 0 ];
                long c_y = colorCoords[ idx + 1 ];

                bool correct = ( c_x >= 0 && c_x < width 
                        && c_y >= 0 && c_y < height );
                c_x = correct ? c_x : x;
                c_y = correct ? c_y : y;

                Iout[ 0*height*width + x*height + y ] =
                        pColorRow[ 0*height*width + c_x*height + c_y ];
                Iout[ 1*height*width + x*height + y ] =
                        pColorRow[ 1*height*width + c_x*height + c_y ];
                Iout[ 2*height*width + x*height + y ] =
                        pColorRow[ 2*height*width + c_x*height + c_y ];

            }

    }

如果别人的回答对你的问题有帮助并解决了你的问题,你应该让其他人知道。如果没有解决你的问题,那么为什么呢?这就是这个社区的运作方式。 - masad
给masad:是的,谢谢你的回复。我还没有确认你的答案是否有效,但我现在正在这样做。稍后会告诉你结果。 - vsector
3个回答

5
这是立体视觉系统中一个众所周知的问题。我之前也遇到了相同的问题。原始问题发布在这里。我当时试图做的与此类似,但经过大量研究,我得出结论:捕获的数据集不容易对齐。
另一方面,在记录数据集时,您可以轻松使用函数调用来对齐RGB和深度数据。此方法在OpenNI和Kinect SDK中均可用(功能相同,但每个函数调用的名称不同)。
看起来您正在使用Kinect SDK来捕获数据集,要使用Kinect SDK对齐数据,可以使用MapDepthFrameToColorFrame
既然您还提到了使用OpenNI,请查看AlternativeViewPointCapability
我没有使用过Kinect SDK,但是通过在注册记录器节点之前进行以下函数调用,OpenNI v1.5解决了整个问题:
depth.GetAlternativeViewPointCap().SetViewPoint(image);

其中 image 是图像生成器节点,而 depth 是深度生成器节点。这是使用旧版SDK的操作,已被 OpenNI 2.0 SDK 取代。因此,如果您正在使用最新的SDK,则函数调用可能会有所不同,但整个过程可能类似。

我还添加了一些示例图像:

如果没有使用以上对齐函数调用,则RGB上的深度边缘未对齐 Without Alignment

当使用函数调用时,深度边缘完全对齐(存在一些红外阴影区域显示一些边缘,但它们只是无效深度区域) With Alignment


谢谢您的回复。我已经尝试过对齐,但没有成功。事实证明,我使用了正确的调用,但Kinect有一个额外的“陷阱”。在“常见NUI问题和FAQ”(http://social.msdn.microsoft.com/Forums/en-US/4da8c75e-9aad-4dc3-bd83-d77ab4cd2f82/common-nui-problems-and-faq)中,它列出了从Kinect获取的深度值必须通过位移3来进行位移,因为玩家的索引也存储在深度值中。调整后,使用我的原始代码进行的对齐工作正常。如果没有您提供的链接,我就不会发现这一点:)。 - vsector
1
我将把您的回复设置为答案,因为它还提供了对齐问题的重要背景、示例和代码。 - vsector

1
您可以使用Kinect SDK读取U、V纹理映射参数,轻松地对齐深度帧和彩色帧。对于深度帧D(i,j)的每个像素坐标(i,j),相应的彩色帧的像素坐标为(U(i,j),V(i,j)),因此颜色由C(U(i,j),V(i,j))给出。
U、V函数包含在每个Kinect的硬件中,它们因工厂在硬件板上粘贴时的微小差异而从Kinect到Kinect不同地对齐深度相机与视频相机。但是,如果您从Kinect SDK读取U、V,则不必担心这一点。
下面我将为您提供一个图像示例和一个在Java中使用{{link1:J4K开源库}}的Kinect SDK实际源代码示例:
public class Kinect extends J4KSDK{

    VideoFrame videoTexture; 

public Kinect() { 
    super(); 
    videoTexture=new VideoFrame(); 
}

@Override 
public void onDepthFrameEvent(short[] packed_depth, int[] U, int V[]) { 
    DepthMap map=new DepthMap(depthWidth(),depthHeight(),packed_depth); 
    if(U!=null && V!=null) map.setUV(U,V,videoWidth(),videoHeight()); 
} 

@Override 
public void onVideoFrameEvent(byte[] data) {     
    videoTexture.update(videoWidth(), videoHeight(), data); 
} }

以下是显示同一深度视频对齐帧的 3 种不同视角的图像示例: enter image description here

我希望这能帮到你!


1

enter image description here depth.GetAlternativeViewPointCap().SetViewPoint(image);

工作良好,但问题在于它通过 FOCAL_rgb/FOCAL_kinect 对深度图像进行了降采样,并通过视差 d=focal*B/z 来移动深度像素;根据出厂设置,可能还会有轻微的旋转。

因此,在不撤销这些变换的情况下,无法恢复所有三个真实世界坐标。话虽如此,那些不依赖于准确的 x、y 坐标而只考虑 z 坐标的方法(例如分割)可能在偏移的映射中也能很好地工作。此外,它们可以利用颜色和深度来执行更好的分割。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接