数字图像BMP文件格式详解

Posted hatsusakana

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数字图像BMP文件格式详解相关的知识,希望对你有一定的参考价值。

BMP文件格式详解

-------------------------------------------------------------------------------------------------------

Lena

技术图片

 

摘录百科:BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。

BMP文件存储的是原始的BGR数据,格式非常简单,研究数字图入门必备。因为数据没有经过任何压缩,所以BMP文件都比较大。

 

BMP文件格式

  • 文件信息  -->BITMAPFILEHEADER
  • 图像信息  -->BITMAPINFOHEADER
  • 调色板     -->由颜色索引数决定
  • 图像数据  -->由图像尺寸决定

 

1. BITMAPFILEHEADER

typedef struct {

    WORD  bfType;

    DWORD bfSize;

    WORD  bfReserved1;

    WORD  bfReserved2;

    DWORD bfOffBits;

} BITMAP_FILE_HEADER;

 

WORD --> unsigned short

DWORD --> unsigned long (32位占4位,64位占8)

 

变量名

大小(字节)

作用

bfType

2

文件标头BM

bfSize

4

整个文件大小

bfReserved1

2

保留

bfReserved2

2

保留

bfOffBits

4

偏移,一般偏移长度是BITMAPFILEHEADER+BITMAPINFOHEADER,如果存在调色板则随着调色板长度变化

 

2.BITMAPINFOHEADER

typedef struct BITMAP_INFO_HEADER {

    DWORD biSize;

    int     biWidth;

    int     biHeight;

    WORD  biPlanes;

    WORD  biBitCount;

    DWORD biCompression;

    DWORD biSizeImage;

    int     biXPelsPerMeter;

    int     biYPelsPerMeter;

    DWORD biClrUsed;

    DWORD biClrImportant;

} BITMAP_INFO_HEADER;

 

这里的int原来是long64位下会出问题改了下。

 

变量名

大小(字节)

作用

biSize

4

sizeof(BITMAP_INFO_HEADER)

biWidth

4

宽度

biHeight

4

高度

BMP文件图像是倒着存储的,所以如果biHeight>0表示图像是倒立的,如果biHeight<0表示图像是正的。

biPlanes

2

颜色平面数

biBitCount

2

位数(1,4,8,16,24,32)

biCompression

4

压缩

0 BI_RGB(BMP文件不压缩)

1 BI_RLE8

2 BI_RLE4

3 BI_BITFILELDS

4 BI_JPEG

5 BI_PNG

biSizeImage

4

图像数据大小

biXPelsPerMeter

4

水平分辨率

biYPelsPerMeter

4

垂直分辨率

biClrUsed

4

实际使用的颜色索引数

biClrImportant

4

对图像显示有重要影响的颜色索引的数目,如果是0表示都重要

 

3.调色板

调色板实际上是一种颜色索引表,因为常见的BMP文件都是2432位的,而2432位不需要调色板,所以这里不做过多研究。

 

4.图像数据

24位位图 -->  bgr,bgr,bgr

32位位图 -->  bgra,bgra,bgra

 

5.内存对齐

Windows默认是4字节对齐,如果不了解内存对齐可以研究一下,c语言sizeof(结构体)的大小。内存对齐的主要目的是为了加速,BMP文件也需要进行内存对齐处理,简单来说就是每一行的字节数%4==0,如果不足则由0补足。比如:一个24BMP,宽度为99,那么他的图像部分字节数位 99 * 3 ==> 297 % 4 = 1,需要补3个字节0

 

参考文章:

https://www.cnblogs.com/Matrix_Yao/archive/2009/12/02/1615295.html

 

 

/*****************************************************
   BMP文件读写 
   hatsusakana@gmail.com 
*****************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef unsigned short WORD;
typedef unsigned int DWORD;

#pragma pack(1)
typedef struct {
    WORD  bfType;
    DWORD bfSize;
    WORD  bfReserved1;
    WORD  bfReserved2;
    DWORD bfOffBits;
} BITMAP_FILE_HEADER;

typedef struct BITMAP_INFO_HEADER { 
    DWORD biSize; 
    int   biWidth; 
    int   biHeight; 
    WORD  biPlanes; 
    WORD  biBitCount; 
    DWORD biCompression; 
    DWORD biSizeImage; 
    int   biXPelsPerMeter; 
    int   biYPelsPerMeter; 
    DWORD biClrUsed; 
    DWORD biClrImportant; 
} BITMAP_INFO_HEADER;
#pragma pack()

/**
 * 32->24
 */
static void bmp_remove_aplpha (unsigned char *data, unsigned char *buf, size_t *size, size_t w, size_t h) {
    size_t i = 0, j = 0, dst_fix = 0;

    dst_fix = 4 - ((w * 24)>>3) & 3;
    *size = ((((w * 24) + 31) >> 5) << 2) * h;

    for (j = 0; j < h; j++) {
        for (i = 0; i < w; i++) {
            memcpy(buf, data, 3);
            data += 4;
            buf += 3;
        }
        buf += dst_fix;
    }
}

/**
 * 倒转图像
 */
static void bmp_reverse (unsigned char *data, unsigned char *buf, size_t size, size_t w, size_t h) {
    unsigned char *src = NULL;
    int i = 0, j = 0, preline = 0;
    
    preline = size / h;
    for (i = 0, j = h - 1; j >= 0; i++, j--) {
        src = data + j * preline;
        memcpy(buf + i * preline, src, preline);
    }
}

/**
 * bmp_read
 * 
 * 读取一个BMP文件
 * 只支持24/32位,32位会转为24位,倒转的图像会摆正
 */
unsigned char *bmp_read (const char *path, size_t *size, size_t *w, size_t *h) {
    FILE *fp = NULL;
    BITMAP_FILE_HEADER file = {0};
    BITMAP_INFO_HEADER info = {0};
    unsigned char *data = NULL, *buf = NULL;
    
    if (!path || *path == 0 || !size || !w || !h)
        return NULL;
    
    if (!(fp = fopen(path, "rb")))
        return NULL;

    if (fread(&file, 1, sizeof(BITMAP_FILE_HEADER), fp) != sizeof(BITMAP_FILE_HEADER)) {
        fclose(fp);
        return NULL;
    }

    if (fread(&info, 1, sizeof(BITMAP_INFO_HEADER), fp) != sizeof(BITMAP_INFO_HEADER)) {
        fclose(fp);
        return NULL;
    }

    //非BM开头
    if (file.bfType != 0x4d42) {
        fclose(fp);
        return NULL;
    }

    //暂时只能支持24与32位
    if (info.biBitCount != 24 && info.biBitCount != 32) {
        fclose(fp);
        return NULL;
    }

    if (info.biSizeImage == 0) {
        info.biSizeImage = info.biWidth * abs(info.biHeight) * (info.biBitCount == 24 ? 3 : 4);
    }
    
    data = (unsigned char *)malloc(sizeof(unsigned char) * info.biSizeImage);
    buf = (unsigned char *)malloc(sizeof(unsigned char) * info.biSizeImage);
    if (!data || !buf) {
        if (data) free(data);
        if (buf) free(buf);
        fclose(fp);
        return NULL;
    }
    
    if (fread(data, 1, info.biSizeImage, fp) != info.biSizeImage) {
        free(data);
        free(buf);
        fclose(fp);
        return NULL;
    }
    
    fclose(fp);
    
    *size = info.biSizeImage;
    *w = info.biWidth;
    *h = abs(info.biHeight);
    
    //32转24
    if (info.biBitCount == 32) {
        bmp_remove_aplpha(data, buf, size, *w, *h);
        memcpy(data, buf, *size);
    }
    
    //图像倒转
    if (info.biHeight > 0) {
        bmp_reverse(data, buf, *size, *w, *h);
        memcpy(data, buf, *size);
    }

    free(buf);
    return data;
}

/**
 * bmp_write
 */
int bmp_write (unsigned char *data, size_t size, size_t w, size_t h, const char *path) {
    FILE *fp = NULL;
    BITMAP_FILE_HEADER file = {0};
    BITMAP_INFO_HEADER info = {0};
    unsigned char *src = NULL;
    int hsrc = 0, preline = 0;
    
    if (!data || size == 0 || w == 0 || h == 0 || !path || *path == 0)
        return 0;
    
    if (!(fp = fopen(path, "wb+")))
        return 0;

    file.bfType = 0x4d42;
    file.bfSize = sizeof(BITMAP_FILE_HEADER) + sizeof(BITMAP_INFO_HEADER) + size;
    file.bfReserved1 = 0;
    file.bfReserved2 = 0;
    file.bfOffBits = sizeof(BITMAP_FILE_HEADER) + sizeof(BITMAP_INFO_HEADER);
    
    info.biSize = sizeof(BITMAP_INFO_HEADER);
    info.biWidth = w;
    info.biHeight = h;
    info.biPlanes = 1;
    info.biBitCount = 24;
    info.biCompression = 0L;
    info.biSizeImage = size;
    info.biXPelsPerMeter = 0;
    info.biYPelsPerMeter = 0;
    info.biClrUsed = 0;
    info.biClrImportant = 0;
    
    fwrite(&file, 1, sizeof(BITMAP_FILE_HEADER), fp);
    fwrite(&info, 1, sizeof(BITMAP_INFO_HEADER), fp);
    
    //倒转写入图像数据
    preline = size / h;
    for (hsrc = h - 1; hsrc >= 0; hsrc--) {
        src = data + hsrc * preline;
        fwrite(src, 1, preline, fp);
    }
    
    fclose(fp);
    return 1;
}

int main (int argc, char *argv[]) {
    unsigned char *data = NULL;
    size_t size = 0, w = 0, h = 0;
    
    data = bmp_read("1.bmp", &size, &w, &h);
    bmp_write(data, size, w, h, "2.bmp");
    return 1;
}

 

以上是关于数字图像BMP文件格式详解的主要内容,如果未能解决你的问题,请参考以下文章

BMP格式详解

BMP文件格式详解

BMP文件格式详解

如何用MATLAB把bmp格式变成灰度图像

数据恢复培训资料:BMP文件详解

BMP格式详解