2014-07-09 26 views
1

Хорошо, вся история, я пытаюсь использовать Leptonica + Tesseract OCR на C++, чтобы сделать снимок экрана, сохранить его в * .bmp-файле, а затем загрузить его обратно в OCR с этим. Мне не нужно будет делать это часто, но поскольку я не могу копировать данные скриншота непосредственно в структуру PIX Leptonica, мне нужно сначала сохранить его в файл. Фактически, решение было бы предпочтительным.C++: Hbitmap/BITMAP в .bmp файл

Вот код, который я нашел онлайн, пытаясь помочь мне.

экрана крышка:

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

бессовестно скопированный с постов людей на протяжении многих лет. Хорошо! Проблема, с которой я сталкиваюсь, заключается в том, что я не могу понять, как GlobalAlloc Hbitmap в глобально доступную ручку, которую можно преобразовать или использовать с LPBITMAPINFOHEADER. Вскоре, когда lpbi создается, каждое отдельное поле внутри него является ошибкой «Невозможно прочитать память» в отладке Visual Studio 2012. Это недоступно, несмотря на то, что оно создано.

Solutions .. Идите прямо от ScreenCap к PIX, в памяти .. Найти способ сохранить растровое изображение и создавать их периодически читать .. Найти другой путь целиком, что делает больше смысла ..

Предпочитаю сначала, но, я прошу решения в этом, ко второму ... или третьему.

Если вам нужна дополнительная информация, я могу попытаться ее предоставить. Это в основном сводится к тому, что «я никогда раньше не делал такой код, и его не преподавали в моих классах, поэтому я стараюсь учиться, когда я иду».

ответ

5

А гораздо более простой способ сохранить файл HBITMAP в файле - использовать GDI +. Это дает вам преимущество в том, что вы можете сохранять в любом формате, который поддерживает окна изначально, освобождая вас от гадости от игры или даже нуждаясь в понимании различных форматов изображений.

В приведенном ниже примере я только что использовал 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; 
} 
+0

Спасибо. Код других людей работал бы, если бы я мог понять это, но ваш был очень прост и сделал именно то, что мне было нужно. – user3285714

+0

Добро пожаловать. GDI + существует уже более 10 лет и намного проще/лучше, чем DIY-решения для многих и многих других. – enhzflep

1

Я предполагаю, что вы получили свой код отсюда Storing an Image. Некоторое время назад мне пришлось изменить код для работы с WinCE 5.0 и WinCE 6.0. Вот бета-образец, хотя он довольно грязный. Он делает это без 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; 

} 
+0

Я проверю этот код утром, когда смогу. Но нет, я получил свой код с сообщений на форуме. Из MSDN ничего не поступало. Одна функция из переполнения стека, другая из CodeGuru. Забудьте, какой был. – user3285714

4

Я недавно должен был сделать то же самое, что и вы, и успешно использовал 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; 
}