如果是保存字节对齐的24位彩色图像,操作相对简单,只用依次写入文件头,信息头和图像像素即可,如下列代码中函数SaveRectClrBmpImg()所示。如果是保存8位灰度图像,则需要再额外写入调色板信息,如代码中函数SaveGrayBmpImg()所示。这里对256位灰度图像的调色板的定义是red=green=blue分量,从0到255一共256个调色板分量。
保存图像时需要考虑图像的4字节对齐问题,对齐的方法是在每行像素的末尾补0。在 SaveGrayBmpImg()函数中假设输入图像数据是4字节对齐的,不用补0,如果图像数据不是4字节对齐的,则需要进行补0操作;在SaveRectClrBmpImg()函数中要在图像中间截取图像的一部分,要根据需要在行末尾补0。
保存BMP图像
//功能:从24位真彩色图像中截取一部分并写到文件中
//输入:szFileName,文件名;
//输入:pBmpBits,图像像素指针,每行像素字节数是4对齐的
//输入:nWidth,图像宽度;nHeight,图像高度,可以是负数
//输入:rectCut,原图像中截取的矩形区域
//输出:保存成功,返回true,保存失败,返回false
bool SaveRectClrBmpImg(char *szFileName, const unsigned char *pBmpBits,
int nWidth, int nHeight, CRect rectCut)
{
FILE *pFile = NULL;
if (fopen_s(&pFile, szFileName, "wb")) {
std::cout << "打开文件失败" << std::endl;
return false;
}
int nDstWidth = rectCut.Width(); //截取图像宽度
int nDstHeight = rectCut.Height(); //截取图像高度
BITMAPFILEHEADER bmfHeader; //文件头对象
bmfHeader.bfType = DIB_HEADER_MARKER; //'BM'
bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER); //真彩色图像没有调色板
bmfHeader.bfSize = bmfHeader.bfOffBits +
nDstHeight*WIDTHBYTES(nDstWidth*24); //BMP文件大小
//Y坐标要根据情况进行转换
int nLeft = rectCut.left;
int nRight = rectCut.right;
int nTop = 0, nBot = 0;
if (nHeight > 0) {
nTop = nHeight - rectCut.bottom;
nBot = nHeight - rectCut.top;
}
else {
nTop = rectCut.top;
nBot = rectCut.bottom;
nDstHeight = -nDstHeight;
}
//写入文件头
if (fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER),1,pFile)!= 1){
std::cout << "写文件头失败" << std::endl;
fclose(pFile);
return false;
}
//BMP信息头
BITMAPINFOHEADER infoHeader;
memset(&infoHeader, 0, sizeof(BITMAPINFOHEADER));
infoHeader.biBitCount = 24;
infoHeader.biHeight = nDstHeight;
infoHeader.biWidth = nDstWidth;
infoHeader.biPlanes = 1;
infoHeader.biSize = sizeof(BITMAPINFOHEADER); //40
//写入信息头
if (fwrite(&infoHeader, sizeof(BITMAPINFOHEADER), 1,pFile)!= 1){
std::cout << "写信息头失败" << std::endl;
fclose(pFile);
return false;
}
//原图像每行字节数
int nSrcLineBytes = WIDTHBYTES(nWidth * 24);
//目标图像每行字节数
int nDstLineBytes = WIDTHBYTES(nDstWidth * 24);
//目标图像每行实际像素所占用的字节数
int nPixelBytes = nDstWidth * 3;
//需要填充的字节数
int nPadSize = nDstLineBytes - nPixelBytes;
unsigned char padArray[3] = { 0 };
//写入图像像素数据
for (int y = nTop; y < nBot; y++) {
unsigned char *pSrcPtr = pBmpBits + \
y*nSrcLineBytes + nLeft * 3;
if (fwrite(pSrcPtr, nPixelBytes, 1, pFile) != 1) {
std::cout << "写入一行数据失败" << std::endl;
fclose(pFile);
return false;
}
if (nPadSize > 0) //在每行的末尾填0以保证字节对齐
fwrite(padArray, nPadSize, 1, pFile);
}
fclose(pFile);
return true;
}
//功能:保存8位灰度图像
//输入:szFileName,文件名;
//输入:pBmpBits,图像像素指针,每行像素字节数是4对齐的
//输入:nWidth,图像宽度;nHeight,图像高度
//输出:保存成功,返回true;保存失败,返回false
bool SaveGrayBmpImg(char *szFileName, unsigned char *pBmpBits,
int nWidth, int nHeight)
{
FILE *pFile = NULL;
if (fopen_s(&pFile, szFileName, "wb") != 0) {
std::cout << "打开文件失败" << std::endl;
return false;
}
BITMAPFILEHEADER bmfHeader; //文件头对象
bmfHeader.bfType = DIB_HEADER_MARKER;
bmfHeader.bfOffBits = sizeof(BITMAPFILEHEADER) +
sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD);
int nLineBytes = WIDTHBYTES(nWidth * 8); //每行字节数
bmfHeader.bfSize = bmfHeader.bfOffBits + nLineBytes*abs(nHeight);
//写入文件头
if (fwrite(&bmfHeader, sizeof(BITMAPFILEHEADER), 1,pFile)!= 1){
std::cout << "写入文件头失败" << std::endl;
fclose(pFile);
return false;
}
//BMP信息头
BITMAPINFOHEADER infoHeader;
memset(&infoHeader, 0, sizeof(BITMAPINFOHEADER));
infoHeader.biBitCount = 8;
infoHeader.biHeight = nHeight;
infoHeader.biWidth = nWidth;
infoHeader.biPlanes = 1;
infoHeader.biClrUsed = 256;
infoHeader.biClrImportant = 0;
infoHeader.biSize = sizeof(BITMAPINFOHEADER);
//写入信息头
if (fwrite(&infoHeader, sizeof(BITMAPINFOHEADER),1,pFile) != 1){
std::cout << "写入信息头失败" << std::endl;
fclose(pFile);
return false;
}
//写入调色板
RGBQUAD pal;
for (int i = 0; i < 256; i++) {
pal.rgbBlue = i;
pal.rgbGreen = i;
pal.rgbRed = i;
pal.rgbReserved = 0;
if (fwrite(&pal, sizeof(RGBQUAD), 1, pFile) != 1){
std::cout << "写入调色板失败" << std::endl;
fclose(pFile);
return false;
}
}
//写入图像像素
if (fwrite(pBmpBits, nLineBytes*abs(nHeight), 1, pFile) != 1){
std::cout << "写入像素值失败" << std::endl;
fclose(pFile);
return false;
}
fclose(pFile);
return true;
}