最近在做嵌入式视觉工作,故不可以用opencv库,只好自己写,做视觉第一点便是读取图片,于是学习了bmp(byte map picture)的读写,发现这是一个小坑,首先不知道怎么得到bmp图片,这里推荐网站:转换网站
bmp图片有32,24,16,8,1,位bmp文件之分,一般由header字段(54字节),调色板字段(1位bmp有2*4字节,8位bmp有256*4字节,16位bmp有2^16*4字节,24位,36位无调色板),数据字段(不定长字节,由不同位数与图片大小决定)。
#pragma pack(1)
typedef struct //这个结构体就是对上面那个图做一个封装。
{
//bmp header
u8 Signatue[2] ; //标识帧,默认0x42, 0x4d
u32 FileSize ; //文件大小(字节),重要的值
u16 Reserv1 ;//保留值1,默认0x00
u16 Reserv2 ;//保留值2,默认0x00
u32 FileOffset ; //文件头偏移量,重要的值
//DIB header
u32 DIBHeaderSize ; //DIB头大小,默认0x36
u32 ImageWidth ; //图片宽度,重要的值
u32 ImageHight ; //图片高度,重要的值
u16 Planes ;图像的位面数(位面数是调色板的数量)
u16 BPP ; //每个相素点的位数,一般取1,8,16,24,36,重要的值
u32 Compression ;压缩类型
u32 ImageSize ; //图文件大小
u32 XPPM ;//水平分辨率
u32 YPPM ;//垂直分辨率
u32 CCT ;//使用的色彩数
u32 ICC ;//重要的色彩数
}bmp_header;#pragma pack()
header含有一共含有54个字节,包含了这个bmp文件的描述信息,其中的计算如下:
FileSize=BBP/8*ImageHight *ImageWidth (数据段)+FileOffset(header字段+调色板字段)
#pragma pack (n) 作用:C编译器将按照n个字节对齐。
#pragma pack () 作用:取消自定义字节对齐方式。
读取文件时一般先读取header字段,再一起读取header字段+调色板字段,再根据上述计算式分离出数据段。
我们知道真彩至少需要8位RGB表示,那么一个像素至少需要24位,可以表示256*256*256种颜色。当图片为1,8,24位时,分别可以表示2,256,256*256种颜色。
我们以8位为例:
我们需要确定我们的256种颜色对应真彩的哪256种颜色
一般调色板的最小单位为这样的结构体:
typedef struct tagRGBQuAD
{
//定义每个像素的数据类型
unsigned char rgbBlue;//blue分量
unsigned char rgbGreen;//green分量
unsigned char rgbRed;//red分量
unsigned char reserved;//保留值,一般为0
} RGBQuAD;
所有我们写入8位bmp文件时,先写入header,再写入256个上面的结构体,第n个结构体就表示那个像素点的的色彩值为n时的色彩。
1,16位也同理同上,我们也可以知道24位,36位不需要调色板。
保存图片的颜色值,
8位的数据字段大小=1*图片高*图片宽
16位的数据字段大小=2*图片高*图片宽
24位的数据字段大小=3*图片高*图片宽
需要注意36位的4字节位RGB+alpha,alpha表示透明度
上代码:
main.h:
#ifndef MAIN_H
#define MAIN_H
#include
#include "stdio.h"
#include "stdlib.h"
typedef signed char int8_t;
typedef signed short int int16_t;
typedef signed int int32_t;
typedef signed long long int64_t;
/* exact-width unsigned integer types */
typedef unsigned char uint8_t;
typedef unsigned short int uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;
typedef unsigned char bool_t;
typedef float fp32;
typedef double fp64;
typedef uint32_t u32;
typedef uint16_t u16;
typedef uint8_t u8;
typedef uint8_t bool_t;typedef struct
{
u16 row, col;
u8 *element;
}Mat;//一个通道的结构体typedef struct
{
Mat r;
Mat g;
Mat b;
unsigned char channel;
}PIC;//一个图片的结构体,当通道数为1时,使用r通道typedef struct tagRGBQuAD
{
unsigned char rgbBlue;//blue分量
unsigned char rgbGreen;//green分量
unsigned char rgbRed;//red分量
unsigned char reserved;//保留值,一般为0
} RGBQuAD;
extern void BMP_clonebmp(char *name1,char *name2);//将name1的图片复制到name2
extern int BMP_color(RGBQuAD *color, int xsize, int ysize, char *filename);//创作一个24位图片全部的颜色为color
extern int BMP_bmp_to_pic(char *fileName,PIC *pic);//将BMP图片读取,并把数据给picextern int BMP_pic_to_bmp(PIC *pic,char *fileName);//将pic存为为bmp图片,支持8位,24位
extern void bmp_print_info(char *name1);//输出图片信息
#endif // MAIN_H
main.c
#include "main.h"
#include "stdio.h"unsigned char *image1=0;//用于临时保存文件读取的值
#define more 10
#pragma pack(1)
typedef struct //这个结构体就是对上面那个图做一个封装。
{
//bmp header
u8 Signatue[2] ; // B M
u32 FileSize ; //文件大小
u16 Reserv1 ;
u16 Reserv2 ;
u32 FileOffset ; //文件头偏移量
//DIB header
u32 DIBHeaderSize ; //DIB头大小
u32 ImageWidth ; //文件宽度
u32 ImageHight ; //文件高度
u16 Planes ;
u16 BPP ; //每个相素点的位数
u32 Compression ;
u32 ImageSize ; //图文件大小
u32 XPPM ;
u32 YPPM ;
u32 CCT ;
u32 ICC ;
}bmp_header;
#pragma pack()
PIC *PIC_new(u16 width,u16 height,unsigned char channel)
{
PIC *pic=0;
if(channel==3)
{
pic=(PIC *)malloc(sizeof(PIC)*1);
pic->b.element=(unsigned char *)malloc(width*height+more);
pic->r.element=(unsigned char *)malloc(width*height+more);
pic->g.element=(unsigned char *)malloc(width*height+more);
pic->b.col=width;
pic->b.row=height;
pic->g.col=width;
pic->g.row=height;
pic->r.col=width;
pic->r.row=height;
pic->channel=3;
return pic;
}
else if(channel==1)
{
pic=(PIC *)malloc(1);
pic->r.element=(unsigned char *)malloc(width*height+more);
pic->g.element=0;
pic->b.element=0;
pic->r.col=width;
pic->r.row=height;pic->channel=1;
return pic;
}
}
void PIC_free(PIC *pic)//free一个的PIC
{
if(pic->channel==3)
{
free(pic->r.element);
free(pic->g.element);
free(pic->b.element);
pic->r.element=0;
pic->g.element=0;
pic->b.element=0;
}
else
{
free(pic->r.element);
pic->r.element=0;
}
free(&pic);
pic=0;
}void bmp_print_info(char *name1)
{
bmp_header bmpFileHeader;
bmp_read(name1,&bmpFileHeader);
free(image1);
printf("位图文件头主要是对位图文件的一些描述:BMPFileHeader\n\n");
printf("标识帧:%d,%d \n",bmpFileHeader.Signatue[0],bmpFileHeader.Signatue[1]);
printf("BMP 文件大小 = %d 字节\n", bmpFileHeader.FileSize);
printf("保留值1 = %d \n", bmpFileHeader.Reserv1);
printf("保留值2 = %d \n", bmpFileHeader.Reserv2);
printf("文件头的最后到图像数据位开始的偏移量 = %d 字节\n", bmpFileHeader.FileOffset);
// 输出BMP文件的位图信息头的所有信息
printf("\n\n位图信息头主要是对位图图像方面信息的描述:bmpFileHeader\n\n");
printf("信息头的大小 = %d 字节\n", bmpFileHeader.DIBHeaderSize);
printf("位图的高度 = %d \n", bmpFileHeader.ImageHight);
printf("位图的宽度 = %d \n", bmpFileHeader.ImageWidth);
printf("图像的位面数(位面数是调色板的数量,默认为1个调色板) = %d \n", bmpFileHeader.Planes);
printf("每个像素的位数 = %d 位\n", bmpFileHeader.BPP);
printf("压缩类型 = %d \n", bmpFileHeader.Compression);
printf("图像的大小 = %d 字节\n", bmpFileHeader.ImageSize);
printf("水平分辨率 = %d \n", bmpFileHeader.XPPM);
printf("垂直分辨率 = %d \n", bmpFileHeader.YPPM);
printf("使用的色彩数 = %d \n", bmpFileHeader.CCT);
printf("重要的色彩数 = %d \n", bmpFileHeader.ICC);
}
void bmp_go_error(char *mes)//报错函数
{
printf("\n BMP error:");
printf(mes);
exit(0);
}
int bmp_read(char *filename,bmp_header *header)
{
FILE *fp=0;
if (!(fp = fopen(filename, "rb")))
bmp_go_error("can not open file at: bmp_read(unsigned char *image, char *filename,bmp_header1 *header)");
fread(header ,1, sizeof(bmp_header),fp);//得到头信息
image1=(unsigned char *)malloc(header->ImageHight*header->ImageWidth*header->BPP/8+header->FileOffset-54);//根据头信息分配空间
if (image1==NULL)
bmp_go_error("failed to malloc at:bmp_read(unsigned char *image, char *filename,bmp_header1 *header)");
fread(image1,1,(size_t)(long long)(header->ImageHight*header->ImageWidth*header->BPP/8+header->FileOffset-54),fp);
fclose(fp);
return 0;
}
int bmp_write(char *filename,bmp_header *header1)
{
unsigned char header[54] =
{
0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0,
54, 0, 0, 0, 40, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 24, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
long file_size = (long)header1->ImageWidth * (long)header1->ImageHight * 3 + 54;
header[2] = (unsigned char)(file_size &0x000000ff);
header[3] = (file_size >> 8) & 0x000000ff;
header[4] = (file_size >> 16) & 0x000000ff;
header[5] = (file_size >> 24) & 0x000000ff;long width = header1->ImageWidth;
header[18] = width & 0x000000ff;
header[19] = (width >> 8) &0x000000ff;
header[20] = (width >> 16) &0x000000ff;
header[21] = (width >> 24) &0x000000ff;long height = header1->ImageHight;
header[22] = height &0x000000ff;
header[23] = (height >> 8) &0x000000ff;
header[24] = (height >> 16) &0x000000ff;
header[25] = (height >> 24) &0x000000ff;
FILE *fp;
if (!(fp = fopen(filename, "wb")))
return -1;fwrite(header, sizeof(unsigned char), 54, fp);
if(header1->BPP==24)//24 bits color
fwrite(image1, sizeof(unsigned char), (size_t)(long)width * height * 3+header1->FileOffset-54, fp);else if(header1->BPP==32)//32bits color
for(int count=0;count<(header1->FileSize-header1->FileOffset)/4;count++)//把32位转换为24位关键所在,舍弃最后的alpha
{
fwrite(&image1[header1->FileOffset-54+count*4], sizeof(unsigned char),1, fp);
fwrite(&image1[header1->FileOffset-54+count*4+1], sizeof(unsigned char),1, fp);
fwrite(&image1[header1->FileOffset-54+count*4+2], sizeof(unsigned char),1, fp);
}
fclose(fp);
return 0;
}void BMP_clonebmp(char *name1,char *name2)
{
bmp_header header;
bmp_read(name1,&header);
if(header.BPP!=8)
bmp_write(name2,&header);
free(image1);
}int BMP_color(RGBQuAD *color, int xsize, int ysize, char *filename)
{
unsigned char header[54] =
{
0x42, 0x4d, 0, 0, 0, 0, 0, 0, 0, 0,
54, 0, 0, 0, 40, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0, 24, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0
};
int i;
int j;
long file_size = (long)xsize * (long)ysize * 3 + 54;
header[2] = (unsigned char)(file_size &0x000000ff);
header[3] = (file_size >> 8) & 0x000000ff;
header[4] = (file_size >> 16) & 0x000000ff;
header[5] = (file_size >> 24) & 0x000000ff;
long width=xsize;
header[18] = width & 0x000000ff;
header[19] = (width >> 8) &0x000000ff;
header[20] = (width >> 16) &0x000000ff;
header[21] = (width >> 24) &0x000000ff;
long height = ysize;
header[22] = height &0x000000ff;
header[23] = (height >> 8) &0x000000ff;
header[24] = (height >> 16) &0x000000ff;
header[25] = (height >> 24) &0x000000ff;
FILE *fp;
if (!(fp = fopen(filename, "wb")))
return -1;
fwrite(header, sizeof(unsigned char), 54, fp);RGBQuAD color1;
color1.rgbBlue=color->rgbBlue;
color1.rgbGreen=color->rgbGreen;
color1.rgbRed=color->rgbRed;
for(j=0; jfwrite(&color1, sizeof(RGBQuAD)-1,1, fp);
fclose(fp);
return 0;
}PIC *BMP_bmp_to_pic(char *fileName)
{
PIC *pic=0;
bmp_header header;
bmp_read(fileName,&header);
if(header.BPP==24||header.BPP==32)
{
pic=PIC_new(header.ImageWidth,header.ImageHight,3);
if(header.BPP==32)//32位
{
for(int count=0;countpic->b.element[count]=image1[count*4+2+header.FileOffset-54];
for(int count=0;countpic->g.element[count]=image1[count*4+1+header.FileOffset-54];
for(int count=0;countpic->r.element[count]=image1[count*4+header.FileOffset-54];
}
else//24位
{
for(int count=0;countpic->b.element[count]=image1[count*3+2+header.FileOffset-54];
for(int count=0;countpic->g.element[count]=image1[count*3+1+header.FileOffset-54];
for(int count=0;countpic->r.element[count]=image1[count*3+header.FileOffset-54];
}
}
free(image1);
return pic;
}int BMP_pic_to_bmp(PIC *pic,char *fileName)
{
bmp_header header;
header.ImageHight=pic->r.row;
header.ImageWidth=pic->r.col;
header.FileOffset=54;
if(pic->channel==3)
{
image1=(unsigned char *)malloc(header.ImageHight*header.ImageWidth*3);
for(int count=0;count{
image1[3*count]=pic->r.element[count];
image1[3*count+1]=pic->g.element[count];
image1[3*count+2]=pic->b.element[count];
}
header.BPP=24;
bmp_write(fileName,&header);
free(image1);
}
return 1;
}
int main()
{
PIC *mypic=0;
RGBQuAD color={0,255,255}; //一行像素值数组
BMP_clonebmp("pic\\p1.bmp","pic\\p2.bmp"); //功能一:实现一副图像的拷贝,32位到24位
BMP_clonebmp("pic\\p1_1.bmp","pic\\p4.bmp"); //功能一:实现一副图像的拷贝,24位到24位
BMP_color(&color,100,200,"pic\\p3.bmp");//功能二:制作固定大小单色图片mypic=BMP_bmp_to_pic("pic\\p1.bmp");//读取
BMP_pic_to_bmp(mypic ,"pic\\p5.bmp");//写入PIC_free(mypic);//释放内存
return -1;
}
同上的测试代码:https://download.csdn.net/download/libizhide/12131274,0积分币。
读写8位BMP主要在添加一个调色板,以下代码提供思路:
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;
}
如果没有思路,你也可以花2积分币购买高级bmp读取在:
下载(2积分币)
(提供纯C语言读写24BMP文件,32位BMP转24位BMP,读写8位BMP,二值化程序)