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.299rgbRed+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->biWidth24)+31)/32)4;

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


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

这两行其实也可以用上一篇文章的WIDTHBYTES(bitmapInfoHeader->biWidth24)和WIDTHBYTES(bitmapInfoHeader->biWidth8)

有些地方也用( (bi.biWidth+3)/4 ) * 4和((bi.biWidth3+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][j3]0.114+(float)oldImageData[i][j3+1]0.587+(float)oldImageData[i][3j+2]0.299); 可看出,每个灰度值都是由原来的彩色图像的每个RGB分量通过一定的公式计算得来的。因此灰度图像和原来的彩色图像虽然在宽度和高度(像素单位)是一样的,但是因为组成不同,所以每行的字节数就是不一样的。。

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

效果如下:

原图像:

BMP图像的灰度化---C++实现

灰度图像:

BMP图像的灰度化---C++实现

原文链接: https://www.cnblogs.com/bbsno1/p/3253647.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/99599

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月10日 上午5:17
下一篇 2023年2月10日 上午5:19

相关推荐