数字图像处理:关于BMP格式图像的理解和读写(c++)

一、什么是BMP格式? 它与我们常见的其他图片格式(jpeg, jpg, png, tif)有什么区别?

1、JPEG

JPEG是Joint Photographic Experts Group(联合图像专家小组)的缩写,是第一个国际图像压缩标准。JPEG图像压缩算法能够在提供良好的压缩性能的同时,具有比较好的重建质量,被广泛应用于图像、视频处理领域。人们日常碰到的“.jpeg”、‘’.jpg“等指代的是图像数据经压缩编码后在媒体上的封存形式,不能与JPEG压缩标准混为一谈。

2、BMP

BMP,(全称Bitmap)是Windows操作系统中的标准图像文件格式,可以分成两类:设备相关位图(DDB)和设备无关位图(DIB),使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP 文件所占用的空间很大。BMP文件的图像深度可选1bit、4bit、8bit及24bit。BMP 文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。

3、TIF

TIF,正确的是TIFF,标签图像文件格式(Tagged Image File Format,简写为TIFF)是一种灵活的位图格式,主要用来存储包括照片和艺术图在内的图像。它最初由Aldus公司与微软公司一起为PostScript打印开发。TIFF与JPEG和PNG一起成为流行的高位彩色图像格式。TIFF格式在业界得到了广泛的支持,如Adobe公司的Photoshop、The GIMP Team的GIMP、Ulead PhotoImpact 和Paint Shop Pro等图像处理应用、QuarkXPress 和Adobe InDesign 这样的桌面印刷和页面排版应用,扫描、传真、文字处理、光学字符识别和其它一些应用等都支持这种格式。

4、PNG

PNG,图像文件存储格式,其设计目的是试图替代GIF和TIFF文件格式,同时增加一些GIF文件格式所不具备的特性。PNG的名称来源于“可移植网络图形格式(Portable Network Graphic Format,PNG)”,也有一个非官方解释“PNG’s Not GIF”,是一种位图文件(bitmap file)存储格式,读作“ping”。PNG用来存储灰度图像时,灰度图像的深度可多到16位,存储彩色图像时,彩色图像的深度可多到48位,并且还可存储多到16位的α通道数据。PNG使用从LZ77派生的无损数据压缩算法,一般应用于JAVA程序、网页或S60程序中,原因是它压缩比高,生成文件体积小

二、怎么计算BMP的大小

BMP是一种不压缩的图像文件,我们怎么计算bmp图像的大小呢?

BMP的类别及存储方式

1.BMP的常见类型

通常我们最常见的就是24位图,所谓的24位图,就是说一个像素的颜色信息用24位来表示,也就是说,对于三原色BRG,每一个颜色都用以字节(8)位来表示。除了24位图,还有1位(单色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增强色),24位(真彩色)和32位等。

2. BMP的文件头:

BMP文件头(bmp file header):共14字节

0-1:bfType,表示文件类型,BMP格式的文件这两个字节是0x4D42,10进制就是19778,字符显示就是‘BM’;
2-5:bfSize,表示文件的大小,检查文件信息,验证正确;
6-7:bfReserved1,保留位,必须设置为0;
8-9:bfReserved2,保留位,必须设置为0;
a-d:bfOffBits,4字节的偏移,表示从文件头到位图数据的偏移;

位图信息头(bitmap information):共40字节;

0e-11:4字节的biSize, 信息头的大小,即40;
12-15:4字节的biWidth,用像素表示图像的宽度,查看文件信息验证正确;
16-19:4字节的biHeight,以像素为单位说明图像的高度,同时如果为正,说明位图倒立(即数据表示从图像的左下角到右上角),如果为负说明正向; (这两个信息在程序要在内存分配的时候使用)
1a-1b:2字节的biPlanes,为目标设备说明颜色平面数,总被设置为1;
1c-1d:2字节的biBitCount,说明比特数/像素数,值有1、2、4、8、16、24、32;
1e-21:4字节的biCompression,说明图像的压缩类型,最常用的就是0(BI_RGB),表示不压缩;
22-25:4字节的biSizeImage,说明位图数据的大小,当用BI_RGB格式时,可以设置为0;
26-29:4字节的biXPelsPerMeter,表示水平分辨率,单位是像素/米,有符号整数;
2a-2d:4字节的biYPelsPerMeter,表示垂直分辨率,单位是像素/米,有符号整数;
2e-31:4字节的biClrUsed,说明位图使用的调色板中的颜色索引数,为0说明使用所有;
32-35:4字节的biClrImportant,说明对图像显示有重要影响的颜色索引数,为0说明都重要;

所以,总的BMP格式从头到尾的顺序应该是:
a. bmp文件头(bmp file header):共14字节;
b. 位图信息头(bitmap information):共40字节;
c. 调色板(color palette):可选;
d. 位图数据;

计算大小

假设我们获取的是每个像素是24位的bmp图像,即每个像素占用三个字节(按照R、G、B的顺序,每个分量占一个字节),此外,这种格式还需要54个额外的字节来存储“头”信息,可以根据以下公式确定BMP文件大小:

Hbytes=(Hpixels*3+3) & (11.......00)
24b RGB BMP文件大小= 54 + Hbytes*Vpixels

其中Vpixels和Hpixels是图像的高度和宽度,(这里V是指vertical,H是指horizon,与一般width和high表示方式要区别开,如果是640×480的图片,就是对应Vpixels=480, Hpixels=640),则图片大小为

54+3×640×480=921654 bytes。

但是看公式,可以知道Hbytes必须舍入为下一个可以被4整除的整数,以确保BMP图像大小是4的倍数。

为什么要确保BMP图像大小是4的倍数?

1.首先普及一个概念,什么是32位系统、64位系统?

32位和64位操作系统是指:CPU一次处理数据的能力是32位还是64位。现在市场上的CPU一般都是64位的,但是这些CPU并不是真正意义上的64 位CPU,里面依然保留了大部分32位的技术,只是进行了部分64位的改进。32位和64位的区别还涉及了内存的寻址方面,32位系统的最大寻址空间是2 的32次方= 4294967296(bit)= 4(GB)左右,而64位系统的最大寻址空间的寻址空间则达到了2的64次方= 4294967296(bit)的32次方=数值大于1亿GB。换而言之,就是说32位系统的处理器最大只支持到4G内存,而64位系统最大支持的内存高达亿位数。

2.网上最多的解释

位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount(每个像素所需的位数)=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是4的倍数(即以long为单位),不足的以0填充。

3.举个例子

如果是24位1024×1024的BMP文件需要的存储空间为:

54+1024*1024*3 = 3145782字节

如果是24位321×1024的BMP文件需要的存储空间为:

54+(321*3+1)*127 = 122482字节

相当于需要每行要浪费一个字节,总共需要浪费127个字节。

三、怎么读取bmp格式的图片


#include "ImageStuff.h"


unsigned char **ReadBMP(char *filename) {
    int i;
    FILE *f = fopen(filename, "rb");
    if (f == NULL) {
        printf("\n\n%s NOT FOUND\n\n", filename);
        exit(1);
    }

    unsigned char HeaderInfo[54];
    fread(HeaderInfo, sizeof(unsigned char), 54, f); // read the 54-byte header

    // extract image height and width from header
    int width = *(int *) &HeaderInfo[18];
    int height = *(int *) &HeaderInfo[22];

    //copy header for re-use
    for (i = 0; i < 54; i++) {
        ip.HeaderInfo[i] = HeaderInfo[i];
    }

    ip.Vpixels = height;
    ip.Hpixels = width;
    int RowBytes = (width * 3 + 3) & (~3);//Be careful with this!
    ip.Hbytes = RowBytes;

    printf("\n   Input BMP File name: %20s  (%u x %u)", filename, ip.Hpixels, ip.Vpixels);

    unsigned char tmp;
    unsigned char **TheImage = (unsigned char **) malloc(height * sizeof(unsigned char *));
    for (i = 0; i < height; i++) {
        TheImage[i] = (unsigned char *) malloc(RowBytes * sizeof(unsigned char));
    }

    for (i = 0; i < height; i++) {
        fread(TheImage[i], sizeof(unsigned char), RowBytes, f);
    }


    fclose(f);
    return TheImage;  // remember to free() it in caller!
}

四、怎么写入bmp格式的图片


void WriteBMP(unsigned char **img, char *filename) {
    FILE *f = fopen(filename, "wb");
    if (f == NULL) {
        printf("\n\nFILE CREATION ERROR: %s\n\n", filename);
        exit(1);
    }

    unsigned long int x, y;
    char temp;

    //write header
    for (x = 0; x < 54; x++) {
        fputc(ip.HeaderInfo[x], f);
    }

    //write data
    for (x = 0; x < ip.Vpixels; x++) {
        for (y = 0; y < ip.Hbytes; y++) {
            temp = img[x][y];
            fputc(temp, f);
        }
    }
    printf("\n  Output BMP File name: %20s  (%u x %u)", filename, ip.Hpixels, ip.Vpixels);

    fclose(f);
}

对应的头文件 ImageStuff.h:

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

struct ImgProp {
    int Hpixels;
    int Vpixels;
    unsigned char HeaderInfo[54];
    unsigned long int Hbytes;
};

struct Pixel {
    unsigned char R;
    unsigned char G;
    unsigned char B;
};

unsigned char **ReadBMP(char *);

void WriteBMP(unsigned char **, char *);

extern struct ImgProp ip;

参考地址:
https://blog.csdn.net/u012877472/article/details/50272771

发布了300 篇原创文章 · 获赞 203 · 访问量 59万+

猜你喜欢

转载自blog.csdn.net/Felaim/article/details/103868035
今日推荐