【问题标题】:C++ Window capture output isn't the same size as said windowC++ 窗口捕获输出与所述窗口的大小不同
【发布时间】:2018-10-27 22:46:47
【问题描述】:

我有一个用 C++ 编写的简短程序来从这个 BlueStacks 仿真器窗口捕获像素,然后我将使用 OpenCV 操作像素,然后根据这里和那里的一些决定发送鼠标输入 (win32api)。我在 Visual Studio Enterprise 2017 中工作。

唯一的问题是,与整个窗口相比,该函数似乎捕获的像素区域更小。

这是一个示例图像。左边是原始窗口,右边是镜像输出。

我该如何解决这个问题?我已经在我的项目设置中启用了高 DPI 感知,不知道除了在函数中添加随机幻数之外还需要采取什么其他步骤。

我已经在 Python 中完成了这个程序,但我想在 C++ 中重做这个程序以提高性能。

Here's a video (warning: noise).And another one.

这是我当前的代码:

#include "stdafx.h"
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
#include <Windows.h>
#include <iostream>

using namespace std;
using namespace cv;

Mat hwnd2mat(HWND hwnd);

int main(int argc, char **argv)
{
    printf("Hello, world!\n");
    Sleep(1000);

    HWND hwndDesktop;

    hwndDesktop = GetForegroundWindow();
    // namedWindow("output", WINDOW_NORMAL);
    int key = 0;
    Mat src;

    while (key != 27)
    {
        src = hwnd2mat(hwndDesktop);
        // you can do some image processing here
        imshow("output", src);
        key = waitKey(1); // you can change wait time
    }

}

Mat hwnd2mat(HWND hwnd)
{
    HDC hwindowDC, hwindowCompatibleDC;

    int height, width, srcheight, srcwidth;
    HBITMAP hbwindow;
    Mat src;
    BITMAPINFOHEADER  bi;

    hwindowDC = GetDC(hwnd);
    hwindowCompatibleDC = CreateCompatibleDC(hwindowDC);
    SetStretchBltMode(hwindowCompatibleDC, COLORONCOLOR);

    RECT windowsize;    // get the height and width of the screen
    GetClientRect(hwnd, &windowsize);

    srcheight = windowsize.bottom;
    srcwidth = windowsize.right;
    height = windowsize.bottom / 1;  //change this to whatever size you want to resize to
    width = windowsize.right / 1;

    src.create(height, width, CV_8UC4);

    // create a bitmap
    hbwindow = CreateCompatibleBitmap(hwindowDC, width, height);
    bi.biSize = sizeof(BITMAPINFOHEADER);    //http://msdn.microsoft.com/en-us/library/windows/window/dd183402%28v=vs.85%29.aspx
    bi.biWidth = width;
    bi.biHeight = -height;  //this is the line that makes it draw upside down or not
    bi.biPlanes = 1;
    bi.biBitCount = 32;
    bi.biCompression = BI_RGB;
    bi.biSizeImage = 0;
    bi.biXPelsPerMeter = 0;
    bi.biYPelsPerMeter = 0;
    bi.biClrUsed = 0;
    bi.biClrImportant = 0;

    // use the previously created device context with the bitmap
    SelectObject(hwindowCompatibleDC, hbwindow);
    // copy from the window device context to the bitmap device context
    StretchBlt(hwindowCompatibleDC, 0, 0, width, height, hwindowDC, 0, 0, srcwidth, srcheight, SRCCOPY); //change SRCCOPY to NOTSRCCOPY for wacky colors !
    GetDIBits(hwindowCompatibleDC, hbwindow, 0, height, src.data, (BITMAPINFO *)&bi, DIB_RGB_COLORS);  //copy from hwindowCompatibleDC to hbwindow

                                                                                                       // avoid memory leak
    DeleteObject(hbwindow);
    DeleteDC(hwindowCompatibleDC);
    ReleaseDC(hwnd, hwindowDC);

    return src;
}

【问题讨论】:

    标签: c++ opencv computer-vision bluestacks


    【解决方案1】:

    拉伸是由于 DPI 缩放。您自己的程序不支持 DPI,其他程序似乎支持 DPI。让您的程序了解 DPI 的最简单方法是在程序开始时调用 SetProcessDPIAware();

    另外注意,在调用GetDIBits 时,不应在设备上下文中选择HBITMAP 句柄。您可以将代码重写为

    SetProcessDPIAware();
    ...
    
    Mat hwnd2mat(HWND hwnd)
    {
        RECT rc;
        GetClientRect(hwnd, &rc);
        int width = rc.right;
        int height = rc.bottom;
    
        Mat src;
        src.create(height, width, CV_8UC4);
    
        HDC hdc = GetDC(hwnd);
        HDC memdc = CreateCompatibleDC(hdc);
        HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height);
        HBITMAP oldbmp = (HBITMAP)SelectObject(memdc, hbitmap);
    
        BitBlt(memdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY);
        SelectObject(memdc, oldbmp);
    
        BITMAPINFOHEADER  bi = { sizeof(BITMAPINFOHEADER), width, -height, 1, 32, BI_RGB };
        GetDIBits(hdc, hbitmap, 0, height, src.data, (BITMAPINFO*)&bi, DIB_RGB_COLORS);
    
        DeleteObject(hbitmap);
        DeleteDC(memdc);
        ReleaseDC(hwnd, hdc);
    
        return src;
    }
    

    此代码仅适用于本机 Win32 程序。对于其他程序,如 Chrome、WPF、Qt 应用程序,这将显示空白屏幕。您需要截取桌面窗口的屏幕截图。

    【讨论】:

    • 感谢您的提示 :) 奇怪的是,以下项目设置是否不够? i.imgur.com/NoPyDgA.png(在 Visual Studio Enterprise 2017 中)我现在就试试你的建议
    • 好的,我已经尝试添加方法以及新函数 - 程序输出与原始帖子中的图像相同:/ 我在函数中为高度和宽度添加了一个常量值这足以满足我的需要,尽管我不确定是否需要更改任何其他值。即int width = rc.right + 100int height = rc.bottom + 100;
    • 您的 DPI 方法是首选,它设置清单。不要添加SetProcessDPIAware。还有一些您没有在问题中显示的内容。
    • 奇怪...我重新启动了我的笔记本电脑,然后它工作正常(例如,我现有的函数将扫描比窗口大 100 像素,删除常量将正确扫描窗口)。我可以告诉你,我确实将 DPI 设置为 200%,而且我正在使用 3840x2160 屏幕。我现在也刚刚发现 BlueStacks 窗口本身是可调整大小的。我将在此处链接我当前的代码:pastebin.com/12L56jXx
    • DPI 更改有时不会生效,除非您重新启动或注销并登录
    【解决方案2】:

    我想你想要GetActiveWindow() 而不是GetForegroundWindow()

    this SO answer

    【讨论】:

    • 谢谢 :) 我使用 GetForegroundWindow() 作为占位符来查找程序的窗口标题 - 我将尽快用 FindWindow() 替换它。不过感谢您的链接,我会通读一遍,因为它似乎有重要的区别。
    • 我已经尝试过你的建议(直接用 GetActiveWindow() 替换 GetForegroundWindow()),无论如何它似乎评估为 NULL:/
    • 原来我从 const char* 到 LPCSTR 进行了错误的转换 - 我应该使用 TEXT() 宏进行转换。不过仍然想知道,为什么 GetActiveWindow() 在我的情况下会返回 NULL?当然,我至少有一个活动窗口。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-22
    • 2012-07-31
    • 2011-06-13
    • 2012-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多