由于BMP格式的图像简单,读写BMP图的实现也相对容易,所以在构建自己的图像处理库的时候,都是使用BMP作为测试图像。首先说一BMP图像相关的知识。
#BMP图像的组成
BMP图像由4个部分组成:
##文件头 BITMAPFILEHEADER
typedef struct tagBITMAPFILEHEADER
{
WORD bfType; //位图文件的类型,该值必需是0x4D42,也就是字符'BM'。
DWORD bfSize; //位图文件大小:文件头+信息头+颜色表+数据部分
WORD bfReserved1; //保留字,为0,留做扩展,对实际的解码格式没有影响。
WORD bfReserved2; //同上
DWORD bfOffBits; //位图文件头到图像数据的偏移量,以字节为单位
} BITMAPFILEHEADER;
这个结构的长度是固定的,为14个字节(WORD为无符号16位整数,DWORD为无符号32位整数)。该结果体作为文件存储的时候用。
结构体的定义参考Windows.h
##信息头BITMAPINFOHEADER
这部分告诉应用程序图像的详细信息,在屏幕上显示图像将会使用这些信息,它从文件的第15个字节开始。
typedef struct tagBITMAPINFOHEADER
{
DWORD biSize; //BITMAPINFOHEADER结构大小,字节为单位
LONG biWidth; //宽度(以像素为单位,不是每行字节数!!)
LONG biHeight; //高度(以像素为单位)
WORD biPlanes; //目标设备的级别,必须为1
WORD biBitCount; //颜色深度,每个像素所需要的位数
DWORD biCompression; //位图的压缩类型
DWORD biSizeImage; //位图数据部分的大小,以字节为单位(4字节对齐)
LONG biXPelsPerMeter; //位图水平分辨率,每米像素数
LONG biYPelsPerMeter; //位图垂直分辨率,每米像素数
DWORD biClrUsed; //位图实际使用的颜色表中的颜色数
DWORD biClrImportant; //位图显示过程中重要的颜色数
} BITMAPINFOHEADER;
该结构体大小为40字节。
##颜色表RGBQUAD
BMP只能存储单色(1位),16色(4位),256色(8位),和真彩色(24位)4种格式的数据,只有真彩色没有调色板(颜色表)。
8位的BMP中,每个像素的数值就是颜色表数组的下标。而24位的BMP图,每个像素的数值就是表示实际的像素值。
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
##图像数据
在8位BMP中,每个像素8位,存放的是颜色表的索引,24位每个像素24位,存放的是实际颜色的值,分为三个通道BGR。
要注意两点:
(1) 每一行的字节数必须是4的整倍数,如果不是,则需要补齐。也就是4字节对齐。
(2) bmp文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个像素,然后是左边第二个像素……接下来是倒数第二行左边第一个像素,左边第二个像素……依次类推 ,最后得到的是最上面一行的最右一个像素。
下面给出实现
#使用QQ::Mat读写图像
##Bmp.h
//////////////////////////////////////////////////////////////////////////
// 读写BMP图 Bmp.h
// 2014-11-12,by QQ
//
// Please contact me if you find any bugs, or have any suggestions.
// Contact:
// Telephone:15366105857
// Email:[email protected]
// Blog: http://blog.csdn.net/qianqing13579
//////////////////////////////////////////////////////////////////////////
#ifndef __BMP_H__
#define __BMP_H__
#include "Mat.h"
#include "CommonDefinition.h"
#include"BmpDefinition_Windows.h" // 在Linux中使用 BmpDefinition_Linux.h
#include
using namespace std;
namespace QQ
{
// C++接口(支持8位单通道和3通道)
DLL_EXPORTS void ReadBmp(const string &fileName,Mat &image);// 读BMP
DLL_EXPORTS void WriteBmp(const string &fileName, const Mat &image);// 写BMP
DLL_EXPORTS void WriteMarkedBMP(const string &fileName, const Mat& image);// 标记BMP图
// C接口
DLL_EXPORTS uchar *ReadBmp_C(const char * fileName, int &width, int &height, int &bitCount_PerPixel);
DLL_EXPORTS bool WriteBmp_C(uchar *image, int width, int height, const char * fileName, int bitCount_PerPixel);
DLL_EXPORTS bool WriteMarkedBmp_C(uchar *image, int width, int height, const char * filename);
}//namespace QQ
#endif
##Bmp.cpp
#define DLLAPI_EXPORTS
#include "Bmp.h"
namespace QQ
{
void ReadBmp(const string &fileName, Mat &image)
{
//图像参数
int width,height,bitCount_PerPixel;
//读BMP
uchar *data=ReadBmp_C(fileName.c_str(),width,height,bitCount_PerPixel);
//设置图像头
int numberOfChannels = bitCount_PerPixel >> 3;
Mat temp(height, width, numberOfChannels, data, true);
image = temp;
delete[] data;
}
void WriteBmp(const string &fileName, const Mat &image)
{
//写入bmp
int bitCount_PerPixel=image.numberOfChannels<<3;
WriteBmp_C(image.data,image.cols,image.rows,fileName.c_str(),bitCount_PerPixel);
}
void WriteMarkedBMP(const string &fileName, const Mat &image)
{
WriteMarkedBmp_C(image.data, image.cols, image.rows, fileName.c_str());
}
///读取8位或者24位Bmp
///返回原始图像数据,未对齐的数据
uchar *ReadBmp_C(const char * fileName,
int &width, int &height,//图像大小(像素的宽度和高度)
int &bitCount_PerPixel//返回图像的每像素位数
)
{
FILE *fp;
BITMAPFILEHEADER bitmap_FileHeader;
BITMAPINFOHEADER bitmap_InfoHeader;
RGBQUAD *colorTable;
bool isSuccess = true;
width = height = 0;
if ((fp = fopen(fileName, "rb")) == NULL)
return NULL;
// 读入文件头和信息头内的信息
if (fread((void *)&bitmap_FileHeader, 1, sizeof(BITMAPFILEHEADER), fp) != sizeof(BITMAPFILEHEADER))
isSuccess = false;
if (fread((void *)&bitmap_InfoHeader, 1, sizeof(BITMAPINFOHEADER), fp) != sizeof(BITMAPINFOHEADER))
isSuccess = false;
if ((isSuccess == false) || (bitmap_FileHeader.bfOffBits= imageData; temp -= ucharCount_PerPixel * width)
{
writeCount = fwrite((void *)temp, 1, width * ucharCount_PerPixel, fp);
if (writeCount != (unsigned int)(ucharCount_PerPixel * width))
isSuccess = false; // 真实的数据
}
}
else
{
for (temp = imageData + (height - 1) * ucharCount_PerPixel * width; temp >= imageData; temp -= ucharCount_PerPixel * width)
{
writeCount = fwrite((void *)temp, 1, width * ucharCount_PerPixel, fp);
if (writeCount != (unsigned int)(ucharCount_PerPixel * width))
isSuccess = false; // 真实的数据
for (i = 0; i= image; temp -= ucharCount_PerPixel * width)
{
if (fwrite((void *)temp, 1, width * ucharCount_PerPixel, fp) != (unsigned int)(ucharCount_PerPixel * width))
isSuccess = false; // 真实的数据
}
}
else
{
for (temp = image + (height - 1) * ucharCount_PerPixel * width; temp >= image; temp -= ucharCount_PerPixel * width)
{
if (fwrite((void *)temp, 1, width * ucharCount_PerPixel, fp) != (unsigned int)(ucharCount_PerPixel * width))
isSuccess = false; // 真实的数据
for (i = 0; i < extend; i++) // 扩充的数据,数据内容不限
{
if (fwrite((void *)(temp + ucharCount_PerPixel* (width - 1)), 1, 1, fp) != 1)
isSuccess = false;
}
}
}
// Return;
fclose(fp);
return isSuccess;
}
}// namespace QQ
##BmpDefinition_Windows.h
//////////////////////////////////////////////////////////////////////////
// Windows下Bmp图定义 Bmp_Definition_Windows.h
// 2014-11-13,by QQ
//
// Please contact me if you find any bugs, or have any suggestions.ww
// Contact:
// Telephone:15366105857
// Email:[email protected]
// Blog: http://blog.csdn.net/qianqing13579
//////////////////////////////////////////////////////////////////////////
#ifndef __BMP_DEFINITION_WINDOWS_H__
#define __BMP_DEFINITION_WINDOWS_H__
//
//
/*************************Windows BMP 类型定义******************************/
typedef unsigned long DWORD;
typedef int BOOL;
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef long LONG;
#pragma pack(1)//禁止VC字节对齐
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
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;
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
#pragma pack()
//
//
//
#endif
##测试程序
void TestReadAndWrite()
{
//读取
Mat srcImage;//建立一个空图像
ReadBmp("D:/Test.bmp", srcImage);
//// 将第100行设置为0
for (int i = 0; i <= srcImage.cols - 1; ++i)
{
srcImage.At(100, i) = Vec3b(0,0,0);
}
//保存
WriteBmp("D:/1.bmp", srcImage);
}
完整的工程:https://github.com/qianqing13579/QQImageProcess
2017-1-10 18:35:33