zoukankan      html  css  js  c++  java
  • BMP图像的灰度化---C++实现

    灰度图的结构主要包括文件头,BMP信息头,调色板,BMP数据内容四部分。灰度图的调色板共有256项RGBQUAD结构,存放0到255的灰度值,每一项rgbRed、rgbGreen、rgbBlue分量值相等。

    参考文章:BMP图像的结构及读写和灰度化
    24位真彩BMP图像的灰度化
          把24位真彩BMP图像转变成256阶灰度图的具体步骤如下:
    (1) 修改信息头
           信息头共有11部分,灰度化时需要修改两部分
    bi2.biBitCount=8;
    bi2.biSizeImage=( (bi.biWidth+3)/4 ) * 4*bi.biHeight;

    (2)修改文件头
           文件头共有5部分,灰度化时需要修改两部分
              bf2.bfOffBits = sizeof(bf2)+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);
    bf2.bfSize = bf2.bfOffBits + bi2.biSizeImage;


    (3)创建调色板
    RGBQUAD *ipRGB2 = (RGBQUAD *)malloc(256*sizeof(RGBQUAD));
    for ( i = 0; i < 256; i++ )
    ipRGB2[i].rgbRed = ipRGB2[i].rgbGreen = ipRGB2[i].rgbBlue = i;


    (4)修改位图数据部分
             这部分主要是由原真彩图的rgbRed、rgbGreen、rgbBlue分量值得到灰度图像的灰度值Y,

    可以用下面公式得到:
                Y=0.299*rgbRed+0.587* rgbGreen+0.114*rgbBlue;
    具体修改代码如下:
    int nBytesPerLine2 = ( (bi.biWidth+3)/4 ) * 4;
    nLineStart2 = nBytesPerLine2 * i;
    for ( int j = 0; j<nBytesPerLine2;j++ )
    ImgData2[nLineStart2+j]= int( (float)Imgdata[i][3 * j] * 0.114 +
    (float)Imgdata[i][3 * j + 1] * 0.587 +
    (float)Imgdata[i][3 * j + 2] * 0.299 );//用一个一维数组顺序存储灰度值


    (5)按顺序写入BMP图像的各个部分
              fwrite(&bf2,sizeof(BITMAPFILEHEADER),1,fp); 
    fwrite(&bi2,sizeof(BITMAPINFOHEADER),1,fp);
    fwrite(ipRGB2,sizeof(RGBQUAD),256,fp);
    fwrite(ImgData2,nImageSize2,1,fp);

    #include<iostream>
    #include <Windows.h>
    
    using namespace std;
    
    
    void main()
    {
    	
    	FILE* stream=fopen("D:\3.bmp","rb");
    	if(stream==NULL)
    	{
    		cout<<"文件不存在"<<endl;
    		return;
    	}
    	
    	int sizeFileHeader=sizeof(BITMAPFILEHEADER);
    	int sizeInfoHeader=sizeof(BITMAPINFOHEADER);
    	
    	BITMAPFILEHEADER* bitmapFileHeader=new BITMAPFILEHEADER[sizeFileHeader+1];
    	
    	BITMAPINFOHEADER* bitmapInfoHeader=new BITMAPINFOHEADER[sizeInfoHeader+1];
    	
    	memset(bitmapFileHeader,0,sizeFileHeader+1);
    	memset(bitmapInfoHeader,0,sizeInfoHeader+1);
    	fread(bitmapFileHeader,sizeof(char),sizeFileHeader,stream);
    	fseek(stream,sizeFileHeader,0);
    	fread(bitmapInfoHeader,sizeof(char),sizeInfoHeader,stream);
    	int srcImageLineByteCount=(((bitmapInfoHeader->biWidth*24)+31)/32)*4;
    	int destImageLineByteCount=(((bitmapInfoHeader->biWidth)*8+31)/32)*4;
    
    	//************位图信息头**********************
    	
    	BYTE** oldImageData=new BYTE*[bitmapInfoHeader->biHeight];
    	for(int i=0;i<bitmapInfoHeader->biHeight;i++)
    	{
    		oldImageData[i]=new BYTE[srcImageLineByteCount+1];
    		memset(oldImageData[i],0,srcImageLineByteCount+1);
    	}
    
    	//***********位图数据***********************
    	fseek(stream,sizeFileHeader+sizeInfoHeader,0);
    	//读取图像数据
    	for(int i=0;i<bitmapInfoHeader->biHeight;i++)
    	{
    		for (int j=0;j<srcImageLineByteCount;j++)
    		{
    			fread(&oldImageData[i][j],sizeof(BYTE),1,stream);
    
    		}
    		
    	}
    
    	fclose(stream);
    	
    	//调色板
    	RGBQUAD* pRgbQuards=new RGBQUAD[256];
    	for(int i=0;i<256;i++)
    	{
    		pRgbQuards[i].rgbBlue=i;
    		pRgbQuards[i].rgbRed=i;
    		pRgbQuards[i].rgbGreen=i;
    
    	}
    	
    	//修改信息头
    	bitmapInfoHeader->biBitCount=8;
    	bitmapInfoHeader->biSizeImage=(bitmapInfoHeader->biHeight)*destImageLineByteCount;
    
    	//修改文件头
    	bitmapFileHeader->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256;
    	bitmapFileHeader->bfSize=bitmapFileHeader->bfOffBits+bitmapInfoHeader->biSizeImage;
    	
    
    	//写数据
    	
    	BYTE** newImageData=new BYTE*[bitmapInfoHeader->biHeight];
    
    	for (int i=0;i<bitmapInfoHeader->biHeight;i++)
    	{
    		newImageData[i]=new BYTE[destImageLineByteCount];
    	}
    
    	for(int i=0;i<bitmapInfoHeader->biHeight;i++)
    	{
    		for(int j=0;j<destImageLineByteCount;j++)
    		{
    			newImageData[i][j]=(int)((float)oldImageData[i][j*3]*0.114+
    				(float)oldImageData[i][j*3+1]*0.587+(float)oldImageData[i][3*j+2]*0.299);
    		}
    	}
    
    
    	//写入文件
    	FILE* fileWrite=fopen("D:\6.bmp","a+");
    	fwrite(bitmapFileHeader,sizeof(char),sizeof(BITMAPFILEHEADER),fileWrite);
    	fwrite(bitmapInfoHeader,sizeof(char),sizeof(BITMAPINFOHEADER),fileWrite);
    	fwrite(pRgbQuards,sizeof(RGBQUAD),256,fileWrite);
    	
    	for(int i=0;i<bitmapInfoHeader->biHeight;i++)
    	{
    		for(int j=0;j<destImageLineByteCount;j++)
    		{
    			fwrite(&newImageData[i][j],sizeof(BYTE),1,fileWrite);
    		}
    
    	}
    	fclose(fileWrite);
    
    	cout<<"success"<<endl;
    }

     int srcImageLineByteCount=(((bitmapInfoHeader->biWidth*24)+31)/32)*4;
     int destImageLineByteCount=(((bitmapInfoHeader->biWidth)*8+31)/32)*4;

    提醒:这里没有进行指针的释放。。。

    这两行其实也可以用上一篇文章的WIDTHBYTES(bitmapInfoHeader->biWidth*24)和WIDTHBYTES(bitmapInfoHeader->biWidth*8)

    有些地方也用( (bi.biWidth+3)/4 ) * 4和((bi.biWidth*3+3)/4)*4这样的表达式。。原理都是一样的。其实( (bi.biWidth+3)/4 ) * 4写成( (bi.biWidth*1+3)/4 ) * 4估计会好理解吧。。

    因为BMP图像每个像素都是有三个RGB分量组成(24位,32位也就是多了个Alpha),而在灰度图像中每个像素只是一个灰度值,从代码:newImageData[i][j]=(int)((float)oldImageData[i][j*3]*0.114+(float)oldImageData[i][j*3+1]*0.587+(float)oldImageData[i][3*j+2]*0.299); 可看出,每个灰度值都是由原来的彩色图像的每个RGB分量通过一定的公式计算得来的。因此灰度图像和原来的彩色图像虽然在宽度和高度(像素单位)是一样的,但是因为组成不同,所以每行的字节数就是不一样的。。

    至于其他的就不多说了,在前面WIDTHBYTES位图操作函数详解BMP文件读写复习---C++实现文章中都说的差不多了,如果还有不明白的可以留言。。

    效果如下:

    原图像:

    灰度图像:

  • 相关阅读:
    Scikit-learn学习记录【持续更新】——Scikit-learn框架的入门介绍
    【步骤超详细】mac系统 通过anaconda配置Pycharm的scikit-learn环境
    与高哥聊天有感
    蓝杰学长学姐交流记录
    Robcup2D足球学习记录【2020.01.06】
    分治算法
    多所学校就业报告分析总结
    想要半年之内拿到30万大厂的offer?看这里!!!
    C语言变量的存储类别
    C语言函数入门
  • 原文地址:https://www.cnblogs.com/bbsno1/p/3253647.html
Copyright © 2011-2022 走看看