bmp是BitMap(位圖)的簡稱,也是所有windows上圖片顯示的基礎。所有的圖片格式,都必須轉換成bmp才能進行最終的顯示。所以,bmp文件的讀寫,就變得非常重要了。然而,很多人是借助於MFC類,C# 庫函數,OpenCV,OpenGL等庫函數進行bmp文件的讀寫。試想一下,如果你要在諸如DSP、FPGA之類的嵌入式設備上進行bmp文件的讀寫,總不能去安裝一個龐大的MFC,C#類庫吧?其實,我們完全可以拋開這些龐雜繁瑣的類庫和API函數,僅僅利用C語言,編寫幾個函數,就完全可以實現bmp文件的讀寫了。本文的意圖正在於此。
一個完整的bmp位圖文件,可以分為文件信息頭,位圖信息頭和RGB顏色陣列三個部分。文件信息頭主要包含“是否是BMP文件”,文件的大小等信息。而位圖信息頭則主要包含bmp文件的位圖寬度,高度,位平面,通道數等信息。而RGB顏色陣列,裡面才真正包含我們所需要的bmp位圖的像素數據。需要提醒的是,bmp位圖的顏色陣列部分,像素數據的存儲是以左下角為原點。也就是說,當你打開一個bmp圖片並顯示在電腦屏幕上的時,實際在存儲的時候,這個圖片的最左下角的像素是首先被存儲在bmp文件中的。之後,按照從左到右,從下到上的順序,依次進行像素數據的存儲。如果,你存儲的是3通道的位圖數據(也就是我們通常說的彩圖),那麼它是按照B0G0R0B1G1R1B2G2R2...的順序進行存儲的,同時,還要考慮到4字節對齊的問題。OK,了解了這些基本概念,相信,自己編程實現一些bmp文件的讀寫函數並非難事。這裡,我給出C語言的版本,僅供參考,如有錯誤,歡迎指正。
- chenLeeCV.h
- #ifndef CHENLEECV_H
- #define CHENLEECV_H
-
- typedef struct
- {
- //unsigned short bfType;
- unsigned long bfSize;
- unsigned short bfReserved1;
- unsigned short bfReserved2;
- unsigned long bfOffBits;
- } ClBitMapFileHeader;
-
- typedef struct
- {
- unsigned long biSize;
- long biWidth;
- long biHeight;
- unsigned short biPlanes;
- unsigned short biBitCount;
- unsigned long biCompression;
- unsigned long biSizeImage;
- long biXPelsPerMeter;
- long biYPelsPerMeter;
- unsigned long biClrUsed;
- unsigned long biClrImportant;
- } ClBitMapInfoHeader;
-
- typedef struct
- {
- unsigned char rgbBlue; //該顏色的藍色分量
- unsigned char rgbGreen; //該顏色的綠色分量
- unsigned char rgbRed; //該顏色的紅色分量
- unsigned char rgbReserved; //保留值
- } ClRgbQuad;
-
- typedef struct
- {
- int width;
- int height;
- int channels;
- unsigned char* imageData;
- }ClImage;
-
- ClImage* clLoadImage(char* path);
- bool clSaveImage(char* path, ClImage* bmpImg);
-
- #endif
-
- chenLeeCV.cpp
- #include "chenLeeCV.h"
- #include <stdio.h>
- #include <stdlib.h>
-
- ClImage* clLoadImage(char* path)
- {
- ClImage* bmpImg;
- FILE* pFile;
- unsigned short fileType;
- ClBitMapFileHeader bmpFileHeader;
- ClBitMapInfoHeader bmpInfoHeader;
- int channels = 1;
- int width = 0;
- int height = 0;
- int step = 0;
- int offset = 0;
- unsigned char pixVal;
- ClRgbQuad* quad;
- int i, j, k;
-
- bmpImg = (ClImage*)malloc(sizeof(ClImage));
- pFile = fopen(path, "rb");
- if (!pFile)
- {
- free(bmpImg);
- return NULL;
- }
-
- fread(&fileType, sizeof(unsigned short), 1, pFile);
- if (fileType == 0x4D42)
- {
- //printf("bmp file! \n");
-
- fread(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
- /*printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
- printf("bmp文件頭信息:\n");
- printf("文件大小:%d \n", bmpFileHeader.bfSize);
- printf("保留字:%d \n", bmpFileHeader.bfReserved1);
- printf("保留字:%d \n", bmpFileHeader.bfReserved2);
- printf("位圖數據偏移字節數:%d \n", bmpFileHeader.bfOffBits);*/
-
- fread(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
- /*printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");
- printf("bmp文件信息頭\n");
- printf("結構體長度:%d \n", bmpInfoHeader.biSize);
- printf("位圖寬度:%d \n", bmpInfoHeader.biWidth);
- printf("位圖高度:%d \n", bmpInfoHeader.biHeight);
- printf("位圖平面數:%d \n", bmpInfoHeader.biPlanes);
- printf("顏色位數:%d \n", bmpInfoHeader.biBitCount);
- printf("壓縮方式:%d \n", bmpInfoHeader.biCompression);
- printf("實際位圖數據占用的字節數:%d \n", bmpInfoHeader.biSizeImage);
- printf("X方向分辨率:%d \n", bmpInfoHeader.biXPelsPerMeter);
- printf("Y方向分辨率:%d \n", bmpInfoHeader.biYPelsPerMeter);
- printf("使用的顏色數:%d \n", bmpInfoHeader.biClrUsed);
- printf("重要顏色數:%d \n", bmpInfoHeader.biClrImportant);
- printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n");*/
-
- if (bmpInfoHeader.biBitCount == 8)
- {
- //printf("該文件有調色板,即該位圖為非真彩色\n\n");
- channels = 1;
- width = bmpInfoHeader.biWidth;
- height = bmpInfoHeader.biHeight;
- offset = (channels*width)%4;
- if (offset != 0)
- {
- offset = 4 - offset;
- }
- //bmpImg->mat = kzCreateMat(height, width, 1, 0);
- bmpImg->width = width;
- bmpImg->height = height;
- bmpImg->channels = 1;
- bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*height);
- step = channels*width;
-
- quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad)*256);
- fread(quad, sizeof(ClRgbQuad), 256, pFile);
- free(quad);
-
- for (i=0; i<height; i++)
- {
- for (j=0; j<width; j++)
- {
- fread(&pixVal, sizeof(unsigned char), 1, pFile);
- bmpImg->imageData[(height-1-i)*step+j] = pixVal;
- }
- if (offset != 0)
- {
- for (j=0; j<offset; j++)
- {
- fread(&pixVal, sizeof(unsigned char), 1, pFile);
- }
- }
- }
- }
- else if (bmpInfoHeader.biBitCount == 24)
- {
- //printf("該位圖為位真彩色\n\n");
- channels = 3;
- width = bmpInfoHeader.biWidth;
- height = bmpInfoHeader.biHeight;
-
- bmpImg->width = width;
- bmpImg->height = height;
- bmpImg->channels = 3;
- bmpImg->imageData = (unsigned char*)malloc(sizeof(unsigned char)*width*3*height);
- step = channels*width;
-
- offset = (channels*width)%4;
- if (offset != 0)
- {
- offset = 4 - offset;
- }
-
- for (i=0; i<height; i++)
- {
- for (j=0; j<width; j++)
- {
- for (k=0; k<3; k++)
- {
- fread(&pixVal, sizeof(unsigned char), 1, pFile);
- bmpImg->imageData[(height-1-i)*step+j*3+k] = pixVal;
- }
- //kzSetMat(bmpImg->mat, height-1-i, j, kzScalar(pixVal[0], pixVal[1], pixVal[2]));
- }
- if (offset != 0)
- {
- for (j=0; j<offset; j++)
- {
- fread(&pixVal, sizeof(unsigned char), 1, pFile);
- }
- }
- }
- }
- }
-
- return bmpImg;
- }
-
- bool clSaveImage(char* path, ClImage* bmpImg)
- {
- FILE *pFile;
- unsigned short fileType;
- ClBitMapFileHeader bmpFileHeader;
- ClBitMapInfoHeader bmpInfoHeader;
- int step;
- int offset;
- unsigned char pixVal = '\0';
- int i, j;
- ClRgbQuad* quad;
-
- pFile = fopen(path, "wb");
- if (!pFile)
- {
- return false;
- }
-
- fileType = 0x4D42;
- fwrite(&fileType, sizeof(unsigned short), 1, pFile);
-
- if (bmpImg->channels == 3)//24位,通道,彩圖
- {
- step = bmpImg->channels*bmpImg->width;
- offset = step%4;
- if (offset != 4)
- {
- step += 4-offset;
- }
-
- bmpFileHeader.bfSize = bmpImg->height*step + 54;
- bmpFileHeader.bfReserved1 = 0;
- bmpFileHeader.bfReserved2 = 0;
- bmpFileHeader.bfOffBits = 54;
- fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
-
- bmpInfoHeader.biSize = 40;
- bmpInfoHeader.biWidth = bmpImg->width;
- bmpInfoHeader.biHeight = bmpImg->height;
- bmpInfoHeader.biPlanes = 1;
- bmpInfoHeader.biBitCount = 24;
- bmpInfoHeader.biCompression = 0;
- bmpInfoHeader.biSizeImage = bmpImg->height*step;
- bmpInfoHeader.biXPelsPerMeter = 0;
- bmpInfoHeader.biYPelsPerMeter = 0;
- bmpInfoHeader.biClrUsed = 0;
- bmpInfoHeader.biClrImportant = 0;
- fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
-
- for (i=bmpImg->height-1; i>-1; i--)
- {
- for (j=0; j<bmpImg->width; j++)
- {
- pixVal = bmpImg->imageData[i*bmpImg->width*3+j*3];
- fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
- pixVal = bmpImg->imageData[i*bmpImg->width*3+j*3+1];
- fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
- pixVal = bmpImg->imageData[i*bmpImg->width*3+j*3+2];
- fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
- }
- if (offset!=0)
- {
- for (j=0; j<offset; j++)
- {
- pixVal = 0;
- fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
- }
- }
- }
- }
- else if (bmpImg->channels == 1)//8位,單通道,灰度圖
- {
- step = bmpImg->width;
- offset = step%4;
- if (offset != 4)
- {
- step += 4-offset;
- }
-
- bmpFileHeader.bfSize = 54 + 256*4 + bmpImg->width;
- bmpFileHeader.bfReserved1 = 0;
- bmpFileHeader.bfReserved2 = 0;
- bmpFileHeader.bfOffBits = 54 + 256*4;
- fwrite(&bmpFileHeader, sizeof(ClBitMapFileHeader), 1, pFile);
-
- bmpInfoHeader.biSize = 40;
- bmpInfoHeader.biWidth = bmpImg->width;
- bmpInfoHeader.biHeight = bmpImg->height;
- bmpInfoHeader.biPlanes = 1;
- bmpInfoHeader.biBitCount = 8;
- bmpInfoHeader.biCompression = 0;
- bmpInfoHeader.biSizeImage = bmpImg->height*step;
- bmpInfoHeader.biXPelsPerMeter = 0;
- bmpInfoHeader.biYPelsPerMeter = 0;
- bmpInfoHeader.biClrUsed = 256;
- bmpInfoHeader.biClrImportant = 256;
- fwrite(&bmpInfoHeader, sizeof(ClBitMapInfoHeader), 1, pFile);
-
- quad = (ClRgbQuad*)malloc(sizeof(ClRgbQuad)*256);
- for (i=0; i<256; i++)
- {
- quad[i].rgbBlue = i;
- quad[i].rgbGreen = i;
- quad[i].rgbRed = i;
- quad[i].rgbReserved = 0;
- }
- fwrite(quad, sizeof(ClRgbQuad), 256, pFile);
- free(quad);
-
- for (i=bmpImg->height-1; i>-1; i--)
- {
- for (j=0; j<bmpImg->width; j++)
- {
- pixVal = bmpImg->imageData[i*bmpImg->width+j];
- fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
- }
- if (offset!=0)
- {
- for (j=0; j<offset; j++)
- {
- pixVal = 0;
- fwrite(&pixVal, sizeof(unsigned char), 1, pFile);
- }
- }
- }
- }
- fclose(pFile);
-
- return true;
- }
-
- Main.cpp
- #include "stdafx.h"
- #include "chenLeeCV.h"
-
-
- int _tmain(int argc, _TCHAR* argv[])
- {
- ClImage* img = clLoadImage("c:/test.bmp");
- bool flag = clSaveImage("c:/result.bmp", img);
- if(flag)
- {
- printf("save ok... \n");
- }
-
-
- while(1);
- return 0;
- }