BMP转YUV

来源:互联网 发布:linux export 编辑:程序博客网 时间:2024/05/19 23:16

一、实验原理

(1)文件头 BITMAP_FILE_HEADER,包含如下内容

typedef struct tagBITMAPFILEHEADER {    //0x00~0x01,说明文件的类型    WORD bfType;     //0x02~0x05,说明文件的大小,用字节B为单位    DWORD bfSize;    //0x06~0x07,保留,设置为0    WORD bfReserved1;    //0x08~0x09,保留,设置为0    WORD bfReserved2;    //0x0a~0x0d,说明从BITMAP_FILE_HEADER结构开始到实际的图像数据之间的字节偏移量    DWORD bfOffBits;} BITMAPFILEHEADER;

(2)信息头BITMAP_INFO_HEADER,包含如下内容

typedef struct tagBITMAPINFOHEADER {    //0x0e~0x11,说明当前结构体所需字节数    DWORD biSize;    //0x12~0x15,以像素为单位说明图像的宽度    LONG biWidth;    //0x16~0x19,以像素为单位说明图像的高度    LONG biHeight;    //0x1a~0x1b,说明位面数,必须为1    WORD biPlanes;    //0x1c~0x1d,说明图像的位深度    WORD biBitCount;    //0x1e~0x21,说明图像是否压缩及压缩类型    DWORD biCompression;    //0x22~0x25,以字节为单位说明图像大小,必须是4的整数倍    DWORD biSizeImage;    //0x26~0x29,目标设备的水平分辨率,像素/米     LONG biXPelsPerMeter;    //0x2a~0x2d,目标设备的垂直分辨率,像素/米    LONG biYPelsPerMeter;    //0x2e~0x31,说明图像实际用到的颜色数,如果为0,则颜色数为2的biBitCount次方    DWORD biClrUsed;    //0x32~0x35,说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。    DWORD biClrImportant;} BITMAPINFOHEADER;

(3)bmp图片基本信息

iu.bmp 这里写图片描述 vs中二进制方式打开 这里写图片描述 图像基本信息 分辨率:1024*680 图片大小:1.99MB 0x00~0x01 标记了此文件为BMP文件,内容是B和M两个字母的ASCII码 0x02~0x05 以字节为单位的文件大小 0x0a~0x0d 实际数据的字节偏移量。 0x22~0x25 BMP图像大小biSizeImage

-

0x22~0x25,BMP图像大小biSizeImage可由下式计算

biSizeImage=cx×biBitCount+3132×4×cy+2

其中,cx,cy表示水平和垂直方向的像素数。cx×biBitCount表示一行图像占了多少位。BMP规定这个图像大小必须是4字节的整数倍,也就是32位的整数倍,因此需要把cx×biBitCount加31再除以32后下取整,就保证了计算结果是离这个数最近的而且是比它大的32的倍数,也就保证了是4字节的整数倍。乘以4和行数,得到4字节整数倍的图像大小。
另外,BMP文件的末尾两个字节是保留位,无论图像是什么这两个字节都为0,因此最后计算结果还要加上2字节。图像大小biSizeImage+字节偏移量bfOffBits=文件大小bfSize。

二、程序过程

(1)——–main_bmp2yuv.cpp——–

main()流程:

Created with Raphaël 2.1.0读取文件获得bmp文件自包含信息创建buf缓冲区分别调用BMP2RGB(),RGB2YUV()函数进行空间转换将YUV缓冲区内容依次写入YUV文件释放缓冲区
int main(int argc, char** argv){    //设置命令行参数    char* bmpFileName = argv[1];    char* yuvFileName = argv[2];    //打开文件    FILE* bmpFile = fopen(bmpFileName, "rb");    if (bmpFile == NULL)    {        printf("Cannot open the BMP file.\n");        exit(1);    }    else    {        printf("The BMP file is %s\n", bmpFileName);    }    FILE* yuvFile = fopen(yuvFileName, "wb");    if (yuvFile == NULL)    {        printf("Cannot open the YUV file.\n");        exit(1);    }    else    {        printf("The YUV file is %s\n", yuvFileName);    }    //读取BMP文件头,信息头,读取错误时的处理代码    BITMAPFILEHEADER file_header;    BITMAPINFOHEADER info_header;    if (fread(&file_header, sizeof(BITMAPFILEHEADER), 1, bmpFile) != 1)        if (file_header.bfType != 0x4D42)        {            printf("Not BMP file.\n");            exit(1);        }    if (fread(&info_header, sizeof(BITMAPINFOHEADER), 1, bmpFile) != 1)    {        printf("read info header error!");        exit(0);    }//结束读取BMP文件头    //读取图像尺寸    int width = info_header.biWidth;    int height = info_header.biHeight;    //开辟缓冲区 buf    u_int8_t* yBuf = (u_int8_t*)malloc(height*width);    u_int8_t* uBuf = (u_int8_t*)malloc(height*width / 4);    u_int8_t* vBuf = (u_int8_t*)malloc(height*width / 4);    u_int8_t* rgbBuf = (u_int8_t*)malloc(height*width * 3);    if (yBuf == NULL || uBuf == NULL || vBuf == NULL || rgbBuf == NULL)    {        printf("Not enough memory\n");        exit(1);    }    //BMP与RGB的转换,得到RGB数据    if (BMP2RGB(file_header, info_header, bmpFile, rgbBuf))        {            printf("BMP2RGB error\n");            exit(1);        }    //RGB与YUV的转换,得到YUV数据    int flip = 0;    /*读取到的图像数据是倒序存放的,flip=0保证了RGB2YUV可以正确地对其转换*/    if (RGB2YUV(width, height, rgbBuf, yBuf, uBuf, vBuf, flip))        {            printf("RGB2YUV error\n");            exit(1);        }    //将yuv按顺序写入yuvfile文件    fwrite(yBuf, 1, width * height, yuvFile);    fwrite(uBuf, 1, (width * height) / 4, yuvFile);    fwrite(vBuf, 1, (width * height) / 4, yuvFile);    //打印宽高,方便yuv观看程序打开    printf("width is %i", width);    printf("\n");    printf("heightis %i", height);     printf("\n");    //清理内存    free(rgbBuf);    free(yBuf); free(uBuf); free(vBuf);    fclose(bmpFile);    fclose(yuvFile);    return 0;}

特别:
因为bmp文件是倒序存储的。所以在读文件是应该从最后一行开始读,开始设置(!flip)=1,则执行以下条件

if (!flip) {        for (j = 0; j < y_dim; j ++)        {            y = y_buffer + (y_dim - j - 1) * x_dim;            u = u_buffer + (y_dim - j - 1) * x_dim;            v = v_buffer + (y_dim - j - 1) * x_dim;            for (i = 0; i < x_dim; i ++) {                g = b + 1;                r = b + 2;                *y = (unsigned char)(  RGBYUV02990[*r] + RGBYUV05870[*g] + RGBYUV01140[*b]);                *u = (unsigned char)(- RGBYUV01684[*r] - RGBYUV03316[*g] + (*b)/2          + 128);                *v = (unsigned char)(  (*r)/2          - RGBYUV04187[*g] - RGBYUV00813[*b] + 128);                b += 3;                y ++;                u ++;                v ++;            }        }    } 

重点解释:假设图片是5*3的像素矩阵最后一行的地址为y_buffer+(3-1)*5
抽象成宽高分别x_dim、y_dim的循环则为:

ybuffer+(ydimj1)xdim

y = y_buffer + (y_dim - j - 1) * x_dim;u = u_buffer + (y_dim - j - 1) * x_dim;v = v_buffer + (y_dim - j - 1) * x_dim;

(2)BMP2RGB()函数

1.rgb的位数为24(8,8,8)

BMP2RGB()流程:

Created with Raphaël 2.1.0确定像素的实际点阵数开辟实际字节数量的缓冲区依次写入rgb释放缓冲区
//确定像素的实际点阵数    w = (info_h.biWidth*info_h.biBitCount + 31) / 32 * 4;//w为实际一行的字节数    h = info_h.biHeight;//h为列数    //开辟实际字节数量的缓冲区,读数据,一次读取一个字节    u_int8_t* dataBuf = (u_int8_t*)malloc(w*h);    /*使用文件头的字节偏移属性bfOffBits    直接把文件指针定位到像素值数据的起始 */    fseek(pFile, file_h.bfOffBits, 0);    fread(dataBuf, 1, w*h, pFile);    unsigned char* data = dataBuf;    unsigned char* rgb = rgbBuf;    //开始写入rgb    int i, j;    for (j = 0; j < h; j++)//j控制行循环    {        for (i = 0; i < w; i += 3)//i控制列循环        {            *rgb = data[i + w*j];//B            *(rgb + 1) = data[i + w*j + 1];//G            *(rgb + 2) = data[i + w*j + 2];//R            rgb += 3;        }    }    //释放内存    free(dataBuf);    return 0;}

实验结果:rgb的位数为24

这里写图片描述

  • 补充说明
  • fopen()vs2013提示安全错误解决办法:打开文件的属性页,单击“预处理器”将“_CRT_SECURE_NO_WARNINGS”复制在“预处理器定义中’,然后确定再单击应用即可。
0 0
原创粉丝点击