4

好的,整个故事是,我正在尝试在 C++ 中使用 Leptonica+Tesseract OCR 来截取屏幕截图,将其保存到 *.bmp 文件,然后将其加载回 OCR。我不需要经常这样做,但由于我似乎无法将屏幕截图数据直接复制到 Leptonica PIX 结构中,所以我需要先将其保存到文件中。实际上,最好有一个解决方案。

这是我在网上找到的一些代码,试图帮助我。

屏幕盖:

HBITMAP ScreenCapture(){
  int width=100;
  int height=100;
  // get the device context of the screen
  HDC hScreenDC = CreateDC(L"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);

  // maybe worth checking these are positive values
  HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, x, y);

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

  BitBlt(hMemoryDC, 0, 0, width, height, hScreenDC, 0, 0, SRCCOPY);
  hBitmap = (HBITMAP)SelectObject(hMemoryDC, hOldBitmap);

  //GlobalAlloc(GPTR, hBitmap)

  WriteDIB(L"test.bmp", (HGLOBAL)hBitmap);

  // clean up
  DeleteDC(hMemoryDC);
  DeleteDC(hScreenDC);

  return hBitmap;
  // now your image is held in hBitmap. You can save it or do whatever with it
}

尝试编写函数:

BOOL WriteDIB( LPTSTR szFile, HANDLE hDIB)
{
  cout<<endl<<"Running save function";
  /*HANDLE hDIB=GlobalAlloc(GPTR, sizeof(hDIBtochange));//this doesn't work, the result is four. Also the HANDLE parameter's name would be changed to hDIBtochange, so that the rest of the function uses the old 'hDIB' throughout
  cout<<endl<<sizeof(hDIBtochange);*/
  BITMAPFILEHEADER  hdr;
  LPBITMAPINFOHEADER    lpbi;
  if (!hDIB)
    return FALSE;
  CFile file;
  if( !file.Open( szFile, CFile::modeWrite|CFile::modeCreate) )
    return FALSE;
  lpbi = (LPBITMAPINFOHEADER)hDIB;
  int nColors = 1 << lpbi->biBitCount;
  // Fill in the fields of the file header 
  hdr.bfType        = ((WORD) ('M' << 8) | 'B');    // is always "BM"
  hdr.bfSize        = GlobalSize (hDIB) + sizeof( hdr );
  hdr.bfReserved1   = 0;
  hdr.bfReserved2   = 0;
  hdr.bfOffBits     = (DWORD) (sizeof( hdr ) + lpbi->biSize + nColors * sizeof(RGBQUAD));
  // Write the file header 
  file.Write( &hdr, sizeof(hdr) );
  // Write the DIB header and the bits 
  file.Write( lpbi, GlobalSize(hDIB) );
  return TRUE;
}

多年来无耻地抄袭人们的帖子。好的!我面临的问题是,我似乎无法理解如何将 Hbitmap 全局分配到全局可访问的句柄中,该句柄可以转换或与 LPBITMAPINFOHEADER 一起使用。创建 lpbi 后,其中的每个字段在 Visual Studio 2012 调试中都是“无法读取内存”错误。它是不可访问的,尽管被创建了。

解决方案..直接从屏幕截图到 PIX,在内存中..找到一种方法来保存到位图并定期创建它们以供阅读..找到另一种更有意义的方法..

更喜欢第一个,但是,我要求解决这个问题,而不是第二个..或第三个。

如果您需要更多信息,我可以尝试提供。这主要归结为“我以前从未做过这样的代码,而且我的课堂上也没有教过它,所以我正在努力学习”。

4

3 回答 3

12

A much easier way to save an HBITMAP to file is to make use of GDI+. This gives you the advantage of being able to save to any format that windows supports natively, while freeing you from the muck of playing around with or even needing to understand, various image formats.

In the below example, I've just used LoadImage as a quik and dirty way of loading a pre-existing image - you could simply use the HBITMAP you've already captured.

Here's an example that loads a bitmap and saves it again. (I had initially used "image/png" as the output type, along with an appropriate output filename)

#include <windows.h>
#include <gdiplus.h>
using namespace Gdiplus;

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = NULL;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }
   }
   free(pImageCodecInfo);
   return -1;  // Failure
}

int main()
{
    GdiplusStartupInput gdiplusStartupInput;
    ULONG_PTR gdiplusToken;
    GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);

    HBITMAP hBitmap = (HBITMAP)LoadImage(GetModuleHandle(NULL), "babe.bmp", IMAGE_BITMAP, 0,0, LR_LOADFROMFILE);
    Bitmap *image = new Bitmap(hBitmap, NULL);

    CLSID myClsId;
    int retVal = GetEncoderClsid(L"image/bmp", &myClsId);

    image->Save(L"output.bmp", &myClsId, NULL);
    delete image;

    GdiplusShutdown(gdiplusToken);
    return 0;
}
于 2014-07-09T04:29:00.293 回答
4

我最近不得不做你正在做的同样的事情,并成功地使用了 GlobalAlloc。
此代码的基础来自This MSDN Article

看起来你从这里得到了你的示例代码

MSDN 对于 win32 操作非常可靠,在我的经验中绝对比其他网站更喜欢它。

似乎正在发生的sizeof(hDIBtochange)是返回 4,所以你只分配了 4 个字节的内存。这不足以容纳一个pbi结构。

这是我的代码GlobalAlloc,希望能显示正确的用法。

void
WriteBmpTofile(const bool remote, LPSTR pszFile, PBITMAPINFO pbi, HBITMAP hBmp, HDC hDC)
{
    HANDLE hFile;
    BITMAPFILEHEADER hdr;
    PBITMAPINFOHEADER pbih;
    LPBYTE lpBits;
    DWORD dwTemp;

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

    if(!lpBits)
    {
        return; // could not allocate bitmap
    }

    GetDIBits(hDC, hBmp, 0, (WORD)pbih->biHeight, lpBits, pbi, DIB_RGB_COLORS);

    hFile = CreateFile(pszFile,
                        GENERIC_READ | GENERIC_WRITE,
                        0,
                        NULL,
                        CREATE_ALWAYS,
                        FILE_ATTRIBUTE_NORMAL,
                        NULL);

    if(hFile == INVALID_HANDLE_VALUE)
    {
        return; // Could not open screenshot file
    }

    // type == BM
    hdr.bfType = 0x4d42;

    hdr.bfSize = (sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD) + pbih->biSizeImage);
    hdr.bfReserved1 = 0;
    hdr.bfReserved2 = 0;

    hdr.bfOffBits = sizeof(BITMAPFILEHEADER) + pbih->biSize + pbih->biClrUsed * sizeof(RGBQUAD);

    // write the bitmap file header to file
    WriteFile(hFile, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER), &dwTemp, NULL);

    // write the bitmap header to file
    WriteFile(hFile, (LPVOID)pbih, sizeof(BITMAPINFOHEADER) + pbih->biClrUsed * sizeof(RGBQUAD), &dwTemp, NULL);

    // copy the bitmap colour data into the file
    WriteFile(hFile, (LPSTR)lpBits, pbih->biSizeImage, &dwTemp, NULL);

    CloseHandle(hFile);

    GlobalFree((HGLOBAL)lpBits);
}

如果您需要,这是该 MSDN 文章中的顶级功能(再次由我修改)。

PBITMAPINFO
Print::CreateBitmapInfo(HBITMAP hBmp)
{
    BITMAP bmp;
    PBITMAPINFO pbmi;

    GetObject(hBmp, sizeof(BITMAP), &bmp);

    pbmi = static_cast<PBITMAPINFO>(LocalAlloc(LPTR, sizeof(BITMAPINFOHEADER)));

    pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth = bmp.bmWidth;
    pbmi->bmiHeader.biHeight = bmp.bmHeight;
    pbmi->bmiHeader.biPlanes = bmp.bmPlanes; // we are assuming that there is only one plane
    pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;

    // no compression this is an rgb bitmap
    pbmi->bmiHeader.biCompression = BI_RGB;

    // calculate size and align to a DWORD (8bit), we are assuming there is only one plane.
    pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * bmp.bmBitsPixel +31) & -31) * pbmi->bmiHeader.biHeight;

    // all device colours are important
    pbmi->bmiHeader.biClrImportant = 0;

    return pbmi;
}
于 2014-07-09T03:43:24.767 回答
1

我猜你从这里Storing an Image得到你的代码。不久前,我不得不修改代码以使用 WinCE 5.0 和 WinCE 6.0。这是 beta 样本,虽然它有点乱。它在没有 GlobalAlloc 的情况下完成。它使用 CreateDibSection 代替。

int CreateBMPFile(HWND hwnd, LPCTSTR pszFile, PBITMAPINFO pbi, 
                  HBITMAP hBMP, HDC hDC) 
 { 
    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; 
    int ret = 0;


    pbi = CreateBitmapInfoStruct(NULL, hBMP);
    if(pbi == NULL)
    {
        return ret;
    }
    pbih = (PBITMAPINFOHEADER) pbi; 

    /*
    lpBits = (LPBYTE) GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);
    if (!lpBits) 
    {
         //errhandler("GlobalAlloc", hwnd); 
        return;
    }
    */

    RGBQUAD *rgbq;
    rgbq = pbi->bmiColors;
    PALETTEENTRY pe[256];
    GetSystemPaletteEntries(hDC, 0, pbih->biClrUsed, pe);
    for(DWORD i = 0; i < pbih->biClrUsed; i++)
    {
        rgbq[i].rgbRed = pe[i].peRed;
        rgbq[i].rgbBlue = pe[i].peBlue;
        rgbq[i].rgbGreen = pe[i].peGreen;
        rgbq[i].rgbReserved = 0;
    }

    // CE5.0 + CE6.0
    HDC tHDC;
    tHDC = CreateCompatibleDC(hDC);
    HBITMAP h = CreateDIBSection(hDC, pbi, DIB_PAL_COLORS, (void **)&hp, NULL, 0);
    if(h == NULL)
    {
        goto close_bmp;
    }
    SelectObject(tHDC, h);
    BitBlt(tHDC, 0, 0, SCREEN_W, SCREEN_H, hDC, 0, 0, SRCCOPY);

    /*
    // Retrieve the color table (RGBQUAD array) and the bits  
    // (array of palette indices) from the DIB.  
    if (!GetDIBits(hDC, hBMP, 0, (WORD) pbih->biHeight, lpBits, pbi, 
        DIB_RGB_COLORS)) 
    {
        //errhandler("GetDIBits", hwnd); 
        return;
    }
    */

    // Create the .BMP file.  
    hf = CreateFile(pszFile, 
                   GENERIC_READ | GENERIC_WRITE, 
                   (DWORD) 0, 
                    NULL, 
                   CREATE_ALWAYS, 
                   FILE_ATTRIBUTE_NORMAL, 
                   (HANDLE) NULL); 
    if (hf == INVALID_HANDLE_VALUE) 
    {
        //errhandler("CreateFile", hwnd); 
        goto close_bmp;
    }
    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)) 
    {
       //errhandler("WriteFile", hwnd); 
        goto close_bmp;
    }

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

    // 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)) 
    {
        //errhandler("WriteFile", hwnd); 
        goto close_bmp;
    }



close_bmp:
    // Close the .BMP file.  
    if(hf != INVALID_HANDLE_VALUE)
    {
        if (!CloseHandle(hf)) 
        {
           //errhandler("CloseHandle", hwnd); 
        }
        else
        {
            ret = 1;
        }
    }
    // Free memory.  
    // GlobalFree((HGLOBAL)lpBits); 
    if(tHDC != NULL)
        DeleteObject(tHDC);
    if(h != NULL)
        DeleteObject(h);
    if(pbi != NULL)
    {
        //LocalFree(pbi);
        free(pbi);
    }

    return ret;

}
于 2014-07-09T03:25:33.177 回答