美文网首页
音视频入门-04-BMP图像四字节对齐的问题

音视频入门-04-BMP图像四字节对齐的问题

作者: binglingziyu | 来源:发表于2019-09-25 11:02 被阅读0次

* 音视频入门文章目录 *

BMP 图像四字节对齐

表示 BMP 位图中像素的位元是以行为单位对齐存储的,每一行的大小都向上取整为4字节(32 位 DWORD)的倍数。如果图像的高度大于 1,多个经过填充实现对齐的行就形成了像素数组。

完整存储的一行像素所需的字节数可以通过这个公式计算:

image-demo-bmp-dword-align
  • 每一行的末尾通过填充若干个字节的数据(并不一定为 0)使该行的长度为 4 字节的倍数。像素数组读入内存后,每一行的起始地址必须为 4 的倍数。这个限制仅针对内存中的像素数组,针对存储时,仅要求每一行的大小为 4 字节的倍数,对文件的偏移没有限制。

  • 例如:对于 24 位色的位图,如果它的宽度为 1 像素,那么除了每一行的数据(蓝、绿、红)需要占 3 字节外,还会填充 1 字节;而如果宽为 2 像素,则需要 2 字节的填充;宽为 3 像素时,需要 3 字节填充;宽为 4 像素时则不需要填充。

  • 图像相同的条件下,位图图像文件通常比使用其它压缩算法的图像文件大很多。

四字节对齐问题-发现

没有四字节对齐的 700x700 BMP 文件

之前,用 RGB 拼出一张 700x700 的彩虹图片,并成功保存成 BMP 文件。

当时并没有进行四字节对齐,保存成的 BMP 文件也没有问题。

image-demo-rainbow-bmp

原因:
根据四字节对齐的要求,700 x 3 = 2100,2100 / 4 = 525,行像素数据已经四字节对齐了。

如果没有对齐会怎么样

将图片尺寸改成 711x711:

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

// 彩虹的七种颜色
u_int32_t rainbowColors[] = {
        0XFF0000, // 红
        0XFFA500, // 橙
        0XFFFF00, // 黄
        0X00FF00, // 绿
        0X007FFF, // 青
        0X0000FF, // 蓝
        0X8B00FF  // 紫
};

/*bmp file header*/
typedef struct {
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
} BitmapFileHeader;

/*bmp info header*/
typedef struct {
    unsigned int   biSize; /* Size of info header */
    int            biWidth; /* Width of image */
    int            biHeight; /* Height of image */
    unsigned short biPlanes; /* Number of color planes */
    unsigned short biBitCount; /* Number of bits per pixel */
    unsigned int   biCompression; /* Type of compression to use */
    unsigned int   biSizeImage; /* Size of image data */
    int            biXPelsPerMeter; /* X pixels per meter */
    int            biYPelsPerMeter; /* Y pixels per meter */
    unsigned int   biClrUsed; /* Number of colors used */
    unsigned int   biClrImportant; /* Number of important colors */
} BitmapInfoHeader;

void writeRGBToBmp(char *filename, int width, int height) {
    FILE *bitmapFile = fopen(filename, "wb");
    if(!bitmapFile) {
        printf("Could not write file \n");
        return;
    }

    uint16_t bfType = 0x4d42;

    BitmapFileHeader fileHeader;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + width*height*3;
    fileHeader.bfOffBits = 0x36;

    BitmapInfoHeader infoHeader;
    infoHeader.biSize = sizeof(BitmapInfoHeader);
    infoHeader.biWidth = width;
    infoHeader.biHeight = -height;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biSizeImage = 0;
    infoHeader.biCompression = 0;
    infoHeader.biXPelsPerMeter = 5000;
    infoHeader.biYPelsPerMeter = 5000;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&bfType, sizeof(bfType), 1, bitmapFile);
    fwrite(&fileHeader, sizeof(fileHeader), 1, bitmapFile);
    fwrite(&infoHeader, sizeof(infoHeader), 1, bitmapFile);

    for (int i = 0; i < width; ++i) {

        // 当前颜色
        u_int32_t currentColor = rainbowColors[0];
        if(i < 100) {
            currentColor = rainbowColors[0];
        } else if(i < 200) {
            currentColor = rainbowColors[1];
        } else if(i < 300) {
            currentColor = rainbowColors[2];
        } else if(i < 400) {
            currentColor = rainbowColors[3];
        } else if(i < 500) {
            currentColor = rainbowColors[4];
        } else if(i < 600) {
            currentColor = rainbowColors[5];
        } else if(i < 700) {
            currentColor = rainbowColors[6];
        }
        // 当前颜色 R 分量
        u_int8_t R = (currentColor & 0xFF0000) >> 16;
        // 当前颜色 G 分量
        u_int8_t G = (currentColor & 0x00FF00) >> 8;
        // 当前颜色 B 分量
        u_int8_t B = currentColor & 0x0000FF;

        for (int j = 0; j < height; ++j) {
            // 按 BGR 顺序写入一个像素 RGB24 到文件中
            fwrite(&B, 1, 1, bitmapFile);
            fwrite(&G, 1, 1, bitmapFile);
            fwrite(&R, 1, 1, bitmapFile);
        }
    }

    fclose(bitmapFile);
}

int main() {
    writeRGBToBmp("/Users/hubin/Desktop/rainbow-711x711.bmp", 711, 711);
    return 0;
}

彩虹图片已经无法显示出来了:

image-demo-bmp-no-dword-align

四字节对齐问题-解决

计算一行像素,四字节对齐后的字节数

// 计算每一行像素 4 字节对齐后的字节数
int caculateLineBytes(int width) {
    //******* 四字节对齐 *******
    return (24 * width + 31)/32 *4;
    //******* 四字节对齐 *******
}

写入一行像素的数据

// 计算一行像素四字节对齐所需字节数
int lineBytes = caculateLineBytes(width);

for (int i = 0; i < width; ++i) {
    u_int32_t currentColor = rainbowColors[i];
    u_int8_t R = (currentColor & 0xFF0000) >> 16;
    u_int8_t G = (currentColor & 0x00FF00) >> 8;
    u_int8_t B = currentColor & 0x0000FF;

    // 存储一行像素数据的数组
    u_int8_t lineBytesArray[lineBytes];

    for (int j = 0; j < height; ++j) {
        int currentIndex = 3*j;
        lineBytesArray[currentIndex] = B;
        lineBytesArray[currentIndex+1] = G;
        lineBytesArray[currentIndex+2] = R;
    }

    // 将四字节对齐后的一行像素数据写入文件
    fwrite(lineBytesArray, sizeof(lineBytesArray), 1, file);
}

完整的代码

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

// 彩虹的七种颜色
u_int32_t rainbowColors[] = {
        0XFF0000, // 红
        0XFFA500, // 橙
        0XFFFF00, // 黄
        0X00FF00, // 绿
        0X007FFF, // 青
        0X0000FF, // 蓝
        0X8B00FF  // 紫
};

/*bmp file header*/
typedef struct {
    unsigned int   bfSize;           /* Size of file */
    unsigned short bfReserved1;      /* Reserved */
    unsigned short bfReserved2;      /* ... */
    unsigned int   bfOffBits;        /* Offset to bitmap data */
} BitmapFileHeader;

/*bmp info header*/
typedef struct {
    unsigned int   biSize; /* Size of info header */
    int            biWidth; /* Width of image */
    int            biHeight; /* Height of image */
    unsigned short biPlanes; /* Number of color planes */
    unsigned short biBitCount; /* Number of bits per pixel */
    unsigned int   biCompression; /* Type of compression to use */
    unsigned int   biSizeImage; /* Size of image data */
    int            biXPelsPerMeter; /* X pixels per meter */
    int            biYPelsPerMeter; /* Y pixels per meter */
    unsigned int   biClrUsed; /* Number of colors used */
    unsigned int   biClrImportant; /* Number of important colors */
} BitmapInfoHeader;

// 计算每一行像素 4 字节对齐后的字节数
int caculateLineBytes(int width) {
    //******* 四字节对齐 *******
    return (24 * width + 31)/32 *4;
    //******* 四字节对齐 *******
}

void writeRGBToBmp(char *filename, int width, int height) {
    FILE *bitmapFile = fopen(filename, "wb");
    if(!bitmapFile) {
        printf("Could not write file \n");
        return;
    }

    uint16_t bfType = 0x4d42;

    int lineBytes = caculateLineBytes(width);

    BitmapFileHeader fileHeader;
    fileHeader.bfReserved1 = 0;
    fileHeader.bfReserved2 = 0;
    fileHeader.bfSize = 2 + sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + lineBytes*height;
    fileHeader.bfOffBits = 0x36;

    BitmapInfoHeader infoHeader;
    infoHeader.biSize = sizeof(BitmapInfoHeader);
    infoHeader.biWidth = width;
    infoHeader.biHeight = -height;
    infoHeader.biPlanes = 1;
    infoHeader.biBitCount = 24;
    infoHeader.biSizeImage = 0;
    infoHeader.biCompression = 0;
    infoHeader.biXPelsPerMeter = 5000;
    infoHeader.biYPelsPerMeter = 5000;
    infoHeader.biClrUsed = 0;
    infoHeader.biClrImportant = 0;

    fwrite(&bfType, sizeof(bfType), 1, bitmapFile);
    fwrite(&fileHeader, sizeof(fileHeader), 1, bitmapFile);
    fwrite(&infoHeader, sizeof(infoHeader), 1, bitmapFile);

    for (int i = 0; i < width; ++i) {

        // 当前颜色
        u_int32_t currentColor = rainbowColors[0];
        if(i < 100) {
            currentColor = rainbowColors[0];
        } else if(i < 200) {
            currentColor = rainbowColors[1];
        } else if(i < 300) {
            currentColor = rainbowColors[2];
        } else if(i < 400) {
            currentColor = rainbowColors[3];
        } else if(i < 500) {
            currentColor = rainbowColors[4];
        } else if(i < 600) {
            currentColor = rainbowColors[5];
        } else if(i < 700) {
            currentColor = rainbowColors[6];
        }
        // 当前颜色 R 分量
        u_int8_t R = (currentColor & 0xFF0000) >> 16;
        // 当前颜色 G 分量
        u_int8_t G = (currentColor & 0x00FF00) >> 8;
        // 当前颜色 B 分量
        u_int8_t B = currentColor & 0x0000FF;

        u_int8_t lineBytesArray[lineBytes];

        for (int j = 0; j < height; ++j) {
            int currentIndex = 3*j;
            lineBytesArray[currentIndex] = B;
            lineBytesArray[currentIndex+1] = G;
            lineBytesArray[currentIndex+2] = R;
        }

        fwrite(lineBytesArray, sizeof(lineBytesArray), 1, bitmapFile);
    }
    fclose(bitmapFile);
}

int main() {
    writeRGBToBmp("/Users/staff/Desktop/rainbow-711x711-fix.bmp", 711, 711);
    return 0;
}



711x711 的彩虹图片也显示出来了:

image-demo-bmp-with-dword-align.jpg

代码:

04-rgb-to-bmp-fix

参考资料:

BMP图像四字节对齐的问题

维基百科-BMP

non-dword-aligned-pixel-to-dword-aligned-bitmap

generate-bmp-file-from-array-of-rgb-values

内容有误?联系作者:

联系作者

相关文章

  • 音视频入门-04-BMP图像四字节对齐的问题

    * 音视频入门文章目录 * BMP 图像四字节对齐 表示 BMP 位图中像素的位元是以行为单位对齐存储的,每一行的...

  • OpenGL纹理 - 常用API简介

    原始图像数据与内存包装 图像的存储空间= 图像的宽度 * 图像的高度 * 每个像素的字节数(系统决定) 内存对齐:...

  • sizeof与字节对齐

    参考 【面试题】sizeof引发的血案编译器与字节对齐c 语言字节对齐问题详解C/C++内存对齐内存存取粒度C和C...

  • 字节对齐与大端小端与内存区域划分

    字节对齐 C语言字节对齐C语言字节对齐/7213465 大端小端 字节序(大小端)详解从高低地址和高低位开始理解(...

  • image 优化之 —— image copy 字节对齐

    Core Animation在图像数据非字节对齐的情况下渲染前会先拷贝一份图像数据,官方文档没有对这次拷贝行为作说...

  • 字节对齐align

    关于对齐的问题,我们经常在运行程序的时候出现对齐异常,面试官也特别热衷于纠结字节对齐的问题,因此,记于此,解惑之。...

  • iOS底层之内存对齐算法解析

    目前但凡一个iOS岗面试都会问个内存对齐问题,那么什么是字节对齐?成员变量对齐和对象内存对齐有什么区别?今天我来为...

  • iOS 技术

    结构体的字节对齐和OC对象的字节对齐? instance(实例对象)、class(类对象)、meta-class(...

  • Unpaired Image-to-Image Translat

    Abstract 图像到图像的翻译是一类视觉和图形问题,其目标是使用对齐的图像对训练集来学习输入图像和输出图像之间...

  • c语言结构体对齐

    1.什么是字节对齐 结构体里面一般会按照某种规则去进行字节对齐默认规则如下:对齐是按照结构体中长度最长的变量来对齐...

网友评论

      本文标题:音视频入门-04-BMP图像四字节对齐的问题

      本文链接:https://www.haomeiwen.com/subject/lokayctx.html