练习目的
BMP格式图片灰度化
位图知识点
BMP图像存储结构
| 块名称 | Windows结构体定义 | 大小(BYTE) |
|---|---|---|
| 文件信息头 | BITMAPFILEHEADER | 14 |
| 位图信息头 | BITMAPINFOHEADER | 40 |
| 调色板 | RGBQUAD | 单色、16色、256色图像文件特有,相对应的调色板大小是2,16,256。 |
| RGB像素值阵列 | BYTE* | 由图像长宽尺寸决定,每一行字节数必须是4的整数倍 |
位图文件头
提供文件的格式,大小等信息
typedef struct tagBITMAPFILEHEADER {
UINT16 bfType; //2BYTES,必须为BM,即0x424D,文件格式,windows位图文件 DWORD bfSize; //4BYTES,整个BMP文件的大小 UINT16 bfReverved1; //2BYTES,保留,值为0 UINT16 bfReverved2; //2BYTES,保留,值为0 DWORD bfOffBits; //4BYTES,文件起始位置到图像像素数据的字节偏移量 }BITMAPFILEHEADER;
位图信息头
定义如下:
typedef struct _tagBMP_INFOHEADER {
DWORD biSize; //4BYTES,INFOHEADER结构体大小 LONG biWidth; //4BYTES,图像宽度(以像素为单位) LONG biHeight; //4BYTES,图像高度。值为正,图像存储顺序从下到上,从左到右;值为负,正好相反。 WORD biPlanes; //2BYTES,图像数据平面,BMP存储RGB数据,因此总为1 WORD biBitCount; //2BYTES,图像像素位数 DWORD biCompression; //4BYTES,0:不压缩;1:RLE8;2:RLE4 DWORD biSizeImage; //4BYTES,4字节对齐的图像数据大小 LONG biXPelsPerMeter; //4BYTES,用像素/米表示的水平分辨率 LONG biYPelsPerMeter; //4BYTES,用像素/米表示的垂直分 DWORD biClrUsed; //4BYTES,实际使用的调色板索引数,0:使用所有的调色板索引 DWORD biClrImportant; //4BYTES,重要的调色板索引数,0:所有的索引都重要 }BMP_INFOHEADER;
调色板
结构体定义如下:
typedef struct _tagRGBQUAD {
BYTE rgbBlue; //指定蓝色强度 BYTE rgbGreen; //指定绿色强度 BYTE rgbRed; //指定红色强度 BYTE rgbReserved; //保留,设0 }RGBQUAD;
颜色表中RGBQUAD结构数据的个数有biBitCount来确定,当biBitCount=1,4,8时,分别有2,16,256个表项。
当biBitCount=1时,为2色图像,BMP位图中有2个数据结构RGBQUAD,一个调色板占用4字节数据,所以2色图像的调色板长度为2*4为8字节。
当biBitCount=4时,为16色图像,BMP位图中有16个数据结构RGBQUAD,一个调色板占用4字节数据,所以16像的调色板长度为16*4为64字节。
当biBitCount=8时,为256色图像,BMP位图中有256个数据结构RGBQUAD,一个调色板占用4字节数据,所以256色图像的调色板长度为256*4为1024字节。
当biBitCount=16,24或32时,没有颜色表。
位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充,
一个扫描行所占的字节数计算方法:
DataSizePerLine= (biWidth* biBitCount+31)/8;
// 一个扫描行所占的字节数
DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数
位图数据的大小(不压缩情况下):
DataSize= DataSizePerLine* biHeight;
转灰度图重点
这里参考了文章:http://blog.sina.com.cn/s/blog_677a04530101244t.html
代码例程
#include
#include
using namespace std; void main() {
FILE* originImg = fopen("E:\\Program test\\imgFiltering\\lena-24.bmp","rb"); if(originImg == NULL) {
cout<<"图片不存在"<<endl; system("pause"); 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); //内存初始化,从bitmapFileHeader开始后面sizeFileHeader+1全置0 memset(bitmapInfoHeader, 0, sizeInfoHeader+1); fread(bitmapFileHeader, sizeof(char), sizeFileHeader, originImg); //从originImg中读取sizeFileHeader个char大小的数据到数组bitmapFileHeader中 fseek(originImg, sizeFileHeader, 0); //指针originImg的位置指向以文件头(0指文件头)偏移sizeFileHeader大小的位置,即指向信息头首位 fread(bitmapInfoHeader, sizeof(char), sizeInfoHeader, originImg); //读取原图的信息头到数组bitmapInfoHeader中 int srcImageLineByteCount = (((bitmapInfoHeader->biWidth) * 24 + 31) / 32) * 4; //计算原始24位图每行像素所占的字节数 int grayImageLineByteCount = (((bitmapInfoHeader->biWidth) * 8 + 31) / 32) * 4; //计算8位灰度图每行像素所占的字节数 //位图信息头 //创建了一个高位biHeight,宽为srcImageLineByteCount的二维数组,并对数组初始化 BYTE** origImgData = new BYTE*[bitmapInfoHeader->biHeight]; for(int i = 0; i < bitmapInfoHeader->biHeight; i++) {
origImgData[i] = new BYTE[srcImageLineByteCount + 1]; //每行是一个大小为srcImageLineByteCount的数组,行高i< memset(origImgData[i], 0, srcImageLineByteCount + 1); //从origImgData[i]开始,后面srcImageLinneByteCount+1全置0 } //位图数据 fseek(originImg, sizeFileHeader + sizeInfoHeader, 0); //定位到以文件头偏移sizeFileHeader + sizeInfoHeader大小的位置,即位图数据开始的位置 //读取位图数据 for(int i = 0; i < bitmapInfoHeader->biHeight; i++) {
for (int j = 0; j < srcImageLineByteCount; j++) //字节数 {
fread(&origImgData[i][j], sizeof(BYTE), 1, originImg); //读取原始图像位图数据,以字节为单位读到二维数组origImgData中 } } fclose(originImg); //调色板 RGBQUAD* pRgbQuards = new RGBQUAD[256]; for(int i = 0; i < 256; i++) {
pRgbQuards[i].rgbBlue = i; pRgbQuards[i].rgbRed = i; pRgbQuards[i].rgbGreen = i; } //修改信息头 2部分 bitmapInfoHeader->biBitCount = 8; bitmapInfoHeader->biSizeImage = (bitmapInfoHeader->biHeight) * grayImageLineByteCount; //4字节对齐的图像数据大小 宽*高 //修改文件头 2部分 //8为灰度图,有256个数据结构RGBQUAD,一个调色板占用4字节数据,所以256色图像的调色板长度为256*4为1024字节 bitmapFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 256; //偏移量要加上调色板的大小 bitmapFileHeader->bfSize = bitmapFileHeader->bfOffBits + bitmapInfoHeader->biSizeImage; //整个文件的大小变为 信息头+文件头+调色板+数据,前三项为偏移量 //写数据 BYTE** grayImgData = new BYTE*[bitmapInfoHeader->biHeight]; for (int i = 0; i < bitmapInfoHeader->biHeight; i++) {
grayImgData[i] = new BYTE[grayImageLineByteCount]; } //灰度图位图数据的二维数组 for(int i = 0; i < bitmapInfoHeader->biHeight; i++) {
for(int j = 0; j < grayImageLineByteCount; j++) //灰度图每行像素所占的字节数 {
grayImgData[i][j] = (int)((float)origImgData[i][j*3] * 0.114 + (float)origImgData[i][j*3 + 1] * 0.587 + (float)origImgData[i][3*j+2] * 0.299); } } //写入文件 FILE* grayImg=fopen("E:\\Program test\\imgFiltering\\lena-gray.bmp","wb"); fwrite(bitmapFileHeader, sizeof(char), sizeof(BITMAPFILEHEADER), grayImg); //写文件头 fwrite(bitmapInfoHeader, sizeof(char), sizeof(BITMAPINFOHEADER), grayImg); //写信息头 fwrite(pRgbQuards, sizeof(RGBQUAD), 256, grayImg); // 写调色板 for(int i = 0; i < bitmapInfoHeader->biHeight; i++) {
for(int j = 0; j < grayImageLineByteCount; j++) {
fwrite(&grayImgData[i][j], sizeof(BYTE), 1, grayImg); //写位图数据 } } fclose(grayImg); cout<<"success"<<endl; cin.get(); //释放内存 for(int i = 0; i < bitmapInfoHeader->biHeight; i++) for (int j = 0; j < grayImageLineByteCount; j++) delete []grayImgData[j]; delete []grayImgData; for(int i = 0; i < bitmapInfoHeader->biHeight; i++) for (int j = 0; j < srcImageLineByteCount; j++) delete []origImgData[j]; delete []origImgData; delete []bitmapFileHeader; delete []bitmapInfoHeader; system("pause"); }
发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/220103.html原文链接:https://javaforall.net
