如何用C++捕获特定窗口的游戏区域(使用opencv实现)

问题描述 投票:0回答:1

我正在尝试仅捕获特定窗口的游戏区域。

这是我的代码:

HWND GameWindowManager::findGameWindow() {
    HWND hwndGame = NULL;
    EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&hwndGame));
    return hwndGame;
}

BOOL CALLBACK GameWindowManager::EnumWindowsProc(HWND hwnd, LPARAM lParam) {
    const DWORD TITLE_SIZE = 1024;
    WCHAR windowTitle[TITLE_SIZE];

    if (GetWindowText(hwnd, windowTitle, TITLE_SIZE) > 0) {
        std::wstring title(windowTitle);
        if (title.find(L"Game") != std::wstring::npos) {
            *(HWND*)lParam = hwnd;
            return FALSE;
        }
    }
    return TRUE;
}

cv::Mat GameWindowManager::captureGameWindow() {
    HWND hwnd = findGameWindow();
    if (hwnd == NULL) {
        qDebug() << "Game window not found.";
        return cv::Mat();
    }

    // get the client area of the Game window
    RECT rect;
    GetClientRect(hwnd, &rect);
    POINT topLeft = { rect.left, rect.top };
    POINT bottomRight = { rect.right, rect.bottom };

    // map the client area points to screen coordinates
    ClientToScreen(hwnd, &topLeft);
    ClientToScreen(hwnd, &bottomRight);

    // adjust rect to screen coordinates
    rect.left = topLeft.x;
    rect.top = topLeft.y;
    rect.right = bottomRight.x;
    rect.bottom = bottomRight.y;

    int width = rect.right - rect.left;
    int height = rect.bottom - rect.top;

    HDC hWindowDC = GetDC(hwnd);
    HDC hMemoryDC = CreateCompatibleDC(hWindowDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(hWindowDC, width, height);
    SelectObject(hMemoryDC, hBitmap);

    // copy from the window DC to the memory DC
    BitBlt(hMemoryDC, 0, 0, width, height, hWindowDC, rect.left - topLeft.x, rect.top - topLeft.y, SRCCOPY);

    BITMAPINFOHEADER bi = { 0 };
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = width;
    bi.biHeight = -height;  // negative height to flip the image
    bi.biPlanes = 1;
    bi.biBitCount = 24;
    bi.biCompression = BI_RGB;

    cv::Mat screen(height, width, CV_8UC3);
    GetDIBits(hMemoryDC, hBitmap, 0, height, screen.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

    ReleaseDC(hwnd, hWindowDC);
    DeleteDC(hMemoryDC);
    DeleteObject(hBitmap);

    return screen;
}

当我使用 opencv 打印该窗口的框架时:

cv::imshow("Display Frame", gameplayScreen);

它显示整个窗口,包括标题栏及其侧边栏。我希望它只捕获其中的游戏区域。 OBS 能够轻松完成此操作,因此我不确定如何完成此功能。另外,我也在我的项目中使用 Qt。

如有任何帮助,我们将不胜感激。

c++ qt opencv object
1个回答
0
投票

我只需调整屏幕截图中的一些边距就可以解决这个问题。由于像 greenshot 这样的工具能够捕获内部游戏窗口以及整个客户端本身,这使我能够计算游戏窗口中其他 UI 元素(例如标题栏)的差异。于是,我想出了这个解决方案:

cv::Mat GameWindowManager::captureGameWindow() {
HWND hwnd = findGameWindow();
if (hwnd == NULL) {
    qDebug() << "Game window not found.";
    return cv::Mat();
}

RECT rect;
GetClientRect(hwnd, &rect);

int topMargin = 29;  // adjust value as needed
int bottomMargin = 3;  // adjust value as needed
int leftMargin = 3;  // adjust value as needed
int rightMargin = 39;  // adjust value as needed

// adjust rect to exclude the UI elements
rect.top += topMargin;
rect.bottom -= bottomMargin;
rect.left += leftMargin;
rect.right -= rightMargin;

int width = rect.right - rect.left;
int padding = (4 - (width * 3 % 4)) % 4; // have to include padding - each pixel is 3 bytes for 24-bit bitmap
width += padding;
int height = rect.bottom - rect.top;

HDC hWindowDC = GetDC(hwnd);
HDC hMemoryDC = CreateCompatibleDC(hWindowDC);
HBITMAP hBitmap = CreateCompatibleBitmap(hWindowDC, width, height);
SelectObject(hMemoryDC, hBitmap);

BitBlt(hMemoryDC, 0, 0, width, height, hWindowDC, rect.left, rect.top, SRCCOPY);

BITMAPINFOHEADER bi = { 0 };
bi.biSize = sizeof(BITMAPINFOHEADER);
bi.biWidth = width;
bi.biHeight = -height; // negative height to flip the image
bi.biPlanes = 1;
bi.biBitCount = 24;
bi.biCompression = BI_RGB;

cv::Mat screen(height, width, CV_8UC3);
GetDIBits(hMemoryDC, hBitmap, 0, height, screen.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

ReleaseDC(hwnd, hWindowDC);
DeleteDC(hMemoryDC);
DeleteObject(hBitmap);

return screen;}

还必须在其中包含填充,以便在进行左右边距计算时正确考虑边距。我希望这对将来遇到类似问题的人有所帮助。干杯。

© www.soinside.com 2019 - 2024. All rights reserved.