代码之家  ›  专栏  ›  技术社区  ›  S.T.A.L.K.E.R

Win32 API将ID3D11Texture2D转换为cv::Mat

  •  0
  • S.T.A.L.K.E.R  · 技术社区  · 1 年前

    所以我正在努力改变 ID3D11Texture2D 到OpenCV cv::Mat ,但由于某种原因,我的代码会扭曲图像。

    D3D11_TEXTURE2D_DESC capturedTextureDesc;
    texture->GetDesc(&capturedTextureDesc);
    capturedTextureDesc.Usage = D3D11_USAGE_STAGING;
    capturedTextureDesc.BindFlags = 0;
    capturedTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
    capturedTextureDesc.MiscFlags = 0;
    
    winrt::com_ptr<ID3D11Texture2D> userTexture = nullptr;
    winrt::check_hresult(d3dDevice->CreateTexture2D(&capturedTextureDesc, NULL, userTexture.put()));
    d3dContext->CopyResource(userTexture.get(), texture.get());
    D3D11_MAPPED_SUBRESOURCE resource;
    winrt::check_hresult(d3dContext->Map(userTexture.get(), NULL, D3D11_MAP_READ, 0, &resource));
    
    //https://arxiv.org/pdf/1702.02514
    int scWidth = capturedTextureDesc.Width;
    int scHeight = capturedTextureDesc.Height;
    int CAMERA_CHANNELS = 4;
    unsigned char* buffer = new unsigned char[(scWidth * scHeight * CAMERA_CHANNELS)];
    unsigned char* mappedData = reinterpret_cast<unsigned char*>(resource.pData);
    memcpy(buffer, mappedData, (scWidth * scHeight * CAMERA_CHANNELS));
    d3dContext->Unmap(userTexture.get(), 0);
    IplImage* frame = cvCreateImageHeader(cvSize(scWidth, scHeight),
        IPL_DEPTH_8U, CAMERA_CHANNELS);
    frame->imageData = (char*)buffer;
    cvSetData(frame, buffer, frame->widthStep);
    // cv::Mat mt = cv::Mat(frame, true); // constructor dissapered
    //https://stackoverflow.com/questions/15925084/conversion-from-iplimage-to-cvmat
    cv::Mat mt = cv::cvarrToMat(frame);
    return mt;
    

    以下是用cv::Mat输出捕获的窗口:

    enter image description here

    它们的大小相同,但内容不同。

    2 回复  |  直到 1 年前
        1
  •  1
  •   selbie    1 年前

    代码似乎没有引用 RowPitch D3D11_MAPPED_SUBRESOURCE中的值。RowPitch并不总是 width*pixelSize -每一行可以被缓冲,使得行宽度是16或某个其它值的倍数。

    相反:

    memcpy(buffer, mappedData, (scWidth * scHeight * CAMERA_CHANNELS));
    

    逐行复制:

    for (int h = 0; h < scHeight; h++) {
        unsigned char* srcRow = mappedData + (resource.RowPitch * h);
        unsigned char* dstRow = buffer + scWidth * CAMERA_CHANNELS * h;
        memcpy(dstRow, srcRow, scWidth*CAMERA_CHANNELS);
    }
    

    或将行Pitch传递给OpenCV。

    buffer = new unsigned char[resource.RowPitch * scHeight * CAMERA_CHANNELS];
    memcpy(buffer, mappedData, resource.RowPitch * scHeight * CAMERA_CHANNELS);
    
    ...
    
    cvSetData(frame, buffer, resource.RowPitch);
    
        2
  •  0
  •   wohlstad    1 年前

    该问题与无效步幅(又名步幅/俯仰)有关。
    步幅是图像行中的字节数。
    确实可以 scWidth * CAMERA_CHANNELS 但这不是必须的。
    有时(由于对齐问题)每行都会填充一些字节。

    在你的代码中,你假设没有这样的填充,但根据你发布的图片,实际上有。

    解决方案是使用DirectX纹理中的步幅,即。 resource.rowPitch (而不是假设它是 scWidth*摄像头_通道 ).

    另一个问题是,您不必使用 IplImage 处于中间阶段。既然你想要 cv::Mat ,您可以直接使用它。你甚至不需要 buffer -可以初始化 简历::Mat 直接从映射的数据:

    unsigned char* mappedData = reinterpret_cast<unsigned char*>(resource.pData);
    cv::Mat mt = cv::Mat(scHeight, scWidth, CV_8UC4, mappedData, resource.rowPitch).clone();
    d3dContext->Unmap(userTexture.get(), 0);
    return mt;
    

    笔记 clone() 使用是因为 简历::Mat 接受数据并跨步的constructor创建 简历::Mat 不拥有数据的。 克隆 创建一个拥有它的副本。