白黒BMPファイルの読み書きとデータ白黒画像表示


画像処理にとって、bmp画像から含まれる情報をどのように読み取るかは、非常に重要である.matlabの場合、文は1文で十分です.
im=imread('a.bmp');

c++がファイルをメモリに読み込む方法、またはデータをbmp白黒画像に保存する方法については、画像がどのように格納されているかを検討する必要があります.一般に,白黒画像はファイルヘッダ,情報ヘッダ,パレット,最後にビットマップマトリクス情報からなる.ファイルヘッダは、画像の識別子、サイズ、オフセット量などからなり、以下のように定義されます.
typedef struct tagBITMAPFILEHEADER {
        WORD    bfType;
        DWORD   bfSize;
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;
} BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER;

ヘッダーは、ファイルの縦横などの情報で構成され、次のように定義されます.
typedef struct tagBITMAPINFOHEADER{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BITMAPINFOHEADER, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER;

その後はパレット、白黒画像パレットは次のようになります.
typedef struct tagRGBQUAD {
        BYTE    rgbBlue;
        BYTE    rgbGreen;
        BYTE    rgbRed;
        BYTE    rgbReserved;
} RGBQUAD;

最後に,情報のビット数は一般的に8 bit,最大値は255である2次元画像情報に注目する.
BMPの白黒画像を読み取るには、プログラムは次のように書くことができます.
bool LoadFromFile(const char* str, unsigned char* & data, int & width, int & height)
{
    //            
    ifstream fin(str,ios::in|ios::_Nocreate|ios::binary);
    if(!fin)
        return false;

    fin.seekg(0, ios::end);
    DWORD  size = fin.tellg();  

    //         1024+1024+40     
    if(size < 14+1024+40)
    {
        return false;
    }
    pAllData = new BYTE[size];
    memset(pAllData, 0, size);

    fin.seekg(0, ios::beg);
    fin.read((char*)pAllData, size);

    //          0x4d42,    
    LPBITMAPFILEHEADER pBitMapFileHeader = (LPBITMAPFILEHEADER)pAllData;
    if(pBitMapFileHeader->bfType != 0x4d42)
    {
        pBitMapFileHeader = NULL;
        delete [] pAllData;

        return false;
    }

    //          
    LPBITMAPINFO pBitMapInfo = (LPBITMAPINFO) (pAllData + 14);
    width = pBitMapInfo->bmiHeader.biWidth;
    height = pBitMapInfo->bmiHeader.biHeight;

    //         
    data = new unsigned char[width*height];
    memcpy(data, pAllData + 14 + 40 + 1024, width*height);

    //    ,      
    delete [] pAllData;

    return true;
}

BMPの白黒ファイルにデータを書き込む場合、プログラムは次のように書くことができます.
bool FiberWriteBitmap(UINT8* pImage, int biHeight , int biWidth, CString csPath)
{

    BITMAPFILEHEADER BitmapFileHead;
    BITMAPINFOHEADER BitmapInfoHead;

    BitmapInfoHead.biSize = 40;
    BitmapInfoHead.biBitCount = 8;
    BitmapInfoHead.biClrUsed = 256;
    BitmapInfoHead.biCompression = 0;
    BitmapInfoHead.biHeight = biHeight ;
    BitmapInfoHead.biPlanes = 1;    
    BitmapInfoHead.biClrImportant = 0;
    BitmapInfoHead.biWidth = biWidth;
    BitmapInfoHead.biXPelsPerMeter = 2835;//    72 dpi
    BitmapInfoHead.biYPelsPerMeter = 2835;
    BitmapInfoHead.biSizeImage = (DWORD)biHeight*biWidth;

    static RGBQUAD palette[256];
    for ( unsigned int i = 0; i< 256; i++ )
    {
        palette[i].rgbBlue = i;
        palette[i].rgbGreen = i;
        palette[i].rgbRed = i;
    }

    // Build fileheader and write
    BitmapFileHead.bfType=('M' << 8) + 'B'; 
    BitmapFileHead.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+
        256*sizeof(RGBQUAD);
    BitmapFileHead.bfSize = BitmapFileHead.bfOffBits + BitmapInfoHead.biSizeImage;
    BitmapInfoHead.biClrUsed=256;
    BitmapInfoHead.biClrImportant=0;
    BitmapFileHead.bfReserved1=0;
    BitmapFileHead.bfReserved2=0;

    CFile file;
    if(!file.Open(csPath, CFile::modeCreate|CFile::modeWrite))
        return false;
    file.Write(&BitmapFileHead,sizeof(BITMAPFILEHEADER));
    file.Write(&BitmapInfoHead,sizeof(BITMAPINFOHEADER));
    file.Write(palette,sizeof(RGBQUAD)*256);
    file.Write(pImage,biHeight*biWidth);

    file.Close();   
    return TRUE;
}

マトリクス情報を白黒で画面に表示します.プログラムは次のとおりです.
bool ShowDataToWindow(CDC *pDC,DWORD width, DWORD height, LPBYTE lpData, int xDest, int yDest, int DestWidth, int DestHeight, int xSrc, int ySrc, int SrcWidth, int SrcHeight)
{
    if(lpData == NULL)
        return false;

    LPBITMAPINFO lpBitMapInfo = (LPBITMAPINFO)new BYTE[40+1024];
        lpBitMapInfo->bmiHeader.biSize = 40;
        lpBitMapInfo->bmiHeader.biBitCount = 8;
        lpBitMapInfo->bmiHeader.biClrUsed = 256;
        lpBitMapInfo->bmiHeader.biCompression = 0;
        lpBitMapInfo->bmiHeader.biHeight = height;
        lpBitMapInfo->bmiHeader.biPlanes = 1;       
        lpBitMapInfo->bmiHeader.biClrImportant = 0;
        lpBitMapInfo->bmiHeader.biWidth = width;
        lpBitMapInfo->bmiHeader.biXPelsPerMeter = 2835;
        lpBitMapInfo->bmiHeader.biYPelsPerMeter = 2835;
        lpBitMapInfo->bmiHeader.biSizeImage = (DWORD)height*width;

    LPBYTE lpQuad = (LPBYTE)lpBitMapInfo;
        for(int i = 0;i<256;++i)
        {
            *(lpQuad+40+i*4+0) = (BYTE)i;
            *(lpQuad+40+i*4+1) = (BYTE)i;
            *(lpQuad+40+i*4+2) = (BYTE)i;
            *(lpQuad+40+i*4+3) = 0xcc;
        }
    pDC->SetStretchBltMode(COLORONCOLOR);
    StretchDIBits(pDC->GetSafeHdc(), xDest, yDest, DestWidth, DestHeight, 
        xSrc, ySrc, SrcWidth, SrcHeight, lpData, lpBitMapInfo, DIB_RGB_COLORS, SRCCOPY);
    delete[] lpBitMapInfo;
    return true;
}