1

i am using the Desktop Duplication API - to take a screenshot (c++). This works in principle, but not under every resolution.

i am creating an opencv mat from the D3D11_MAPPED_SUBRESOURCE and then display the image.

Under 1920x1080 it looks like this (correct): enter image description here

Under 1366x768 it looks like this (wrong) (while 1360x768 works correct): There are some other resolutions with the same effect.

enter image description here

Does anyone have an idea what this could be ?

Here is my full code to take a Screenshot:

DXGIScreenCapture.h class DXGIScreenCapture { public: DXGIScreenCapture(); ~DXGIScreenCapture();

    bool Init();
    bool CaptureScreen();


    D3D11_TEXTURE2D_DESC desc;
    D3D11_MAPPED_SUBRESOURCE _resource;
private:
    ID3D11Device* _lDevice;
    ID3D11DeviceContext* _lImmediateContext;
    IDXGIOutputDuplication* _lDeskDupl;
    ID3D11Texture2D* _lAcquiredDesktopImage;
    DXGI_OUTPUT_DESC _lOutputDesc;
    DXGI_OUTDUPL_DESC _lOutputDuplDesc;
    ID3D11Texture2D* currTexture;
};

DXGIScreenCapture.cpp

DXGIScreenCapture::DXGIScreenCapture()
{
    Init();
}

DXGIScreenCapture::~DXGIScreenCapture()
{
}



bool DXGIScreenCapture::Init() {
    // Feature levels supported
    D3D_FEATURE_LEVEL gFeatureLevels[] = {
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
        D3D_FEATURE_LEVEL_9_1
    };
    UINT gNumFeatureLevels = ARRAYSIZE(gFeatureLevels);
    D3D_FEATURE_LEVEL lFeatureLevel;

    HRESULT hr(E_FAIL);
    hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, D3D11_CREATE_DEVICE_FLAG::D3D11_CREATE_DEVICE_SINGLETHREADED, gFeatureLevels, gNumFeatureLevels, D3D11_SDK_VERSION, &_lDevice, &lFeatureLevel, &_lImmediateContext);

    if (FAILED(hr))
        return false;

    if (!_lDevice)
        return false;

    // Get DXGI device
    IDXGIDevice* lDxgiDevice;
    hr = _lDevice->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&lDxgiDevice));
    if (FAILED(hr))
        return false;

    // Get DXGI adapter
    IDXGIAdapter* lDxgiAdapter;
    hr = lDxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&lDxgiAdapter));
    lDxgiDevice->Release();
    lDxgiDevice = nullptr;
    if (FAILED(hr))
        return false;

    UINT Output = 0;
    // Get output
    IDXGIOutput* lDxgiOutput;
    hr = lDxgiAdapter->EnumOutputs(Output, &lDxgiOutput);

    if (FAILED(hr))
        return false;
    lDxgiAdapter->Release();
    lDxgiAdapter = nullptr;

    hr = lDxgiOutput->GetDesc(&_lOutputDesc);

    if (FAILED(hr))
        return false;

    // QI for Output 1
    IDXGIOutput1* lDxgiOutput1;
    hr = lDxgiOutput->QueryInterface(__uuidof(lDxgiOutput1), reinterpret_cast<void**>(&lDxgiOutput1));
    lDxgiOutput->Release();
    lDxgiOutput = nullptr;
    if (FAILED(hr))
        return false;

    // Create desktop duplication
    hr = lDxgiOutput1->DuplicateOutput(_lDevice, &_lDeskDupl);

    if (FAILED(hr))
        return false;

    lDxgiOutput1->Release();
    lDxgiOutput1 = nullptr;

    // Create GUI drawing texture
    _lDeskDupl->GetDesc(&_lOutputDuplDesc);
    // Create CPU access texture

    desc.Width = _lOutputDuplDesc.ModeDesc.Width;
    desc.Height = _lOutputDuplDesc.ModeDesc.Height;
    desc.Format = _lOutputDuplDesc.ModeDesc.Format;
    desc.ArraySize = 1;
    desc.BindFlags = 0;
    desc.MiscFlags = 0;
    desc.SampleDesc.Count = 1;
    desc.SampleDesc.Quality = 0;
    desc.MipLevels = 1;
    desc.CPUAccessFlags = D3D11_CPU_ACCESS_FLAG::D3D11_CPU_ACCESS_READ;
    desc.Usage = D3D11_USAGE::D3D11_USAGE_STAGING;

    hr = _lDevice->CreateTexture2D(&desc, NULL, &currTexture);
    if (!currTexture)
    {
        hr = _lDeskDupl->ReleaseFrame();
        return false;
    }


    return true;
}


bool DXGIScreenCapture::CaptureScreen()
{
    HRESULT hr(E_FAIL);
    IDXGIResource* lDesktopResource = nullptr;
    DXGI_OUTDUPL_FRAME_INFO lFrameInfo;

    //_lDeskDupl->ReleaseFrame();
    hr = _lDeskDupl->AcquireNextFrame(0, &lFrameInfo, &lDesktopResource);


    if (FAILED(hr))
        return false;

    if (lFrameInfo.LastPresentTime.HighPart == 0) // not interested in just mouse updates, which can happen much faster than 60fps if you really shake the mouse
    {
        hr = _lDeskDupl->ReleaseFrame();
        return false;
    }

    // QI for ID3D11Texture2D
    hr = lDesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&_lAcquiredDesktopImage));
    lDesktopResource->Release();
    lDesktopResource = nullptr;
    if (FAILED(hr))
    {
        hr = _lDeskDupl->ReleaseFrame();
        return false;
    }

    _lImmediateContext->CopyResource(currTexture, _lAcquiredDesktopImage);
    UINT subresource = D3D11CalcSubresource(0, 0, 0);
    _lImmediateContext->Map(currTexture, subresource, D3D11_MAP_READ, 0, &_resource);
    _lImmediateContext->Unmap(currTexture, 0);
    hr = _lDeskDupl->ReleaseFrame();


    return true;

}

Taking a Screenshot:

screenshot()
{
    while (!_DXGIScreenCapture.CaptureScreen());
    _imageRecognition._image = cv::Mat(_DXGIScreenCapture.desc.Height, _DXGIScreenCapture.desc.Width, CV_8UC4, _DXGIScreenCapture._resource.pData);


}

Thank you :-)

Dvy Hrnsch
  • 56
  • 8
  • I can't be certain that this is what the problem is, but I typically see that sort of weird diagonal warping whenever the image width doesn't match with whatever the display thinks it is. You can actually calculate what the display is expecting by checking the slope of those lines. It's possible that whatever you're using doesn't support certain resolutions and is just downgrading to the nearest one it supports. – Ian Chu Jan 22 '21 at 16:29
  • 1
    You're using `Mat ` constructor default argumrent `, size_t step=AUTO_STEP)` while you have to pick up `_resource.RowPitch` instead. – Roman R. Jan 25 '21 at 13:25
  • 1
    Thank you very much @RomanR. ! That was the hint I needed :-) – Dvy Hrnsch Jan 27 '21 at 08:57

1 Answers1

3
    _imageRecognition._image = cv::Mat(_DXGIScreenCapture.desc.Height, _DXGIScreenCapture.desc.Width, CV_8UC4, _DXGIScreenCapture._resource.pData);

needs to be this in order to handle potentially extended stride:

    _imageRecognition._image = cv::Mat(
        _DXGIScreenCapture.desc.Height, 
        _DXGIScreenCapture.desc.Width, 
        CV_8UC4, 
        _DXGIScreenCapture._resource.pData, 
        _resource.RowPitch // <<--- texture stride might be not the one you expect it
        );

Roman R.
  • 68,205
  • 6
  • 94
  • 158