将Shader中的DirectX11 ID3D11Texture2D转换为OpenCV IplImage

时间:2016-03-10 18:14:02

标签: c++ opencv image-processing directx memory-layout

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

我现在唯一的问题是读出渲染的非失真texture2D并使用OpenCV进一步转换为现实世界的3DPose跟踪/映射。有趣的事情是,我已经做了相反的方式,这意味着我已经创建了着色器资源视图与我扭曲的相机imageBuffers为不成比例。但不知怎的,我现在被困在了另一边。

以下是我被困的代码:

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;

to(从失真的相机字符*内存缓冲区读取相机帧):

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

所以这显然意味着,使用char *缓冲区的东西并不完全正确,但经过数小时的测试后,我看不出原因。首先我认为问题可能是因为我的两个相机都是 R8G8B8A8_UNORM 格式,并且RenderTargetTexture设置为 DXGI_FORMAT_R32G32B32A32_FLOAT (这给了我一个DirectX调试错误消息)。但是我已经将RenderTargetTexture更改为DXGI_FORMAT_R8G8B8A8_UNORM,因此CopyResource()函数可以工作,我还保存了Image,看看它在CopyResource()之后的样子。

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

Undistorted ShaderResource Cameraimage as .dds

在这里它是相同的图像只保存为.jpeg(看起来像预期): UNdistored ShaderResource Cameraimage as .jpeg

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

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

似乎正确。正如我上面提到的,为了进行测试,我只将IplImage-&gt; imageData更改为仍然失真的相机char * Buffers,并且转换工作正常。所以它必须与memcpy()部分有关,但我实际上是从我将失真的相机缓冲区复制到GPU ShaderResource的地方复制了它,它就像一个魅力!

注意: CAMERA_CHANNLES是4。

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

欢呼声,

  • 微米。

1 个答案:

答案 0 :(得分:0)

我终于可以实现转换了。当从GPU读取渲染的ShaderResource到我的ID3D11Texture2D * undistortedShaderTex时,映射资源并执行单个

就足够了
    memcpy(buffer, mappedData, (screenWidth_ * screenHeight_ * 4));

而不是Loop从 GPU(swizzled Memory Layout )转换为 CPU内存布局。因此,似乎内存布局已经以某种方式进行了转换。虽然这对我很有意思,因为当我将扭曲的相机图像作为ShaderResources提供给GPU时,我绝对不得不使用上面的循环将其转换为GPU内存布局。

我很高兴它现在有效:)