实验二 BMP2YUV文件转换

来源:互联网 发布:c语言memset头文件 编辑:程序博客网 时间:2024/05/17 08:10

一、实验原理

研究各种图像文件格式之间的转换,主要可以从两方面入手:1.从其中一种文件格式出发,考虑图像数据在该种文件格式下的内部存储方式。如:图像数据采用的色彩空间是什么?它存储在文件数据流的哪个部分?该格式除了实际的图像数据,是否还有其它信息数据?它们又存储在哪里?2.寻找两种文件格式之间的关系,以及它们之间的转换方法。关系如文件格式之间组织结构的异同,转换方法取决于色彩空间之间的转换公式。
从上述两方面来分析比较本次实验待转换的两种文件格式——bmp、yuv。

  1. bmp

    bmp(全称Bitmap)是Windows操作系统中的标准图像文件格式。BMP文件的图像深度可选1bit、4bit、8bit、16bit及24bit。典型的BMP图像文件由四部分组成:
    (1)位图头文件数据结构,它包含BMP图像文件的类型、显示内容等信息;
    (2)位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;在转换成yuv时,bmp图像的宽、高需为4的倍数。
    (3)调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
    (4)位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在 24 位图中直接使用RGB,而其他的小于 24 位的使用调色板中颜色索引值。bmp的实际图像数据是以自下而上、自左向右的扫描方式存放的。
    以一幅24bit的bmp(无调色板)为例,以二进制方式打开它,观察内部数据结构:
    24bit的bmp图像文件
    图中以不同颜色标示出了文件头和信息头部分,以及其中包含的几个重要的数据。注意计算机为小端系统,有先进后出的准则,在读取时要注意顺序。如图中图像的宽应读作(000002A8)B=680。
    以下给出bmp的文件头、信息头、调色板中应包含的所有数据(已包含在windows.h库里,实际操作时要记得包含该库):

typedef struct tagBITMAPFILEHEADER {        WORD   bfType;     /* 说明文件的类型  */        DWORD  bfSi;  /* 说明文件的大小,用字节为单位 */                                                            WORD   bfReserved1;   /* 保留,设置为0 */        WORD   bfReserved2;   /* 保留,设置为0 */        DWORD  bfOffBits; /* 说明从BITMAPFILEHEADER结                     构开始到实际的图像数据之间的字节偏移量 */}   BITMAPFILEHEADER;   /* 文件头 */
typedef struct tagBITMAPINFOHEADER {         DWORD  biSize;    /* 说明结构体所需字节数 */        LONG   biWidth; /* 以像素为单位说明图像的宽度 */        LONG   biHeight; /*以像素为单位说明图像的高速 */        WORD   biPlanes;   /* 说明位面数,必须为1 */        WORD   biBitCount; /*说明位数,1、2、4、8、24*/        DWORD  biCompression;  /* 说明图像是否压缩及    压缩类型BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS */        DWORD  biSizeImage;    /*  以字节为单位说明图像                               大小 ,必须是4的整数倍*/        LONG   biXPelsPerMeter;    /*  目标设备的水平分                                    辨率,像素/米 */        LONG   biYPelsPerMeter;    /*目标设备的垂直分辨                                    率,像素/米 */        DWORD  biClrUsed;  /* 说明图像实际用到的颜色数,                 如果为0则颜色数为2的biBitCount次方 */        DWORD  biClrImportant;  /*说明对图像显示有重要             影响的颜色索引的数目,如果是0,表示都重要 */}  BITMAPINFOHEADER;  /* 信息头 */
typedef struct tagRGBQUAD {        BYTE  rgbBlue;           /*指定蓝色分量*/       BYTE  rgbGreen;        /*指定绿色分量*/       BYTE  rgbRed;            /*指定红色分量*/       BYTE  rgbReserved;   /*保留,指定为0*/}  RGBQUAD;  /* 调色板 */
  1. yuv
    yuv是一种简单粗暴的格式,它不像bmp需要诸多相关信息放在文件里给定义,就直接叭叭叭的yyyyuuvv……写下去了。有关yuv格式的其它内容(与RGB的转换、采样等)已在实验一中有所体现,在此不再赘述。

二、流程分析

1.bmp2yuv(24bit无调色板)的工程除头文件外共包含了三个cpp文件:main.cpp、bmp2yuv.cpp、rgb2yuv.cpp(实验一中的文件直接调用即可)。

流程分析

2.writeYUVvideo。完成bmp到yuv图片的转换后,再依次抽取这些yuv数据写入一个新的yuv数据流中,可以实现将生成的yuv图片拼接成一个200帧的yuv视频文件。

三、关键代码及其分析

main.cpp

#include <stdio.h>#include <stdlib.h>#include <malloc.h>#include <windows.h>  #include <math.h>#include "bmp2yuv.h"#define u_int8_t    unsigned __int8#define u_int       unsigned __int32#define u_int32_t   unsigned __int32int main(int argc, char** argv){    unsigned int i;    char* bmpFileName = NULL;    char* yuvFileName = NULL;    FILE *bmpFile = NULL;    FILE *yuvFile = NULL;    u_int8_t* bmpBuf = NULL;    u_int8_t* yBuf = NULL;    u_int8_t* uBuf = NULL;    u_int8_t* vBuf = NULL;    BITMAPFILEHEADER File_header;//文件头    BITMAPINFOHEADER Info_header;//信息头    //初始化    bmpFileName = argv[1];    yuvFileName = argv[2];    bmpFile = fopen(bmpFileName, "rb");    if (bmpFile == NULL)    {        printf("cannot find bmp file\n");        exit(1);    }    else    {        printf("The input bmp file is %s\n", bmpFileName);    }    yuvFile = fopen(yuvFileName, "wb");    if (yuvFile == NULL)    {        printf("cannot find yuv file\n");        exit(1);    }    else    {        printf("The output yuv file is %s\n", yuvFileName);    }    //  read file & info header    if(fread(&File_header,sizeof(BITMAPFILEHEADER),1,bmpFile) != 1)    {        printf("read file header error!");        exit(0);    }    if (File_header.bfType != 0x4D42)//4D42='BM'    {        printf("Not bmp file!");        exit(0);    }    else    {        //printf("this is a %s\n",itoa(File_header.bfType));    }    if(fread(&Info_header,sizeof(BITMAPINFOHEADER),1,bmpFile) != 1)    {           printf("read info header error!");        exit(0);    }    //  end read header    //24bit暂不用调色板    /*RGBQUAD *pRGB = (RGBQUAD *)malloc(sizeof(RGBQUAD)*(unsigned char)pow(2,Info_header.biBitCount));    if(!MakePalette(pFile,file_h,info_h,pRGB))        printf("No palette!");*/    //开辟缓冲区    bmpBuf=(unsigned char *)malloc(Info_header.biWidth*Info_header.biHeight*3);    yBuf=(unsigned char *)malloc(Info_header.biWidth*Info_header.biHeight);    uBuf=(unsigned char *)malloc(Info_header.biWidth*Info_header.biHeight/4);    vBuf=(unsigned char *)malloc(Info_header.biWidth*Info_header.biHeight/4);    //读取bmp数据,调用bmp2yuv函数进行转换    while(fread(bmpBuf,Info_header.biWidth*Info_header.biHeight*3,1,bmpFile))    {        if(BMP2YUV(bmpBuf,Info_header.biWidth,Info_header.biHeight, yBuf, uBuf, vBuf))        {            printf("error");            return 0;        }        for (i = 0; i < Info_header.biWidth*Info_header.biHeight; i++)        {            if (yBuf[i] < 16) yBuf[i] = 16;            if (yBuf[i] > 235) yBuf[i] = 235;        }        for (i = 0; i < Info_header.biWidth*Info_header.biHeight/4; i++)        {            if (uBuf[i] < 16) uBuf[i] = 16;            if (uBuf[i] > 240) uBuf[i] = 240;            if (vBuf[i] < 16) vBuf[i] = 16;            if (vBuf[i] > 240) vBuf[i] = 240;        }        //写入yuv数据        fwrite(yBuf, 1, Info_header.biHeight * Info_header.biWidth, yuvFile);        fwrite(uBuf, 1, (Info_header.biHeight * Info_header.biWidth) / 4, yuvFile);        fwrite(vBuf, 1, (Info_header.biHeight * Info_header.biWidth) / 4, yuvFile);    }    /* cleanup */    free(bmpBuf);    free(yBuf);    free(vBuf);    free(uBuf);    fclose(bmpFile);    fclose(yuvFile);    return 0;}

bmp2yuv.cpp

#include <stdlib.h>#include <windows.h> #include "bmp2yuv.h"int BMP2YUV (void *bmp,long width,long height, void *y_out, void *u_out, void *v_out){    //由于24bit的bmp没有调色板,所以从main函数传递进来的*bmp里就是bgr数据,直接调用rgb2yuv函数即可    RGB2YUV(width,height,bmp,y_out,u_out,v_out,0);    return 0;}

writeYUVvideo.cpp

#include <stdio.h>#include <stdlib.h>#include <malloc.h> #define u_int8_t    unsigned __int8int main(int argc, char** argv){    int i,j,k;    int width,height,n;    char* inFileName = NULL;    char* outFileName = NULL;    FILE *inFile = NULL;    FILE *outFile = NULL;    u_int8_t* outBuf = NULL;    u_int8_t* inBuf = NULL;    /*通过命令行传入的参数依次为:宽、高、yuv1、yuv1所占帧数、yuv2、yuv2所占帧数、……、yuv5、yuv5所占帧数、输出yuv*/    width = atoi(argv[1]);    height = atoi(argv[2]);    outFileName=argv[13];    outFile = fopen(outFileName, "wb");    if (outFile == NULL)    {        printf("cannot find yuv file\n");        exit(1);    }    else    {        printf("The output yuv file is %s\n", outFileName);    }    /*开辟缓冲区*/    inBuf=(unsigned char *)malloc(width*height*3/2);    outBuf=(unsigned char *)malloc(200*width*height*3/2);    unsigned char *out;/*out指针指向当前操作的outBuf内容*/    for(i=0;i<10;i+=2)    {           /*循环读入输入的yuv文件及其帧数*/        inFileName=argv[i+3];        n=atoi(argv[i+4]);        inFile = fopen(inFileName, "rb");        if (inFile == NULL)        {            printf("cannot find yuv file\n");            exit(1);        }        else        {            printf("The input yuv file is %s\n", inFileName);        }        fread(inBuf,width*height*3/2,1,inFile);        for(j=0;j<n;j++)        {            out=outBuf+i*n*width*height*3/2/2+j*width*height*3/2;/*out的偏移量为之前所有的yuv所占帧的数据长度+当前yuv已写入的数据长度*/            for(k=0;k<width*height*3/2;k++)            {                *out=*(inBuf+k);                out++;            }        }    }    fwrite(outBuf, 1, 200*width*height*3/2, outFile);    /* cleanup */    fclose(inFile);    fclose(outFile);    free(inBuf);    free(outBuf);    return 0;}

四、实验结论

  1. 对比同一幅图像在宽、高不同(一幅的宽、高都是4的倍数,另一幅的都不是)时的转换结果
    1.yuv(680*380)
    1_error.yuv(670*374)
    可见若bmp的宽、高不是4的倍数,转换后的yuv图像会有变形错位。
  2. 将5幅bmp图先转换成yuv,再通过writeYUVvideo完成图像流到视频流的写入。
    命令行参数(设置为每个yuv图像重复40帧)
    随机截取生成的yuv视频流中的五幅图像:
    p1
    p2
    p3
    p4
    p5
0 0