从零开始写一个图像处理程序之一(BMP彩色图转灰度图)

图像二值化可以直接调用opencv的二值化函数去完成处理,但是不利用OpenCV从头手写一个处理图片程序未尝不是一件有意思的事情,就拿BMP图片为例去做一个

BMP图像:

BMP(Bitmap)图像是Windows操作系统的标准文件格式,图像是按从左到右、从下到上的顺序扫描和存储的

灰度图:

灰度图(Gray Scale Image or Grey Scale Image)又称灰阶图。把白色与黑色之间按对数关系分为若干等级级,称为灰度。灰度分为256阶。

BMP图像的格式:

BMP格式的文件从头到尾依次是如下信息:

BMP文件头(bmp file header):共14byte

BMP文件位图信息头(bit information):共40byte

调色版(color palette):可选,由颜色索引决定

位图数据:由图像尺寸决定

BMP文件头: 

在其中:

 bfSize和bfOffBits可以利用windows的库函数读出

	bfhead.bfSize=14+40+sizeof(RGBQUAD)*256+NewLineByte*bihead.biHeight;
	bfhead.bfOffBits=14+40+sizeof(RGBQUAD)*256;

BMP文件位图信息头:

特别注意BMP中的结构对齐:

数据按照像素进行包装,便于读取,但这并不是全部,其中会涉及补零操作(zero-padding)

BMP的“数据4字节对齐”以及像素定位算法的个人探究_qyze的博客-CSDN博客

读取图像的信息: 

将文件指定区域的

	BITMAPFILEHEADER bfhead;
 	BITMAPINFOHEADER bihead;
	FILE *fp=fopen("cat.bmp","rb");//打开文件流
	if(0==fp)
	{
		printf("OpenFilefail!");
		return 0;
	}
	//分别为要存入的存储区域,每个数据块的字节数,读取的块数,文件指针
	fread(&bfhead,14,1,fp);
	fread(&bihead,40,1,fp);
	printf("Width:%d, Height: %d,biBitCount:%d\n",bihead.biWidth,bihead.biHeight,bihead.biBitCount);

将颜色索引变为灰度索引:

	RGBQUAD *pColorTable=new RGBQUAD[256];
	for(int i=0;i<256;i++)
	{
		pColorTable[i].rgbRed = pColorTable[i].rgbGreen = pColorTable[i].rgbBlue = i;//使颜色表中每种颜色的R,G,B分量相等且等于索引值
	}

计算出像素的灰度值:

	unsigned char *pNewBmpBuf=new unsigned char[NewLineByte*bihead.biHeight];		
	for(int i=0;i<bihead.biHeight;i++)
		for(int j=0;j<bihead.biWidth;j++)
		{
			char *pb1,*pb2;
			pb1=pBmpBuf+i*LineByte+j*3;
			int y=*(pb1)*0.299+*(pb1+1)*0.587+*(pb1+2)*0.114;
			pb2=pNewBmpBuf+i*NewLineByte+j;
			*pb2=y;
		}

全部代码:

#include<stdio.h>
#include<windows.h>

int main()
{
	BITMAPFILEHEADER bfhead;
 	BITMAPINFOHEADER bihead;
	FILE *fp=fopen("cat.bmp","rb");//打开文件流
	if(0==fp)
	{
		printf("OpenFilefail!");
		return 0;
	}
	//分别为要存入的存储区域,每个数据块的字节数,读取的块数,文件指针
	fread(&bfhead,14,1,fp);
	fread(&bihead,40,1,fp);
	printf("Width:%d, Height: %d,biBitCount:%d\n",bihead.biWidth,bihead.biHeight,bihead.biBitCount);
	
	int LineByte=(bihead.biWidth*24/8+3)/4*4;
	//分配内存去存储图像数据
	unsigned char *pBmpBuf=new unsigned char[LineByte*bihead.biHeight];
	fread(pBmpBuf,LineByte*bihead.biHeight,1,fp);       //将bmp数据区读入内存
	fclose(fp);//关闭文件流
	//输出图像的长宽和高
	FILE *newfp=fopen("newcat.bmp","wb");
	if(0==newfp)
	{
		printf("CreatFilefail!");
		return 0;
	}
	
	int NewLineByte=(bihead.biWidth*8/8+3)/4*4;  //由于灰度化后每像素位数变为8,所以每行字节数发生改变,但仍要求为4的整数倍
	
	//图片的大小
	bfhead.bfSize=14+40+sizeof(RGBQUAD)*256+NewLineByte*bihead.biHeight;
	bfhead.bfOffBits=14+40+sizeof(RGBQUAD)*256;
	//将bmpfilehead写入
	fwrite(&bfhead,14,1,newfp);
	//	将bmpbithead写入
	bihead.biBitCount=8;    //更改每像素位数
	bihead.biSizeImage=NewLineByte*bihead.biHeight;
	fwrite(&bihead,40,1,newfp);
	//灰度为1的rgb值分别为1,1,1,所以将256位颜色的颜色表分别置为相同的灰度值
	RGBQUAD *pColorTable=new RGBQUAD[256];
	for(int i=0;i<256;i++)
	{
		pColorTable[i].rgbRed = pColorTable[i].rgbGreen = pColorTable[i].rgbBlue = i;//使颜色表中每种颜色的R,G,B分量相等且等于索引值
	}
	fwrite(pColorTable,sizeof(RGBQUAD),256,newfp);

	
	unsigned char *pNewBmpBuf=new unsigned char[NewLineByte*bihead.biHeight];		
	for(int i=0;i<bihead.biHeight;i++)
		for(int j=0;j<bihead.biWidth;j++)
		{
			unsigned char *pb1,*pb2;
			pb1=pBmpBuf+i*LineByte+j*3;
			int y=*(pb1)*0.299+*(pb1+1)*0.587+*(pb1+2)*0.114;
			pb2=pNewBmpBuf+i*NewLineByte+j;
			*pb2=y;
		}

 fwrite(pNewBmpBuf,NewLineByte*bihead.biHeight,1,newfp);
 fclose(newfp);
 

 return 0;
}

cat.bmp:

newcat.bmp:

  • 2
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值