【问题标题】:Extract face from 3D kinect model从 3D kinect 模型中提取人脸
【发布时间】:2013-05-24 13:15:10
【问题描述】:

我正在使用 Kinect 创建一个人的 3D 模型。我得到的是这样的:

我将它导出到 Wavefront .obj 文件。但我只对脸本身感兴趣,并想删除它周围的所有东西。由于 Kinect 有一个 RGB 相机,我可以毫无问题地在 RGB 中进行人脸检测。但这给了我一个包含面部的 640x480 图像内的矩形,我如何将其转换为 3D 网格?

【问题讨论】:

标签: c++ 3d kinect


【解决方案1】:

不久前我做过类似的事情:

视频here

我做的很简单:

  1. 使用 OpenCV 的 haar 级联检测 RGB 图像中的人脸
  2. 使用检测到的人脸矩形从深度图像中仅裁剪人脸区域
  3. 将深度图像转换为深度值(3d 点云/网格/等)

理想情况下,您会校准两个 (rgb,depth) 流。

我使用的是 libreenect,但这种技术应该适用于 OpenNI、KinectSDK。

我注意到您的应用程序的标题是 KinectFusion,所以我猜您是从 Microsoft Kinect SDK 1.7 中的 Kinect Fusion 示例开始的。应该有一个内置函数来对齐 rgb 和深度流。关于 CV 部分,您可以在线找到 Kinect 到 OpenCV Mat 的转换,例如 Dennis Ippel functionsEgmuCV 也有人脸检测功能。 KinectSDK 本身允许您在 3D 中跟踪人脸,因此我怀疑 SDK 中一定有一些东西用于人脸检测(甚至没有跟踪)。我自己对 Kinect SDK 的使用还不够,所以目前不能更具体。

简而言之:

  1. 检测人脸
  2. 在深度图中隔离人脸区域
  3. 将深度图人脸区域转换为点云/网格/等。

【讨论】:

  • 非常感谢,编辑输入以计算点云可能确实比尝试编辑输出更有意义。但我还不能真正弄清楚如何做到这一点。我对此很陌生。我在下面添加了更多信息。
【解决方案2】:

这是我用来获取深度流并使用 Fusion 处理它的代码:

void processDepth()
{
    NUI_IMAGE_FRAME depthFrame = { 0 };
    ERROR_CHECK( kinect->NuiImageStreamGetNextFrame( depthStreamHandle, 0, &depthFrame ) );

    BOOL nearMode = FALSE;
    INuiFrameTexture *frameTexture = 0;
    kinect->NuiImageFrameGetDepthImagePixelFrameTexture( depthStreamHandle, &depthFrame, &nearMode, &frameTexture );

    NUI_LOCKED_RECT depthData = { 0 };

    frameTexture->LockRect( 0, &depthData, 0, 0 );
    if ( depthData.Pitch == 0 ) {
        std::cout << "zero" << std::endl;
    }

    processKinectFusion( (NUI_DEPTH_IMAGE_PIXEL*)depthData.pBits, depthData.size);

    ERROR_CHECK( kinect->NuiImageStreamReleaseFrame( depthStreamHandle, &depthFrame ) );
}

void processKinectFusion( const NUI_DEPTH_IMAGE_PIXEL* depthPixel, int depthPixelSize) 
{
    // DepthImagePixel 
    HRESULT hr = ::NuiFusionDepthToDepthFloatFrame( depthPixel, width, height, m_pDepthFloatImage,
                        NUI_FUSION_DEFAULT_MINIMUM_DEPTH, NUI_FUSION_DEFAULT_MAXIMUM_DEPTH, TRUE );
    if (FAILED(hr)) {
        throw std::runtime_error( "::NuiFusionDepthToDepthFloatFrame failed." );
    }

    Matrix4 worldToBGRTransform = { 0.0f };
    worldToBGRTransform.M11 = 256 / 512;
    worldToBGRTransform.M22 = 256 / 384;
    worldToBGRTransform.M33 = 256 / 512;
    worldToBGRTransform.M41 = 0.5f;
    worldToBGRTransform.M42 = 0.5f;
    worldToBGRTransform.M44 = 1.0f;
    Matrix4 worldToCameraTransform;

    m_pVolume->GetCurrentWorldToCameraTransform( &worldToCameraTransform );
    hr = m_pVolume->ProcessFrame( m_pDepthFloatImage, NUI_FUSION_DEFAULT_ALIGN_ITERATION_COUNT,
                                    NUI_FUSION_DEFAULT_INTEGRATION_WEIGHT, &worldToCameraTransform );

    worldToCameraTransform;
    if (FAILED(hr)) {
        ++trackingErrorCount;
        if ( trackingErrorCount >= 100 ) {
            trackingErrorCount = 0;
            m_pVolume->ResetReconstruction( &IdentityMatrix(), nullptr );
        }

        return;
    }

    // PointCloud
    hr = m_pVolume->CalculatePointCloud( m_pPointCloud, &worldToCameraTransform );
    if (FAILED(hr)) {
        throw std::runtime_error( "CalculatePointCloud failed." );
    }

// PointCloud
    hr = ::NuiFusionShadePointCloud( m_pPointCloud, &worldToCameraTransform,
                                &worldToBGRTransform, m_pShadedSurface, nullptr );


    if (FAILED(hr)) {
        throw std::runtime_error( "::NuiFusionShadePointCloud failed." );
    }

    INuiFrameTexture * pShadedImageTexture = m_pShadedSurface->pFrameTexture;

    NUI_LOCKED_RECT ShadedLockedRect;
    hr = pShadedImageTexture->LockRect(0, &ShadedLockedRect, nullptr, 0);

    if (FAILED(hr)) {
        throw std::runtime_error( "LockRect failed." );
    }

    pShadedImageTexture->UnlockRect(0);
    }
};

现在我认为最好的方法是编辑/裁剪 depthData.pBits。但我不知道该怎么做。

【讨论】:

    猜你喜欢
    • 2013-10-23
    • 2020-07-16
    • 1970-01-01
    • 2018-07-03
    • 1970-01-01
    • 2023-02-10
    • 2013-08-20
    • 1970-01-01
    • 2018-04-26
    相关资源
    最近更新 更多