C Win32: сохранить изображение .bmp из HBITMAP

Я работаю с фреймграббером, и мне нужно получить изображения из памяти компьютера и сохранить их в файле изображения. после нескольких дней попыток я получаю следующие 2 функции, которые создают файл, а ОС Windows может запускать файл .bmp, но файл растрового изображения черный (размер изображения составляет 900 КБ, 640 * 480). Кто-нибудь знает, почему картинка черная? вот две функции:

LPSTR CreateBMP( HWND hAppWnd, int nImageType )
{          
    void            * pWinGBits = NULL;
    int             i;
    Z_BITMAPINFO    zWinGHeader;    //  bitmapinfo for cerating the DIB
    //  create DC for bitmap.
    hDCBits = CreateCompatibleDC( ghDCMain );

    switch ( nImageType )
    {
        case bayer_filter:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize;
        zWinGHeader.bmiHeader.biBitCount        = 32;
        zWinGHeader.bmiHeader.biClrUsed         = 0;//3;
        zWinGHeader.bmiHeader.biCompression     = BI_BITFIELDS;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        zWinGHeader.bmiColors[0].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[0].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[0].rgbRed         = 0xFF;
        zWinGHeader.bmiColors[0].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[1].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[1].rgbGreen       = 0xFF;
        zWinGHeader.bmiColors[1].rgbRed         = 0x00;
        zWinGHeader.bmiColors[1].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[2].rgbBlue        = 0xFF;
        zWinGHeader.bmiColors[2].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[2].rgbRed         = 0x00;
        zWinGHeader.bmiColors[2].rgbReserved    = 0x00;
        break;

        case color32:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize/4;
        zWinGHeader.bmiHeader.biBitCount        = 32;
        zWinGHeader.bmiHeader.biClrUsed         = 0;
        zWinGHeader.bmiHeader.biCompression     = BI_BITFIELDS;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        zWinGHeader.bmiColors[0].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[0].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[0].rgbRed         = 0xFF;
        zWinGHeader.bmiColors[0].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[1].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[1].rgbGreen       = 0xFF;
        zWinGHeader.bmiColors[1].rgbRed         = 0x00;
        zWinGHeader.bmiColors[1].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[2].rgbBlue        = 0xFF;
        zWinGHeader.bmiColors[2].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[2].rgbRed         = 0x00;
        zWinGHeader.bmiColors[2].rgbReserved    = 0x00;
        break;

        case color24:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize/3;
        zWinGHeader.bmiHeader.biBitCount        = 24;
        zWinGHeader.bmiHeader.biClrUsed         = 0;
        zWinGHeader.bmiHeader.biCompression     = BI_RGB;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        break;

        case color3x16:
        zWinGHeader.bmiHeader.biSize            = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes          = 1;
        zWinGHeader.bmiHeader.biClrImportant    = 0;
        zWinGHeader.bmiHeader.biHeight          = -lYSize;
        zWinGHeader.bmiHeader.biWidth           = lXSize/6;
        zWinGHeader.bmiHeader.biBitCount        = 32;
        zWinGHeader.bmiHeader.biClrUsed         = 0;
        zWinGHeader.bmiHeader.biCompression     = BI_BITFIELDS;
        zWinGHeader.bmiHeader.biSizeImage       = 0;
        zWinGHeader.bmiColors[0].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[0].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[0].rgbRed         = 0xFF;
        zWinGHeader.bmiColors[0].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[1].rgbBlue        = 0x00;
        zWinGHeader.bmiColors[1].rgbGreen       = 0xFF;
        zWinGHeader.bmiColors[1].rgbRed         = 0x00;
        zWinGHeader.bmiColors[1].rgbReserved    = 0x00;

        zWinGHeader.bmiColors[2].rgbBlue        = 0xFF;
        zWinGHeader.bmiColors[2].rgbGreen       = 0x00;
        zWinGHeader.bmiColors[2].rgbRed         = 0x00;
        zWinGHeader.bmiColors[2].rgbReserved    = 0x00;
        break;

        case bw1x10:
        //  create bitmap-infoheader.
        zWinGHeader.bmiHeader.biSize        = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes      = 1;
        zWinGHeader.bmiHeader.biBitCount    = 8;
        zWinGHeader.bmiHeader.biCompression = BI_RGB;
        zWinGHeader.bmiHeader.biSizeImage   = 0;
        zWinGHeader.bmiHeader.biClrUsed     = 256;
        zWinGHeader.bmiHeader.biClrImportant= 0;
        zWinGHeader.bmiHeader.biHeight      = -lYSize;
        zWinGHeader.bmiHeader.biWidth       = lXSize/2;

        //  create colortable fot bitmap (grayvalues).
        for (i = 0; i < 256; i++) 
        {
            zWinGHeader.bmiColors[i].rgbGreen   = i;
            zWinGHeader.bmiColors[i].rgbBlue    = i;
            zWinGHeader.bmiColors[i].rgbRed     = i;

            zWinGHeader.bmiColors[i].rgbReserved = 0;
        }
        break;

        default:
        case bw8:
        //  create bitmap-infoheader.
        zWinGHeader.bmiHeader.biSize        = sizeof( BITMAPINFOHEADER );
        zWinGHeader.bmiHeader.biPlanes      = 1;
        zWinGHeader.bmiHeader.biBitCount    = 8;
        zWinGHeader.bmiHeader.biCompression = BI_RGB;
        zWinGHeader.bmiHeader.biSizeImage   = 0;        
        zWinGHeader.bmiHeader.biClrUsed     = (1<<8);       
        zWinGHeader.bmiHeader.biClrImportant= 0;
        zWinGHeader.bmiHeader.biHeight      = -lYSize;
        zWinGHeader.bmiHeader.biWidth       = lXSize;
        //zWinGHeader.bmiHeader.biSizeImage = ((zWinGHeader.bmiHeader.biWidth * 8 +31) & ~31) /8
            //                                  * zWinGHeader.bmiHeader.biHeight; 

        //  create colortable fot bitmap (grayvalues).
        for (i = 0; i < 256; i++) 
        {
            zWinGHeader.bmiColors[i].rgbGreen   = i;        
            zWinGHeader.bmiColors[i].rgbBlue    = i;
            zWinGHeader.bmiColors[i].rgbRed     = i;            
            zWinGHeader.bmiColors[i].rgbReserved = 0;           
        }
        break;
    }

    //  cerate identity palette 
    hPal = CreateIdentityPalette( zWinGHeader.bmiColors );

    //  get new palette into DC and map into physical palette register.
    hOldPal = SelectPalette( ghDCMain, hPal, FALSE);    
    RealizePalette( ghDCMain );

    //  cerate DIB-Section f黵 direct access of image-data.
    hBitmap = CreateDIBSection(
        hDCBits,                        //  handle of device context
        (BITMAPINFO *)&zWinGHeader,     //  address of structure containing
                                        //  bitmap size, format and color data
        DIB_RGB_COLORS,                 //  color data type indicator: RGB values
                                        //  or palette indices
        &pWinGBits,                     //  pointer to variable to receive a pointer
                                        //  to the bitmap's bit values
        NULL,                           //  optional handle to a file mapping object
        0                               //  offset to the bitmap bit values within
                                        //  the file mapping object
    );      
    //  get bitmap into DC .
    hOldBitmap = (HBITMAP)SelectObject( hDCBits, hBitmap );

    return pWinGBits;           //  return pointer to DIB 
}

а вот функция сохранения в .bmp:

BOOL SaveToFile(HBITMAP hBitmap3, LPCTSTR lpszFileName)
{   
  HDC hDC;
  int iBits;
  WORD wBitCount;
  DWORD dwPaletteSize=0, dwBmBitsSize=0, dwDIBSize=0, dwWritten=0;
  BITMAP Bitmap0;
  BITMAPFILEHEADER bmfHdr;
  BITMAPINFOHEADER bi;
  LPBITMAPINFOHEADER lpbi;
  HANDLE fh, hDib, hPal,hOldPal2=NULL;
  hDC = CreateDC("DISPLAY", NULL, NULL, NULL);
  iBits = GetDeviceCaps(hDC, BITSPIXEL) * GetDeviceCaps(hDC, PLANES);
  DeleteDC(hDC);
  if (iBits <= 1)
    wBitCount = 1;
  else if (iBits <= 4)
    wBitCount = 4;
  else if (iBits <= 8)
    wBitCount = 8;
  else
    wBitCount = 24; 
  GetObject(hBitmap3, sizeof(Bitmap0), (LPSTR)&Bitmap0);
  bi.biSize = sizeof(BITMAPINFOHEADER);
  bi.biWidth = Bitmap0.bmWidth;
  bi.biHeight =-Bitmap0.bmHeight;
  bi.biPlanes = 1;
  bi.biBitCount = wBitCount;
  bi.biCompression = BI_RGB;
  bi.biSizeImage = 0;
  bi.biXPelsPerMeter = 0;
  bi.biYPelsPerMeter = 0;
  bi.biClrImportant = 0;
  bi.biClrUsed = 256;
  dwBmBitsSize = ((Bitmap0.bmWidth * wBitCount +31) & ~31) /8
                                                * Bitmap0.bmHeight; 
  hDib = GlobalAlloc(GHND,dwBmBitsSize + dwPaletteSize + sizeof(BITMAPINFOHEADER));
  lpbi = (LPBITMAPINFOHEADER)GlobalLock(hDib);
  *lpbi = bi;

  hPal = GetStockObject(DEFAULT_PALETTE);
  if (hPal)
  { 
    hDC = GetDC(NULL);
    hOldPal2 = SelectPalette(hDC, (HPALETTE)hPal, FALSE);
    RealizePalette(hDC);
  }


  GetDIBits(hDC, hBitmap3, 0, (UINT) Bitmap0.bmHeight, (LPSTR)lpbi + sizeof(BITMAPINFOHEADER) 
    +dwPaletteSize, (BITMAPINFO *)lpbi, DIB_RGB_COLORS);

  if (hOldPal2)
  {
    SelectPalette(hDC, (HPALETTE)hOldPal2, TRUE);
    RealizePalette(hDC);
    ReleaseDC(NULL, hDC);
  }

  fh = CreateFile(lpszFileName, GENERIC_WRITE,0, NULL, CREATE_ALWAYS, 
    FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); 

  if (fh == INVALID_HANDLE_VALUE)
    return FALSE; 

  bmfHdr.bfType = 0x4D42; // "BM"
  dwDIBSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + dwPaletteSize + dwBmBitsSize;
  bmfHdr.bfSize = dwDIBSize;
  bmfHdr.bfReserved1 = 0;
  bmfHdr.bfReserved2 = 0;
  bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + (DWORD)sizeof(BITMAPINFOHEADER) + dwPaletteSize;

  WriteFile(fh, (LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER), &dwWritten, NULL);

  WriteFile(fh, (LPSTR)lpbi, dwDIBSize, &dwWritten, NULL);
  GlobalUnlock(hDib);
  GlobalFree(hDib);
  CloseHandle(fh);
  counter=1;
  return TRUE;
} 

Я МОГУ идеально рисовать изображения из памяти с помощью следующей функции, поэтому у меня наверняка нет проблем с чтением данных изображения:

void DrawPicture( HDC hDC, PBYTE pDest, PBYTE pSrc, LONG lXSize, LONG lYSize )
{    
LONG    lXSizeDiv, lYSizeDiv;
LONG    lX, lY;
DWORD   dwMax;
RECT    rect;
HDC     hdc;
PBYTE   pTmpDest;
double  fXFactor, fYFactor;
//  HBRUSH  hBrush;
//  POINT   Point;


switch( gzMvfgKamDef.iImageType )
{
    case bayer_filter:
    lXSizeDiv = 1;
    lYSizeDiv = 1;
    BayerToRGB( (PDWORD) pDest, pSrc, lXSize, lYSize, 
                gzMvfgKamDef.iBayerQuadrant, gzMvfgKamDef.iBayerQuality );

    break;

    case color24:
    lXSizeDiv = 3;
    lYSizeDiv = 1;
    memcpy( pDest, pSrc, (size_t)( lXSize * lYSize ) );

break;

    case color32:
    lXSizeDiv = 4;
    lYSizeDiv = 1;
    memcpy( pDest, pSrc, (size_t)( lXSize * lYSize ) );


    break;

    case color3x16:
    lXSizeDiv = 6;
    lYSizeDiv = 1;
    Conv3x16To3x8( pDest, pSrc, 4, lXSize, lYSize );


    break;

    case bw1x10:
    lXSizeDiv = 2;
    lYSizeDiv = 1;
    Conv1x10To1x8( pDest, pSrc, 2, lXSize, lYSize );


    break;

    case bw6Tap:
    lXSizeDiv = 1;
    lYSizeDiv = 1;
    Conv6TapTo1x8( pDest, pSrc, 1, lXSize, lYSize );

    break;

    default:
    case bw8:
    lXSizeDiv = 1;
    lYSizeDiv = 1;
    memcpy( pDest, pSrc, (size_t)( lXSize * lYSize ) );

    break;

}


if( gahIniDlg[ NdxHistogramDlg ].hWnd )
{
    memset( gadwHistogram, 0, sizeof( gadwHistogram) );

    pTmpDest = pDest;

    for ( lY = 0; lY < lYSize; lY++ )
    {
        for ( lX = 0; lX < lXSize; lX++ )
        {
            gadwHistogram[ *pTmpDest ]++;
            pTmpDest++;
        }
    }

    GetClientRect ( gahIniDlg[ NdxHistogramDlg ].hWnd, &rect) ;

    hdc = GetDC ( gahIniDlg[ NdxHistogramDlg ].hWnd );

    dwMax = 0;
    for ( lX = 0 ; lX <= 0xff; lX++ )
        dwMax = ( gadwHistogram[ lX ] > dwMax ) ? gadwHistogram[ lX ] : dwMax;

    fYFactor = (double) dwMax / (double) rect.bottom;
    fXFactor = (double) ( rect.right - 100 ) / (double) 0x100;

/*
    SelectObject (hdc, GetStockObject (BLACK_PEN)) ;

    for( lX = 0; lX <= 0xff; lX+=8 )
    {
        MoveToEx( hdc, lX * fXFactor, rect.bottom, &Point );
        LineTo( hdc, lX * fXFactor, rect.bottom-10 );
    }
*/
    SelectObject (hdc, GetStockObject (WHITE_PEN)) ;

//      hBrush = CreateSolidBrush( GetSysColor( COLOR_WINDOW ));
//      hBrush = CreateSolidBrush( 0xffffff );
//      SelectObject (hdc, hBrush);


    Polyline( hdc, gaHistogram, 0x100 );

//      DeleteObject( hBrush );

    for ( lX = 0 ; lX <= 0xff; lX++ )
    {
        gaHistogram[ lX ].x = (DWORD)( fXFactor * (double)lX ); 
        gaHistogram[ lX ].y = rect.bottom - (DWORD)( (double) gadwHistogram[ lX ] / fYFactor );
    }

    SelectObject (hdc, GetStockObject (BLACK_PEN)) ;

    Polyline( hdc, gaHistogram, 0x100 );

    ReleaseDC ( gahIniDlg[ NdxHistogramDlg ].hWnd , hdc ) ;
}

//  display the bitmap
StretchBlt( hDC, rcWin.left, rcWin.top, lXSize / lXSizeDiv, lYSize / lYSizeDiv, 
        hDCBits, 0, 0, lXSize / lXSizeDiv, lYSize / lYSizeDiv, SRCCOPY );
//BitBlt( hDC, rcWin.left, rcWin.top, lXSize / lXSizeDiv, lYSize / lYSizeDiv, 
            //hDCBits, 0, 0,  SRCCOPY );

}

person user261002    schedule 06.03.2012    source источник


Ответы (4)


Я только что узнал, что у меня была глупая ошибка в другой части моего кода, который я использовал:

// Convert Image to bitmap and display it
DrawPicture( ghDCMain, pWinGBitmap, gpbImageData, lXSize, lYSize  );    
if(counter!=1) {
  hBitmap2 = CreateCompatibleBitmap (hDCBits, lXSize, lYSize);
  SaveToFile(hBitmap2, "c:\\t.bmp");
  OutputDebugString("tested !!!!");
}

удалив строку hBitmap2 = CreateCompatibleBitmap (hDCBits, lXSize, lYSize); и изменив hBitmap2 на hBitmap результат CreateDIBSection(), он прекрасно сохранил изображение. СПАСИБО ВСЕМ ЗА ПОМОЩЬ.

person user261002    schedule 09.03.2012
comment
я ничего не менял в DrawPicture(); функция - person user261002; 12.03.2012

FORMAT_INFO sFormat;
mvfg_getparam( MVFGPAR_DATAFORMAT, &sFormat, giCurrentGrabberID);
long lFrameSize     = sFormat.lFrameSize;

BYTE *pBitmap = (BYTE*)malloc(FrameSize);
memcpy( pBitmap, CamGetPicture(), FrameSize );
writeBitmapToFile(pBitmap, FrameSize, "tmp.bmp");

/*
 * write our raw data into a bitmap file
 * adding the correct header and put it all
 * together in a single file
 */
int
writeBitmapToFile(void *data, unsigned long size, const char *filename)
{
    bmpFileHeader_t header;
    bmpInfoHeader_t info;
    bmpRGBQuad_t rgb = { 0, 0, 0, 0};
    FILE *out;
    size_t len;
    int i;


    header.type = 0x4d42; // magic sequence 'BM'
    header.size = 0;
    header.res0 = 0;
    header.res1 = 0;
    /* 256 different colors, each is defined by an bmpRBQuad type */
    header.offset = 256 * sizeof(bmpRGBQuad_t) + 
                    sizeof(bmpInfoHeader_t) + sizeof(bmpFileHeader_t);
    info.size = sizeof(bmpInfoHeader_t);
    info.width = WIDTH;
    info.height = HEIGHT;
    info.planes = 1;
    info.cDepth = 8; /* 8 Bit */
    info.compression = 0;
    info.rawSize = size;
    info.hRes = 0;
    info.vRes = 0;
    info.nrColors = 0x100;
    info.impColors = 0;

    out = fopen(filename, "wb");
    if (out == NULL) {
        printf("error cannot open %s for writing\n", filename);
        return -1;
    }
    len = fwrite(&header, 1, sizeof(header), out);
    if (len != sizeof(header)) {
        printf("error while writing header\n");
        return -1;
    }
    len = fwrite(&info, 1, sizeof(info), out);
    if (len != sizeof(info)) {
        printf("error while writing info header\n");
        return -1;
    }
    /* stupid try and error programming leads to this */
    for (i = 0; i < 256; i++) {
        rgb.red = i;
        rgb.green = i;
        rgb.blue = i;
        len = fwrite(&rgb, 1, sizeof(rgb), out);
        if (len != sizeof(rgb)) {
            printf("error while writing rgb header\n");
            return -1;
        }
    }

    len = fwrite(data, 1, size, out);
    if (len != size ) {
        printf("error while writing bitmap data\n");
        return -1;
    }
    return 0;
}

Справочник — микротрон

Справочник — запись растрового изображения

person perreal    schedule 06.03.2012
comment
я изменил свой код с помощью вашего руководства, которое дает мне файл .bin, не могли бы вы сообщить мне, как я могу преобразовать этот файл в изображение? Спасибо - person user261002; 06.03.2012
comment
@ user261002, попробуйте изменить расширение на bmp и открыть его как растровый файл. - person perreal; 06.03.2012
comment
perreal большое спасибо за ваш быстрый ответ. Я изменил fomart на .bmp, а также попытался открыть его с помощью средства просмотра фотографий Windows, но все равно он мне ничего не показывает. как вы думаете, что мне делать? пожалуйста дайте мне знать, спасибо . о, просто чтобы вы знали, у меня тоже есть адрес смещения, как вы думаете, мне добавить смещение? - person user261002; 06.03.2012
comment
@ user261002, у меня нет возможности проверить это, но, надеюсь, это поможет. - person perreal; 06.03.2012
comment
привет perreal, большое спасибо за вашу помощь, я обновил код, а также я проверял ссылку - писать растровое изображение, но я все еще получаю растровое изображение, которое ничего не показывает. :(( есть ли у вас предложения? - person user261002; 07.03.2012

Вы уверены, что там есть правильные данные?

Я бы попробовал извлечь данные RGB и сохранить их в каком-нибудь очень простом формате "вручную". ", просто чтобы посмотреть, что там. Если вы ищете очень простой, но все же стандартный формат изображения, посмотрите на PPM. изображения.

person Prof. Falken    schedule 09.03.2012
comment
привет, спасибо за ваше редактирование, у меня есть RGBQUAD bmicolors[256]; в моем заголовочном файле также используется for loor : for (i = 0; i ‹ 256; i++) { zWinGHeader.bmiColors[i]. rgbЗеленый = я; zWinGHeader.bmiColors[i].rgbBlue = i; zWinGHeader.bmiColors[i].rgbRed = i; zWinGHeader.bmiColors[i].rgbReserved = 0; } для 8-битного изображения, как вы думаете, это правильно? - person user261002; 09.03.2012

Из документации по CreateCompatibleDC:

Когда DC памяти создается, его поверхность отображения имеет ровно один монохромный пиксель в ширину и один монохромный пиксель в высоту. Прежде чем приложение сможет использовать контроллер домена памяти для операций рисования, оно должно выбрать растровое изображение правильной ширины и высоты в контроллер домена.

Поскольку вы используете CreateDIBSection с этим контроллером домена, результирующий раздел DIB также будет 1-битным монохромным.

person Mark Ransom    schedule 09.03.2012