BitMap的读写与修改

位图(BitMap)是一种非常常见的图像类型,而且因为其失真率低,所以在机器视觉中经常会使用到它,因此需要对BitMap有更深一步的认识。

BitMap总的来说分为四个结构:位图文件头结构、位图信息头结构、颜色表和位图数据。

其中,位图文件头结构存储的是对图像文件类型的声明等规定化的内容,位图信息头结构则存储了关于位图的一些相关信息,颜色表是只在灰度位图文件中存在(彩色bmp图像中不存在颜色表),其表示位图灰度值的索引以及对应值,位图数据是存放真正图像数据的内存区。

下面通过C++实现对BitMap的读写与修改,首先包含头文件并定义一些通用的变量。

#include<Windows.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<fileapi.h>
using namespace std;

unsigned char* pBmpBuf;			//读入图像数据的指针
int bmpWidth;				//图像的宽
int bmpHeight;			//图像的高
RGBQUAD* pColorTable;				//颜色表指针
int biBitCount;			//图像类型,每像素位数

实现读取8位灰度bmp图像或彩色bmp图像的函数:

bool readBmp(string bmpName)
{
	//二进制只读模式打开bmp文件
	FILE* fp = fopen(bmpName.c_str(), "rb");
	
	if (0 == fp)
	{
		return false;
	}

	//跳过位图文件头结构BITMAPFILEHEADER
	fseek(fp, sizeof(BITMAPFILEHEADER), 0);

	//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
	BITMAPINFOHEADER head;
	fread(&head, sizeof(BITMAPINFOHEADER), 1, fp);

	//获取图像宽、高、每像素位数等信息
	bmpWidth = head.biWidth;
	bmpHeight = head.biHeight;
	biBitCount = head.biBitCount;

	//计算图像每行像素所占的字节数(由于windows规定每行数据的字节数必须是4的倍数,如果不是4的倍数,则最大相差为3,所以+3来将每行数据的字节数补至最大差值,再整除4去掉多余字节数,最后乘上4得到4的倍数字节数
	int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;

	//如果读入的bmp图像为8位灰度图像
	if (biBitCount == 8)
	{
		//创建颜色表
		pColorTable = new RGBQUAD[256];
		fread(pColorTable, sizeof(RGBQUAD), 256, fp);
	}

	//创建位图数据空间
	pBmpBuf = new unsigned char[lineByte * bmpHeight];
	fread(pBmpBuf, 1, lineByte * bmpHeight, fp);

	fclose(fp);

	return true;
}

实现保存8位灰度bmp图像或彩色bmp图像文件的函数:

bool saveBmp(string bmpName, unsigned char* imgBuf, int width, int height, int biBitCount, RGBQUAD* pColorTable)
{
	//如果位图数据指针为0,则表示无数据
	if (!imgBuf)
	{
		return false;
	}

	//定义颜色表字节大小,灰度图像颜色表为1024字节(256x3),彩色图像颜色表为0(无需颜色表)
	int colorTableSize = 0;
	if (biBitCount == 8)
	{
		colorTableSize = 1024;
	}

	//计算存储图像数据每行字节数
	int lineByte = (width * biBitCount / 8 + 3) / 4 * 4;

	//二进制只写模式打开文件
	FILE* fp = fopen(bmpName.c_str(), "wb");
	if (0 == fp)
	{
		return false;
	}

	//定义位图文件头结构变量,填写文件头信息
	BITMAPFILEHEADER fileHead;
	fileHead.bfType = 0x4D42;			//规定bmp类型

	//整个bmp图像文件的字节大小,即四个组成部分之和
	fileHead.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize + lineByte * height;

	//windows保留字,为0即可
	fileHead.bfReserved1 = 0;
	fileHead.bfReserved2 = 0;

	//bmp图像文件从起始位置到位图数据位置之间偏移的空间大小,即前三个组成部分所需空间之和
	fileHead.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTableSize;

	//将文件头写入文件
	fwrite(&fileHead, 1, sizeof(BITMAPFILEHEADER), fp);

	//创建位图信息头结构变量,填写信息头信息
	BITMAPINFOHEADER infoHead;
	infoHead.biBitCount = biBitCount;			//每像素的位数
	infoHead.biClrImportant = 0;			//重要显示的颜色数量,为0表示所有颜色都重要
	infoHead.biClrUsed = 0;			//位图实际用到的颜色数,为0表示实际用到颜色数为2的biBitCount次幂
	infoHead.biCompression = 0;				//位图压缩类型,默认为不压缩(BI_RGB==0)
	infoHead.biHeight = height;
	infoHead.biWidth = width;
	infoHead.biPlanes = 1;			//目标设备级别,必须为1
	infoHead.biSize = 40;			//位图信息头结构长度,为40个字节
	infoHead.biSizeImage = lineByte * height;			//位图数据的字节大小
	infoHead.biXPelsPerMeter = 0;			//指定目标设备的水平分辨率(像素/米)
	infoHead.biYPelsPerMeter = 0;			//指定目标设备的垂直分辨率(像素/米)

	//将位图信息头写进文件
	fwrite(&infoHead, sizeof(BITMAPINFOHEADER), 1, fp);

	//如果是8位灰度图像,则将颜色表写入文件
	if (biBitCount == 8)
	{
		fwrite(pColorTable, sizeof(RGBQUAD), 256, fp);
	}

	//将位图数据写入文件
	fwrite(imgBuf, height * lineByte, 1, fp);

	fclose(fp);

	return true;
}

至此,实现了对8位灰度bmp图像和彩色bmp图像的读写操作,接下来我们可以在main()函数中进行调用,并且将读入的bmp图像进行像素级别的操作,从而修改bmp图像中的部分像素值,并进行保存。

void main()
{
	string readPath = "C:\\Users\\iamxb\\Desktop\\Visual.CPP图像处理\\Bitmap\\1.bmp";
	readBmp(readPath);

	cout << "width:" << bmpWidth << "\theight:" << bmpHeight << "\tbiBitCount:" << biBitCount << endl;

	//遍历图像像素,将图像左下角1/4部分置为黑色
	int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
	if (biBitCount == 8)
	{
		for (int i = 0; i < bmpHeight / 2; i++)
		{
			for (int j = 0; j < bmpWidth / 2; j++)
			{
				*(pBmpBuf + i * lineByte + j) = 0;
			}
		}
	}
	else if (biBitCount == 24)
	{
		for (int i = 0; i < bmpHeight / 2; i++)
		{
			for (int j = 0; j < bmpWidth / 2; j++)
			{
				for (int k = 0; k < 3; k++)
				{
					*(pBmpBuf + i * lineByte + 3 * j + k) = 0;
				}
			}
		}
	}

	改变灰度图像的颜色表中blue分量的值
	//if (biBitCount == 8)
	//{
	//	for (int i = 0; i < 256; i++)
	//	{
	//		pColorTable[i].rgbBlue = 255 - pColorTable[i].rgbBlue;
	//	}
	//}


	string savePath = "C:\\Users\\iamxb\\Desktop\\Visual.CPP图像处理\\Bitmap\\1(copy).bmp";
	saveBmp(savePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);

	delete []pBmpBuf;
	if (biBitCount == 8)
	{
		delete []pColorTable;
	}

}

到这里就实现了对bmp图像的一些基本操作,注意这里仅仅针对于8位灰度图像和彩色图像进行操作,如果是对于其他位深度图像的操作则需要另外对程序进行修改。

注:本篇博客中的代码借鉴了《Visual C++ 数字图像处理》中的相关内容,并进行了小部分修改以及完善注释。

PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值