【问题标题】:C++: Hbitmap/BITMAP into .bmp file [duplicate]C ++:将Hbitmap / BITMAP转换为.bmp文件[重复]
【发布时间】:2014-08-29 22:47:04
【问题描述】:

好的,整个故事是,我正在尝试在 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,在内存中.. 找到一种方法保存到位图并定期创建它们以读取.. 完全找到另一种更有意义的方法..

更喜欢第一个,但是,我正在寻求解决方案,而不是第二个..或第三个。

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

【问题讨论】:

    标签: c++ hbitmap leptonica dib


    【解决方案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;
    
    }
    

    【讨论】:

    • 我会在早上尽可能测试这段代码。但是不,我从论坛帖子中获得了我的代码。没有任何东西直接来自 MSDN。一个来自 Stack Overflow 的函数,另一个来自 CodeGuru。忘记哪个是哪个。
    【解决方案2】:

    我最近不得不做和你一样的事情,并且成功地使用了 GlobalAlloc。
    这段代码的基础来自This MSDN Article

    看起来像你Got your example code from here

    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;
    }
    

    【讨论】:

      【解决方案3】:

      将 HBITMAP 保存到文件的一种更简单的方法是使用 GDI+。 这使您能够保存为 Windows 原生支持的任何格式,同时让您摆脱玩弄甚至需要理解各种图像格式的麻烦。

      在下面的示例中,我刚刚使用 LoadImage 作为加载预先存在的图像的一种快速而肮脏的方式 - 您可以简单地使用您已经捕获的 HBITMAP。

      这是一个加载位图并再次保存的示例。 (我最初使用“image/png”作为输出类型,以及适当的输出文件名)

      #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;
      }
      

      【讨论】:

      • 谢谢。如果我能弄清楚,其他人的代码会起作用,但你的代码非常简单,并且完全符合我的需要。
      • 不客气。 GDI+ 已经存在了 10 多年,在很多事情上比 DIY 解决方案更容易/更好。
      猜你喜欢
      • 2013-02-23
      • 2012-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-25
      相关资源
      最近更新 更多