将 DirectX11 ID3D11Texture2D 从 Shader 转换为 OpenCV IplImage

Converting DirectX11 ID3D11Texture2D from Shader into OpenCV IplImage

本文关键字:OpenCV IplImage 转换 Shader DirectX11 ID3D11Texture2D      更新时间:2023-10-16

简短介绍:我在C++年用Oculus Rift(DirectX)编写了一个增强现实应用程序。我的一个片段着色器计算全向相机模型的不失真。

我现在唯一的问题是读出渲染的不失真纹理2D,并使用OpenCV将其进一步转换为现实世界的3DPose跟踪/映射。有趣的是,我已经以相反的方式做了,这意味着我已经使用扭曲的相机图像缓冲区创建了着色器资源视图以实现不失真。但不知何故,我现在被困在相反的地方。

这是我卡在下面的代码:

void GraphicsAPI::UndistortionForLsdSlam()
{
  // ------------------------------------ [ Version 4 ] 
  // Get Pointer to the rendered Shader Resource (Camera Undistortion)
  ID3D11Resource* renderBuffer;
  renderTextureRight_->GetRenderTargetView()->GetResource(&renderBuffer);
  D3D11_TEXTURE2D_DESC texDesc;
  texDesc.ArraySize = 1;
  texDesc.BindFlags = 0;
  texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
  texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  texDesc.Width = screenWidth_;
  texDesc.Height = screenHeight_;
  texDesc.MipLevels = 1;
  texDesc.MiscFlags = 0;
  texDesc.SampleDesc.Count = 1;
  texDesc.SampleDesc.Quality = 0;
  texDesc.Usage = D3D11_USAGE_STAGING;
  // Copy Data from GPU only, into CPU Accessable Memory 
  ID3D11Texture2D* undistortedShaderTex;
  device_->CreateTexture2D(&texDesc, 0, &undistortedShaderTex);
  devicecontext_->CopyResource(undistortedShaderTex, renderBuffer);
  // SaveDDSTextureToFile(devicecontext_, undistortedShaderTex, L"SCREENSHOT.dds");
  // Map Resource GPU Resource
  D3D11_MAPPED_SUBRESOURCE mappedResource;
  if (FAILED(devicecontext_->Map(undistortedShaderTex, 0, D3D11_MAP_READ, 0, &mappedResource)))
    std::cout << "Error: [CAM 2] could not Map Rendered Camera ShaderResource for Undistortion" << std::endl;
  // Copy Memory of GPU Memory Layout (swizzled) to CPU
  char* buffer = new char[(screenWidth_ * screenHeight_ * CAMERA_CHANNELS)];
  char* mappedData = static_cast<char*>(mappedResource.pData);
  for (UINT i = 0; i < screenHeight_; i++)
  {
    memcpy(buffer, mappedData, screenWidth_ * 4);
    mappedData += mappedResource.RowPitch;
    buffer += screenWidth_ * 4;
  }
  std::cout << "FINISHED LOOP .. " << std::endl;
  devicecontext_->Unmap(undistortedShaderTex, 0);
  // OpenCV IplImage Convertion
  IplImage* frame = cvCreateImageHeader(cvSize(screenWidth_, screenHeight_), IPL_DEPTH_8U, CAMERA_CHANNELS);
  frame->imageData = (char*)buffer;
  frame->imageDataOrigin = frame->imageData;
  cv::Mat mymat = cv::Mat(frame, true); // Access Violation here(!)
  cv::imshow("Shader Undistortion", mymat);

错误消息:

ARift.exe 中0x680EF41C (msvcr120.dll) 处未处理的异常: 0xC0000005:访问冲突读取位置0x1FD4EFFC

访问冲突恰好发生在尝试使用先前创建的 IplFrame 创建 cv::Mat() 时。出于测试目的,我更改了一行代码以使用扭曲的相机缓冲区测试 OpenCV 转换,它起作用了:

frame->imageData = (char*)buffer;

到(从扭曲的相机字符读取相机帧* 内存缓冲区):

frame->imageData = (char*)(ariftcontrol->caminput->lsdslamCameraBuffer);

所以这显然意味着,带有 char* 缓冲区的东西不太正确,但经过几个小时的测试,我看不出为什么。首先,我认为问题可能是因为我的两个相机都是R8G8B8A8_UNORM格式,并且RenderTargetTexture设置为DXGI_FORMAT_R32G32B32A32_FLOAT(这给了我一个DirectX调试错误msg)。但是我已经将 RenderTargetTexture 更改为 DXGI_FORMAT_R8G8B8A8_UNORM,以便 CopyResource() 函数工作,并且我还保存了图像以查看它在 CopyResource() 之后的外观。

这里是DDS(可以看到它显然是我未失真的2D相机图像,但我不确定为什么它看起来像.dds一样奇怪):

未失真的着色器资源相机图像作为.dds

在这里,它只是保存为.jpeg的相同图像(看起来符合预期):将着色器资源相机图像作为.jpeg取消存储

这意味着,从GPU中的ShaderResource到代码行的所有复制。

// SaveDDSTextureToFile(devicecontext_, undistortedShaderTex, L"SCREENSHOT.dds");

似乎是正确的。正如我上面提到的,为了进行测试,我只将 IplImage->imageData 更改为我仍然失真的相机字符*缓冲区,并且转换有效。所以它必须是 memcpy() 部分的东西,但我实际上是从我将扭曲的相机缓冲区复制到 GPU ShaderResource 的地方复制的,这就像一个魅力!

注意:这里有 4 个CAMERA_CHANNLES。

有人能看到我在这里做错了什么吗?我感谢每一个帮助,谢谢!:)

干杯

  • 米.

我终于可以实现转换。当将渲染的着色器资源从 GPU 读取到我的 ID3D11Texture2D* 中时,将资源映射到单个

    memcpy(buffer, mappedData, (screenWidth_ * screenHeight_ * 4));
而不是循环,

以从 GPU(旋转内存布局)转换为 CPU 内存布局。因此,内存布局似乎已经以某种方式进行了转换。虽然这对我来说很有趣,因为当我将扭曲的相机图像作为着色器资源提供给 GPU 时,我肯定必须使用上面的循环将其转换为 GPU 内存布局。

我很高兴它现在可以工作:)