BMP讲解,调色板讲解,纯C语言读写24BMP文件,32位BMP转24位BMP,读写8位BMP

杂叙:

最近在做嵌入式视觉工作,故不可以用opencv库,只好自己写,做视觉第一点便是读取图片,于是学习了bmp(byte map picture)的读写,发现这是一个小坑,首先不知道怎么得到bmp图片,这里推荐网站:转换网站

正文:

1.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位无调色板),数据字段(不定长字节,由不同位数与图片大小决定)。

header字段:看此结构体

#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*图片高*图片宽

需要注意36位的4字节位RGB+alpha,alpha表示透明度

2.纯C语言读写24BMP文件,32位BMP转24位BMP:

上代码:

main.h:

#ifndef MAIN_H
#define MAIN_H
#include <fcntl.h>
#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图片读取,并把数据给pic

extern 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; j<ysize*xsize; j++)
                fwrite(&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;count<header.ImageHight*header.ImageWidth;count++)
                pic->b.element[count]=image1[count*4+2+header.FileOffset-54];
            for(int count=0;count<header.ImageHight*header.ImageWidth;count++)
                pic->g.element[count]=image1[count*4+1+header.FileOffset-54];
            for(int count=0;count<header.ImageHight*header.ImageWidth;count++)
                pic->r.element[count]=image1[count*4+header.FileOffset-54];
        }
        else//24位
        {
            for(int count=0;count<header.ImageHight*header.ImageWidth;count++)
                pic->b.element[count]=image1[count*3+2+header.FileOffset-54];
            for(int count=0;count<header.ImageHight*header.ImageWidth;count++)
                pic->g.element[count]=image1[count*3+1+header.FileOffset-54];
            for(int count=0;count<header.ImageHight*header.ImageWidth;count++)
                pic->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<header.ImageHight*header.ImageWidth;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积分币。

 

3.读写8位BMP

读写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,二值化程序)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值