Преобразование DirectX11 ID3D11Texture2D из Shader в OpenCV IplImage

Краткое введение. Я написал приложение дополненной реальности с Oculus Rift на C++ (DirectX). Один из моих фрагментных шейдеров вычисляет неискажение для модели всенаправленной камеры.

Единственная проблема, с которой я столкнулся сейчас, - это прочитать визуализированную неискаженную текстуру 2D и преобразовать ее для 3DPose Tracking/Mapping реального мира с использованием OpenCV. Самое смешное, что я уже сделал наоборот, а это значит, что я уже создал представления ресурсов шейдера с моими искаженными буферами изображения камеры для неискажения. Но почему-то я застрял сейчас в обратном направлении.

Вот код, на котором я застрял:

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);

Сообщение об ошибке:

Необработанное исключение по адресу 0x680EF41C (msvcr120.dll) в ARift.exe: 0xC0000005: место чтения с нарушением прав доступа 0x1FD4EFFC

Нарушение прав доступа происходит именно при попытке создать cv::Mat() с ранее созданным IplFrame. В целях тестирования я изменил одну строку кода, чтобы проверить преобразование OpenCV с моим искаженным буфером камеры, и это сработало:

frame->imageData = (char*)buffer;

to (чтение кадра камеры из искаженного буфера памяти char*):

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

Итак, это, очевидно, означает, что что-то с буфером char* не совсем правильно, но после нескольких часов тестирования я не понимаю, почему. Сначала я подумал, что проблема может заключаться в том, что обе мои камеры имеют формат R8G8B8A8_UNORM, а для RenderTargetTexture установлено значение DXGI_FORMAT_R32G32B32A32_FLOAT (что дало мне сообщение об ошибке отладки DirectX). Но я уже изменил RenderTargetTexture на DXGI_FORMAT_R8G8B8A8_UNORM, чтобы функция CopyResource() работала, и я также сохранил изображение, чтобы посмотреть, как оно выглядит после CopyResource().

Вот оно в формате DDS (видно, что это мое неискаженное 2D-изображение с камеры, но я не уверен, почему оно выглядит так странно, как .dds):

Неискаженное изображение камеры ShaderResource как .dds

А вот это то же самое изображение, только сохраненное как .jpeg (выглядит, как и ожидалось): неискаженное изображение ShaderResource Cameraimage как . jpeg

Это означает, что все копирование из ShaderResource в GPU до строки кода

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

Кажется правильным. И, как я упоминал выше, для тестирования я только изменил IplImage->imageData на мои все еще искаженные буферы камеры char *, и преобразование сработало. Так что это должно быть что-то с частью memcpy(), но я скопировал это на самом деле из того места, где я скопировал свои искаженные буферы камеры, в GPU ShaderResource, который работал как шарм!

Примечание. CAMERA_CHANNLES здесь 4.

Может ли кто-нибудь увидеть, что я сделал неправильно здесь? Я ценю каждую помощь, спасибо! :)

ваше здоровье,

  • M.

person Max    schedule 10.03.2016    source источник
comment
примечание: if (language == "c++") use c++ API   -  person Berriel    schedule 11.03.2016


Ответы (1)


Наконец-то я смог добиться преобразования. При чтении визуализированного ShaderResource из графического процессора в мой ID3D11Texture2D* undistortedShaderTex было достаточно сопоставить ресурс и выполнить однократное

    memcpy(buffer, mappedData, (screenWidth_ * screenHeight_ * 4));

вместо цикла для преобразования графического процессора (распределенная схема памяти) в схему памяти процессора. Так что кажется, что макет памяти уже каким-то образом преобразован. Хотя мне это интересно, потому что, когда я передал свои искаженные изображения с камеры как ShaderResources в GPU, мне определенно пришлось преобразовать их в макет памяти GPU, используя описанный выше цикл.

Я рад, что теперь это работает :)

person Max    schedule 11.03.2016
comment
Привет, я пытаюсь сделать обратное, преобразовать cv::Mat в ID3D11Texture2D. Вы знаете, как это сделать, пожалуйста? - person SteveTJS; 17.02.2017