使用 c++ 和 GDI 分析屏幕截图以获得屏幕的平均颜色

Posted

技术标签:

【中文标题】使用 c++ 和 GDI 分析屏幕截图以获得屏幕的平均颜色【英文标题】:Analyze Screenshots with c++ and GDI to get average color of screen 【发布时间】:2013-12-20 07:03:16 【问题描述】:

我正在为一个班级设计一个项目。我想尝试模仿 Phillips 的 Abilight 或常见的自制软件“BobLight”的行为。它通过屏幕截图获得屏幕的平均颜色,在我的例子中,它是通过逐个像素地平均 RGB 像素数据。我决定尝试通过使用 windows GDI api 来做到这一点,到目前为止我得到了一些非常好的结果,我学会了不使用 GetPixel() 来读取像素数据,因为它非常慢,而且我的速度完全可以接受所以远。

我的问题是,当我尝试在一个可以根据需要运行的循环中实现它时,它最终会挂起,给我一个错误提示:std::bad_alloc at memory location 0x0027F9C4。

在这行代码:

BYTE *lpbitmap = new BYTE[dwBmpSize]; 

我在下面附上了我的整个代码 有谁知道可能导致此错误的原因是什么?我是不是应该没有任何记忆,我真的很茫然.. 提前感谢您的帮助!

#define _WIN32_WINNT    0x0501        //xp
#include <windows.h>
#include <iostream>
#include <vector>
#include <time.h>

#define NUM_FRAMES 180

using namespace std;


int main()
 
    int time_start, time_finish;
    float time_total;
    time_start = clock();

for(int z = 0; z < NUM_FRAMES; z++)


HDC hScreenDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL);     
// and a device context to put it in
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);

int x = GetDeviceCaps(hScreenDC, HORZRES);
int y = GetDeviceCaps(hScreenDC, VERTRES);

HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, x, y);

// get a new bitmap
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);


RECT desktop;
// Get a handle to the desktop window
const HWND hDesktop = GetDesktopWindow();
// Get the size of screen to the variable desktop
GetWindowRect(hDesktop, &desktop);
// The top left corner will have coordinates (0,0)
// and the bottom right corner will have coordinates
// (horizontal, vertical)
int horizontal = desktop.right;
int vertical = desktop.bottom;


BitBlt(hMemoryDC, 0, 0, horizontal, vertical, hScreenDC, 0, 0, SRCCOPY);
hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);

BITMAPINFOHEADER   bi;

bi.biSize = sizeof(BITMAPINFOHEADER);    
bi.biWidth = horizontal;    
bi.biHeight = vertical;  
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;

DWORD dwBmpSize = ((horizontal * bi.biBitCount + 31) / 32) * 4 * vertical;

// Starting with 32-bit Windows, GlobalAlloc and LocalAlloc are implemented as wrapper     functions that 
// call HeapAlloc using a handle to the process's default heap. Therefore, GlobalAlloc     and LocalAlloc 
// have greater overhead than HeapAlloc.
HANDLE hDIB = GlobalAlloc(GHND,dwBmpSize); 
BYTE *lpbitmap = new BYTE[dwBmpSize]; 



//BITMAP bm;
GetDIBits(hMemoryDC,hBitmap,0,(UINT)vertical,lpbitmap, (BITMAPINFO *)&bi, DIB_RGB_COLORS);
GetObject (hBitmap, sizeof(lpbitmap), &lpbitmap);


unsigned long red = 0, green = 0, blue = 0;

for (int i = 0; i < horizontal; i++)

    for (int j = 0; j < vertical; j++)
    
    blue += lpbitmap[0];
    green += lpbitmap[1];
    red += lpbitmap[2];

    lpbitmap += 4;
    


red = red/(horizontal*vertical);
green = green/(horizontal*vertical);
blue = blue/(horizontal*vertical);

//cout << "Red: " << red << "    Green: " << green << "    Blue: " << blue << endl;

time_finish = clock();
time_total = time_finish - time_start;

cout << endl << NUM_FRAMES/((time_total)/10000) << endl;
    //release
    //DeleteDC(hdc);
    //DeleteObject(hbmp);
    //ReleaseDC(NULL, hdcScreen);

        std::system("pause");
    return 0;

【问题讨论】:

【参考方案1】:

您的发布代码似乎已被注释掉,这意味着您至少在每一帧都泄漏了hBitmap,这将很快加起来(具体来说约为 470MB/s)。实际上,您似乎也在泄漏lpbitmap,因此您的 180 帧将在 1080p 显示器上消耗大约 2.8 GB。

【讨论】:

这很有意义,我应该删除设备上下文还是真的只是删除 lpbitmap,因为这是我自己分配的唯一东西? 理想情况下,您应该在循环之外预先分配内存,并且只有在显示模式发生变化时才重新创建它。您需要同时发布 lpbitmaphBitmap(可能还有其他人),因为您同时创建了它们。仅仅因为一个对象不是用new 创建的,并不意味着你不需要销毁它。 因此,如果我在 for 循环之外为 lpbitmap 分配空间,那么它可以使用 DIBits 重新填充指针,而不需要在每次迭代时分配更多内存对吗?当我想删除 lpbitmap 时,我可以使用: delete lpbitmap; DeleteObject(hBitmap); 是的,但一定要使用 delete 的数组版本 - delete [] lpbitmap; 当我在循环外分配内存时,我收到访问错误,说我在 lpbitmap 内没有任何内容,当我为每一帧分配并在循环内删除它时,我可以到达在出现类似错误之前大约 1003 像素..

以上是关于使用 c++ 和 GDI 分析屏幕截图以获得屏幕的平均颜色的主要内容,如果未能解决你的问题,请参考以下文章

C++ gdi::Bitmap 到内存中的 PNG 图像

Gdi+ 多台显示器截图

在 C++ 中发送屏幕截图流

是否可以使用 DirectX 工具包中的屏幕抓图捕获桌面/屏幕截图?

屏幕截图

iTunes Connect 不再接受 iPad 屏幕截图