You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用C++结合OpenCV捕获特定窗口的游戏区域

仅捕获游戏窗口客户区域(不含标题栏/边框)的解决方案

问题原因分析

你的代码当前捕获到整个窗口(含标题栏、边框),核心问题是对GDI DC(设备上下文)的使用逻辑有误,额外的屏幕坐标转换导致捕获范围偏离了客户区;部分游戏还会用子窗口单独渲染游戏画面,主窗口仅负责标题栏等外围元素,这时候需要精准定位到渲染子窗口。

修正后的代码实现

1. 基础版:捕获主窗口客户区

修改captureGameWindow函数,直接操作客户区DC,跳过不必要的屏幕坐标转换:

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

    // 获取窗口客户区域(不含标题栏、边框的实际内容区)
    RECT clientRect;
    GetClientRect(hwnd, &clientRect);
    int clientWidth = clientRect.right - clientRect.left;
    int clientHeight = clientRect.bottom - clientRect.top;

    if (clientWidth <= 0 || clientHeight <= 0) {
        qDebug() << "Invalid client area size.";
        return cv::Mat();
    }

    // 获取客户区专属DC(仅包含客户区内容,无外围元素)
    HDC hClientDC = GetDC(hwnd);
    HDC hMemoryDC = CreateCompatibleDC(hClientDC);
    HBITMAP hBitmap = CreateCompatibleBitmap(hClientDC, clientWidth, clientHeight);
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);

    // 直接复制客户区DC内容到内存DC,源坐标为(0,0)(客户区DC的起点就是内容左上角)
    BitBlt(hMemoryDC, 0, 0, clientWidth, clientHeight, hClientDC, 0, 0, SRCCOPY);

    BITMAPINFOHEADER bi = { 0 };
    bi.biSize = sizeof(BITMAPINFOHEADER);
    bi.biWidth = clientWidth;
    bi.biHeight = -clientHeight;  // 负高度修正GDI位图的倒立问题
    bi.biPlanes = 1;
    bi.biBitCount = 24;
    bi.biCompression = BI_RGB;

    cv::Mat frame(clientHeight, clientWidth, CV_8UC3);
    GetDIBits(hMemoryDC, hBitmap, 0, clientHeight, frame.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);

    // 按顺序清理GDI资源,避免内存泄漏
    SelectObject(hMemoryDC, hOldBitmap);
    DeleteObject(hBitmap);
    DeleteDC(hMemoryDC);
    ReleaseDC(hwnd, hClientDC);

    return frame;
}

2. 进阶版:捕获游戏渲染子窗口

如果基础版仍无法得到正确的游戏画面,说明游戏用子窗口单独渲染,需先定位到渲染子窗口:

// 子窗口枚举回调:筛选游戏渲染窗口(根据实际游戏类名调整)
BOOL CALLBACK EnumChildWindowsProc(HWND hwnd, LPARAM lParam) {
    WCHAR className[256];
    GetClassName(hwnd, className, 256);
    std::wstring clsName(className);

    // 示例:匹配Unity/SDL/Direct3D常见渲染窗口类名,需用Spy++工具查看目标游戏的类名
    if (clsName == L"UnityWndClass" || clsName == L"SDL_app" || clsName.find(L"Render") != std::wstring::npos) {
        *(HWND*)lParam = hwnd;
        return FALSE;
    }
    return TRUE;
}

// 修改窗口查找逻辑:先找主窗口,再找渲染子窗口
HWND GameWindowManager::findGameWindow() {
    HWND hwndMain = NULL;
    EnumWindows(EnumWindowsProc, reinterpret_cast<LPARAM>(&hwndMain));
    if (hwndMain == NULL) {
        return NULL;
    }

    HWND hwndRender = NULL;
    EnumChildWindows(hwndMain, EnumChildWindowsProc, reinterpret_cast<LPARAM>(&hwndRender));
    // 找不到子窗口时 fallback 到主窗口
    return hwndRender != NULL ? hwndRender : hwndMain;
}

Qt中显示捕获画面的适配

OpenCV的Mat是BGR格式,Qt的QImage是RGB格式,需转换后再显示:

cv::Mat gameplayScreen = captureGameWindow();
if (!gameplayScreen.empty()) {
    cv::cvtColor(gameplayScreen, gameplayScreen, cv::COLOR_BGR2RGB);
    QImage img(gameplayScreen.data, gameplayScreen.cols, gameplayScreen.rows, gameplayScreen.step, QImage::Format_RGB888);
    // 示例:在QLabel中显示画面
    ui->gameScreenLabel->setPixmap(QPixmap::fromImage(img).scaled(ui->gameScreenLabel->size(), Qt::KeepAspectRatio));
}

内容的提问来源于stack exchange,提问作者Zerx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 17:30:01