您的位置:首页 > 编程语言 > C语言/C++

C++ C语言 读取32位BMP图片转为8位灰度图

2018-03-24 12:51 585 查看
BMP格式详细介绍链接:
BMP格式详解

BMP格式总结:
① 前14字节描述文件类型,大小,数据偏移
② 接着的40字节描述图片的大小,宽度,高度,位深度,分辨率等等
③ 位深度常见的是1,4,8,24,32。其中1表示只有黑白两种颜色,4是16色,8是256色,24是RGB形式表示的,32是在24的基础上加上透明度的RGBA表示。
④ 目前常见的彩色图片位24位或32位,8位一般为灰度图
⑤ 如果是8位图(256色),40字节的图片描述后面会有颜色表,描述图片上256种颜色的RGBA值。灰度图的第 i 种颜色的RGB值均为 i ,A值为0。
⑥由于windows默认的扫描最小单位是4字节,所以BMP格式为了能够更快地按行读取,如果每行的字节数不是4的倍数,会在后面补0直到长度为4的倍数。可以简单地得知,32位图不需要对齐。(对齐规则)

PS:如果用fread整体读入BMP文件信息和图片信息,可能由于 内存对齐 的原因,读入的信息可能存放不到内存里预期的地方,下面的代码实现种用#pragma pack(1)解决由于内存对齐产生的问题

简单的实现代码:#include <cstdio>
#include <cstring>
#include <windows.h>
#include <iostream>
using namespace std;

//设置内存对齐
#pragma pack(1)

typedef struct tag_color_32{
BYTE Red;
BYTE Green;
BYTE Blue;
BYTE Alpha;
} color_32;

int main(int argc,char** argv){
FILE *fp,*out; // 定义一个文件指针
BITMAPFILEHEADER bmpFileHeader; // 定义一个 BMP 文件头的结构体
BITMAPINFOHEADER bmpInfo; // 定义一个 BMP 文件信息结构体
string s(argv[1]);
s = s + "tmp.bmp";
FILE *out = fopen(s.c_str(),"wb");

if((fp = fopen(argv[1], "rb")) == NULL)
{
printf("Cann't open the file!\n");
return 0;
}

fseek(fp, 0, SEEK_SET);

fread(&bmpFileHeader,14,1,fp);
fread(&bmpInfo, sizeof(BITMAPINFOHEADER), 1, fp);

// 输出BMP文件的位图文件头的所有信息
printf("位图文件头主要是对位图文件的一些描述:BMPFileHeader\n\n");
printf("文件标识符 = 0X%X\n", bmpFileHeader.bfType);
printf("BMP 文件大小 = %d 字节\n", bmpFileHeader.bfSize);
printf("保留值1 = %d \n", bmpFileHeader.bfReserved1);
printf("保留值2 = %d \n", bmpFileHeader.bfReserved2);
printf("文件头到图像数据位开始的偏移量 = %d 字节\n", bmpFileHeader.bfOffBits);

// 输出BMP文件的位图信息头的所有信息
printf("\n\n位图信息头主要是对位图图像方面信息的描述:BMPInfo\n\n");
printf("信息头的大小 = %d 字节\n", bmpInfo.biSize);
printf("位图的高度 = %d \n", bmpInfo.biHeight);
printf("位图的宽度 = %d \n", bmpInfo.biWidth);
printf("图像的位面数(位面数是调色板的数量,默认为1个调色板) = %d \n", bmpInfo.biPlanes);
printf("每个像素的位数 = %d 位\n", bmpInfo.biBitCount);
printf("压缩类型 = %d \n", bmpInfo.biCompression);
printf("图像的大小 = %d 字节\n", bmpInfo.biSizeImage);
printf("水平分辨率 = %d \n", bmpInfo.biXPelsPerMeter);
printf("垂直分辨率 = %d \n", bmpInfo.biYPelsPerMeter);
printf("使用的色彩数 = %d \n", bmpInfo.biClrUsed);
printf("重要的色彩数 = %d \n", bmpInfo.biClrImportant);

printf("\n\n\n压缩说明:有0(不压缩),1(RLE 8,8位RLE压缩),2(RLE 4,4位RLE压缩,3(Bitfields,位域存放)");

//32位色图
if(bmpInfo.biBitCount == 32){
int Height = bmpInfo.biHeight;
int Width = bmpInfo.biWidth;
bool reverse = false;
if(Height < 0){
reverse = true;
Height *= -1;
}

color_32 *pixels;
pixels = new color_32[Height*Width];
//fseek(fp,bmpFileHeader.bfOffBits,SEEK_SET);
for(int i = 0;i < Height * Width;i++)
fread(pixels+i,4,1,fp);

//设置文件头
bmpInfo.biBitCount = 8;
bmpInfo.biClrUsed = 256;
//灰度图颜色表
RGBQUAD pRGB[256];
for(int i = 0;i < 256;i++){
pRGB[i].rgbRed = i;
pRGB[i].rgbGreen = i;
pRGB[i].rgbBlue = i;
pRGB[i].rgbReserved = 0;
}
bmpFileHeader.bfOffBits = 54 + 4 * 256;
//考虑对齐规则 计算每一行的字节数
int LineByte = ((bmpInfo.biBitCount / 8 * Width + 3) / 4) * 4;
bmpFileHeader.bfSize = 54 + 4 * 256 + Height * LineByte;

//写入新的文件
fseek(out,0,SEEK_SET);
fwrite(&bmpFileHeader,14,1,out);
fwrite(&bmpInfo,40,1,out);
fwrite(&pRGB,4*256,1,out);

for(int i = 0;i < Height;i++){
for(int j = 0;j < Width;j++){
float pr = pixels[i*Width + j].Red;
float pg = pixels[i*Width + j].Green;
float pb = pixels[i*Width + j].Blue;
//提取灰度低的黑色部分
BYTE data = pr * 0.299 + pg * 0.587 + pb * 0.114;
if((pr - pg) > 5 || (pr - pb) > 5) data = 255;
data = data < 25 ? 0 : 255;
fwrite(&data,1,1,out);
}
for(int j = Width;j < LineByte;j++){
BYTE data = 0;
fwrite(&data,1,1,out);
}
}
}
fclose(out);
fclose(fp);

while(1);

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: