简体   繁体   English

如何使用 c++ 进行灰度截图?

[英]how do i take a grayscale screenshot with c++?

I have this piece of code here that takes a screenshot and as bitmaps and saves it as a BMP file.我这里有这段代码,它截取屏幕截图和位图并将其保存为 BMP 文件。 it works just fine but I want to be able to modify the bits and make it grayscale instead of an RGB map.它工作得很好,但我希望能够修改这些位并使其成为灰度而不是 RGB map。

I found a way to do this but I had to save the BMPINFOHEADER and RGBQUAD array, then read it with the grayscale function it kind of worked, but the size stays the same.我找到了一种方法来做到这一点,但我必须保存 BMPINFOHEADER 和 RGBQUAD 数组,然后用灰度 function 读取它,它有点工作,但大小保持不变。

I am very noob at this but I think the grayscale image should be a lot smaller than an RGB one.我对此很菜鸟,但我认为灰度图像应该比 RGB 图像小很多。

is there a way of removing color from the bitmap right away without having to re-read it and modify it?有没有一种方法可以立即从 bitmap 中去除颜色,而无需重新阅读和修改它?

int CaptureBMP(LPCTSTR szFile)
{
    // Source[1]
    HDC hdcScr, hdcMem;
    HBITMAP hbmScr;
    BITMAP bmp;
    int iXRes, iYRes;

    // Create a normal DC and a memory DC for the entire screen. The
    // normal DC provides a "snapshot" of the screen contents. The
    // memory DC keeps a copy of this "snapshot" in the associated
    // bitmap.
    hdcScr = CreateDC("DISPLAY", NULL, NULL, NULL);


    hdcMem = CreateCompatibleDC(hdcScr);

    iXRes = GetDeviceCaps(hdcScr, HORZRES);
    iYRes = GetDeviceCaps(hdcScr, VERTRES);

    // Create a compatible bitmap for hdcScreen.
    hbmScr = CreateCompatibleBitmap(hdcScr, iXRes, iYRes);
    
    if (hbmScr == 0) return 0;

    // Select the bitmaps into the compatible DC.
    if (!SelectObject(hdcMem, hbmScr)) return 0;

    // Copy color data for the entire display into a
    // bitmap that is selected into a compatible DC.
    if (!StretchBlt(hdcMem,0, 0, iXRes, iYRes,hdcScr,0, 0, iXRes, iYRes,SRCCOPY)) return 0;

    // Source[2]
    PBITMAPINFO pbmi;
    WORD cClrBits;

    // Retrieve the bitmap's color format, width, and height.
    if (!GetObject(hbmScr, sizeof(BITMAP), (LPSTR)&bmp)) return 0;

    // Convert the color format to a count of bits.
    cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
    if (cClrBits == 1)
        cClrBits = 1;
    else if (cClrBits <= 4)
        cClrBits = 4;
    else if (cClrBits <= 8)
        cClrBits = 8;
    else if (cClrBits <= 16)
        cClrBits = 16;
    else if (cClrBits <= 24)
        cClrBits = 24;
    else cClrBits = 32;


    // Allocate memory for the BITMAPINFO structure. (This structure
    // contains a BITMAPINFOHEADER structure and an array of RGBQUAD
    // data structures.)
    if (cClrBits != 24)
        pbmi = (PBITMAPINFO)LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (1 << cClrBits));

    // There is no RGBQUAD array for the 24-bit-per-pixel format.
    else
        pbmi = (PBITMAPINFO)LocalAlloc(LPTR,sizeof(BITMAPINFOHEADER));

    // Initialize the fields in the BITMAPINFO structure.
    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = bmp.bmWidth;
    pbmi->bmiHeader.biHeight = bmp.bmHeight;
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
    if (cClrBits < 24)
        pbmi->bmiHeader.biClrUsed = (1 << cClrBits);

    // If the bitmap is not compressed, set the BI_RGB flag.
    pbmi->bmiHeader.biCompression = BI_RGB;

    // Compute the number of bytes in the array of color
    // indices and store the result in biSizeImage.
    pbmi->bmiHeader.biSizeImage = (pbmi->bmiHeader.biWidth + 7) / 8 * pbmi->bmiHeader.biHeight * cClrBits;

    // Set biClrImportant to 0, indicating that all of the
    // device colors are important.
    pbmi->bmiHeader.biClrImportant = 0;

    HANDLE hf;                  // file handle
    BITMAPFILEHEADER hdr;       // bitmap file-header
    PBITMAPINFOHEADER pbih;     // bitmap info-header
    LPBYTE lpBits;              // memory pointer
    DWORD dwTotal;              // total count of bytes
    DWORD cb;                   // incremental count of bytes
    BYTE* hp;                   // byte pointer
    DWORD dwTmp;

    pbih = (PBITMAPINFOHEADER)pbmi;
    lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

    if (!lpBits) return 0;

    // Retrieve the color table (RGBQUAD array) and the bits
    // (array of palette indices) from the DIB.
    if (!GetDIBits(hdcMem, hbmScr, 0, (WORD)pbih->biHeight, lpBits, pbmi, DIB_RGB_COLORS)) return 0;

    // Create the .BMP file.
    hf = CreateFile(szFile, GENERIC_READ | GENERIC_WRITE, (DWORD)0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL);
    if (hf == INVALID_HANDLE_VALUE) return 0;

    hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M"

    // Compute the size of the entire file.
    hdr.bfSize = (DWORD)(sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
    hdr.bfReserved1 = 0;
    hdr.bfReserved2 = 0;

    // Compute the offset to the array of color indices.
    hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) +
        pbih->biSize + pbih->biClrUsed *
        sizeof(RGBQUAD);

    // Copy the BITMAPFILEHEADER into the .BMP file.
    if (!WriteFile(hf, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), (LPDWORD)&dwTmp, NULL)) return 0;

    // Copy the BITMAPINFOHEADER and RGBQUAD array into the file.
    if (!WriteFile(hf, (LPVOID)pbih, sizeof(BITMAPINFOHEADER)
        + pbih->biClrUsed * sizeof(RGBQUAD),
        (LPDWORD)&dwTmp, NULL))
        return 0;

    // Copy the array of color indices into the .BMP file.
    dwTotal = cb = pbih->biSizeImage;
    hp = lpBits;
    if (!WriteFile(hf, (LPSTR)hp, (int)cb, (LPDWORD)&dwTmp, NULL)) return 0;

    // Close the .BMP file.
    if (!CloseHandle(hf)) return 0;

    // Free memory.
    GlobalFree((HGLOBAL)lpBits);
    ReleaseDC(0, hdcScr);
    ReleaseDC(0, hdcMem);

    return 1;
}

I used array conversion, which is mainly from the rgbRed, rgbGreen and rgbBlue components of the original true color map to the gray value Y of the gray image.我用了数组转换,主要是从原真彩色map的rgbRed、rgbGreen和rgbBlue分量到灰度图像的灰度值Y。

It can be obtained by using the following formula:可以使用以下公式获得:

Y=0.299 * rgbRed+0.587 * rgbGreen+0.114 * rgbBlue Y=0.299 * rgbRed+0.587 * rgbGreen+0.114 * rgbBlue

I added the processing code to your code, you can refer to the following code, the output file size is 1/4 of the original.我在你的代码中添加了处理代码,可以参考下面的代码,output文件大小是原来的1/4。

int CaptureBMP(LPCTSTR szFile)
{
// Source[1]
HDC hdcScr, hdcMem;
HBITMAP hbmScr;
BITMAP bmp;
.................
.................
// Copy the array of color indices into the .BMP file.
dwTotal = cb = pbih->biSizeImage;
hp = lpBits;
if (!WriteFile(hf, (LPSTR)hp, (int)cb, (LPDWORD)& dwTmp, NULL)) return 0;

// Close the .BMP file.
if (!CloseHandle(hf)) return 0;


/***************************************************/

FILE* originImg;
fopen_s(&originImg, "lena-32.bmp", "rb");
if (originImg == NULL) return 0;

int sizeFileHeader = sizeof(BITMAPFILEHEADER);
int sizeInfoHeader = sizeof(BITMAPINFOHEADER);

BITMAPFILEHEADER* bitmapFileHeader = new BITMAPFILEHEADER[sizeFileHeader + 1];
BITMAPINFOHEADER* bitmapInfoHeader = new BITMAPINFOHEADER[sizeInfoHeader + 1];

memset(bitmapFileHeader, 0, sizeFileHeader + 1);
memset(bitmapInfoHeader, 0, sizeInfoHeader + 1);
fread(bitmapFileHeader, sizeof(char), sizeFileHeader, originImg);
fseek(originImg, sizeFileHeader, 0);
fread(bitmapInfoHeader, sizeof(char), sizeInfoHeader, originImg);


int srcImageLineByteCount = (((bitmapInfoHeader->biWidth * 32) + 31) / 32) * 4; //Calculates the number of bytes of pixels per line of the original 32 bitmap
int grayImageLineByteCount = (((bitmapInfoHeader->biWidth) * 8 + 31) / 32) * 4; //Calculate the number of bytes of pixels in each row of 8-bit grayscale map

//************Bitmap header**********************
//Creates a two-dimensional array with high biHeight and width srcImageLineByteCount, and initializes the array
BYTE** origImgData = new BYTE * [bitmapInfoHeader->biHeight];
for (int i = 0; i < bitmapInfoHeader->biHeight; i++)
{
    origImgData[i] = new BYTE[srcImageLineByteCount + 1];       //Each row is an array of size srcImageLineByteCount
    memset(origImgData[i], 0, srcImageLineByteCount + 1);       //Start from origImgData[I] and set srcImageLinneByteCount+1 to 0
}
//**********Bitmap data**********************
fseek(originImg, sizeFileHeader + sizeInfoHeader, 0);   //Locate to the position of the file header offset sizeFileHeader + sizeInfoHeader, the position where the bitmap data starts
//Read bitmap data
for (int i = 0; i < bitmapInfoHeader->biHeight; i++)
{
    for (int j = 0; j < srcImageLineByteCount; j++)     //The number of bytes
    {
        fread(&origImgData[i][j], sizeof(BYTE), 1, originImg);  //Read the original image bitmap data in bytes into the two-dimensional array origImgData
    }
}
fclose(originImg);

// palette
RGBQUAD* pRgbQuards = new RGBQUAD[256];
for (int i = 0; i < 256; i++)
{
    pRgbQuards[i].rgbBlue = i;
    pRgbQuards[i].rgbRed = i;
    pRgbQuards[i].rgbGreen = i;
    //pRgbQuards[i].rgbReserved = 0;
}
//Modify headers
bitmapInfoHeader->biBitCount = 8;
//bitmapInfoHeader->biClrUsed = 256;
bitmapInfoHeader->biSizeImage = (bitmapInfoHeader->biHeight) * grayImageLineByteCount;

//8 is a grayscale image with 256 RGBQUAD data structures. A color palette takes up 4 bytes of data, so the color palette length of 256 color images is 256*4 and 1024 bytes
bitmapFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256;  //The offset plus the size of the palette
bitmapFileHeader->bfSize = bitmapFileHeader->bfOffBits + bitmapInfoHeader->biSizeImage;
//Write the data
BYTE** grayImgData = new BYTE * [bitmapInfoHeader->biHeight];
for (int i = 0; i < bitmapInfoHeader->biHeight; i++)
{
    grayImgData[i] = new BYTE[grayImageLineByteCount];
}
//Grayscale map a two-dimensional array of bitmap data
for (int i = 0; i < bitmapInfoHeader->biHeight; i++)
{
    for (int j = 0; j < grayImageLineByteCount; j++)    //The number of bytes of pixels per row of a grayscale image
    {
        grayImgData[i][j] = (int)((float)origImgData[i][j * 3] * 0.114 +
            (float)origImgData[i][j * 3 + 1] * 0.587 + (float)origImgData[i][3 * j + 2] * 0.299);
    }
}
//Written to the file
FILE* grayImg;
fopen_s(&grayImg, "lena-gray.bmp", "wb");
fwrite(bitmapFileHeader, sizeof(char), sizeof(BITMAPFILEHEADER), grayImg);  
fwrite(bitmapInfoHeader, sizeof(char), sizeof(BITMAPINFOHEADER), grayImg);  
fwrite(pRgbQuards, sizeof(RGBQUAD), 256, grayImg);  
for (int i = 0; i < bitmapInfoHeader->biHeight; i++)
{
    for (int j = 0; j < grayImageLineByteCount; j++)
    {
        fwrite(&grayImgData[i][j], sizeof(BYTE), 1, grayImg);   //Write bitmap data
    }
}
fclose(grayImg);

//Free memory.

delete[]grayImgData;

delete[]origImgData;

/***************************************************/

// Free memory.
GlobalFree((HGLOBAL)lpBits);
ReleaseDC(0, hdcScr);
ReleaseDC(0, hdcMem);

return 1;
}
int main()
{
   CaptureBMP(L"lena-32");
   std::cout << "Hello World!\n";
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM