如何使用QQ::Mat读写BMP图像

由于BMP格式的图像简单,读写BMP图的实现也相对容易,所以在构建自己的图像处理库的时候,都是使用BMP作为测试图像。首先说一BMP图像相关的知识。


#BMP图像的组成
BMP图像由4个部分组成:

  1. 文件头:BITMAPFILEHEADER(14字节)
  2. 信息头:BITMAPINFOHEADER(40字节)
  3. 颜色表:RGBQUAD(8位的为1024字节)(小于24位的图有,24位没有)
  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

非常感谢您的阅读,如果您觉得这篇文章对您有帮助,欢迎扫码进行赞赏。
如何使用QQ::Mat读写BMP图像_第1张图片

你可能感兴趣的:(图像处理)