我正在尝试仅捕获特定窗口的游戏区域。
这是我的代码:
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。
如有任何帮助,我们将不胜感激。
我只需调整屏幕截图中的一些边距就可以解决这个问题。由于像 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;}
还必须在其中包含填充,以便在进行左右边距计算时正确考虑边距。我希望这对将来遇到类似问题的人有所帮助。干杯。