RGB数据写bmp图片文件遇到的这些坑

用ffmpeg解码MP4后,得到YUV数据,然后YUV转换成RGB.

没隔一段时间需要保存一张图片,这里就打算直接将RGB数据写成BMP文件。

因为BMP文件是直接写RGB数据,并不编码,减少CPU的损耗。

就这么一个功能,遇到了好多坑。


一,如何用RGB写BMP文件

BMP文件的图片数据是原始RGB数据,不需要编码。但是需要在文件最开始打上BMP文件头,后面直接写RGB的数据就可以了。

BMP文件头信息如下:

typedef struct {
        WORD    bfType;//0x4d42
        DWORD   bfSize;//sizeof(BMPFILEHEADER_T)+sizeof(BMPINFOHEADER_T)+width*height*3, 整个文件的大小
        WORD    bfReserved1;
        WORD    bfReserved2;
        DWORD   bfOffBits;//sizeof(BMPFILEHEADER_T)+sizeof(BMPINFOHEADER_T),就是RGB数据的偏移量, 偏移这两个数据结构
} BMPFILEHEADER_T;


typedef struct{
        DWORD      biSize;//sizeof(BMPINFOHEADER_T)
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;//RGB24, 就是24位
        DWORD      biCompression;//0, 不压缩
        DWORD      biSizeImage;//RGB24数据大小
        LONG       biXPelsPerMeter;//
        LONG       biYPelsPerMeter;//
        DWORD      biClrUsed;//0
        DWORD      biClrImportant;//0
} BMPINFOHEADER_T;

也就是说,整个BMP文件就是:BMPFILEHEADER_T+BMPINFOHEADER_T+RGB原始数据。简单吧。

然后直接写文件就可以了:

bmpheader.bfType = 0x4d42;  
    bmpheader.bfReserved1 = 0;  
    bmpheader.bfReserved2 = 0;  
    bmpheader.bfOffBits = <span style="font-family: Arial, Helvetica, sans-serif;">sizeof(BMPFILEHEADER_T) </span>+ sizeof(BMPINFOHEADER_T);  
    bmpheader.bfSize = bmpheader.bfOffBits + width*height*bpp/8;  
  
    bmpinfo.biSize = sizeof(BMPINFOHEADER_T);  
    bmpinfo.biWidth = width;  
    bmpinfo.biHeight = -height;  
    bmpinfo.biPlanes = 1;  
    bmpinfo.biBitCount = bpp;  
    bmpinfo.biCompression = 0;  
    bmpinfo.biSizeImage = (width*bpp+31)/32*4*height;  
    bmpinfo.biXPelsPerMeter = 100;  
    bmpinfo.biYPelsPerMeter = 100;  
    bmpinfo.biClrUsed = 0;  
    bmpinfo.biClrImportant = 0;  

    fwrite(&bmpheader, sizeof(BMPFILEHEADER_T), 1, fp);  
    fwrite(&bmpinfo, sizeof(bmpinfo), 1, fp);  
    fwrite(pFrameRGB, width*height*bpp/8, 1, fp);  

    fclose(fp);  


二,坑一:4字节对齐

按照上面的代码写出来的文件,文件图片打开失败,说文件破损。

后来才发现BMPFILEHEADER_T这个数据接口的内部变量排序,并没有4字节对齐,导致sizeof(BMPFILEHEADER_T)比预想的大!

所以直接用宏设置数据结构的字节对齐方式为:1字节对齐。

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

typedef struct{
        DWORD      biSize;
        LONG       biWidth;
        LONG       biHeight;
        WORD       biPlanes;
        WORD       biBitCount;
        DWORD      biCompression;
        DWORD      biSizeImage;
        LONG       biXPelsPerMeter;
        LONG       biYPelsPerMeter;
        DWORD      biClrUsed;
        DWORD      biClrImportant;
} BMPINFOHEADER_T;
#pragma pack()
这些好了,程序运行图片可以打开了,但是还有另外一个坑。

现在图片可以打开,但是人物的颜色都是绿色,都变成“绿巨人”


三、坑二:图片颜色不对--RGB24还是BRG24?

YUV转RGB用的是ffmpeg的swscale代码:

_swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,   
        pCodecCtx->width, pCodecCtx->height, <span style="background-color: rgb(51, 51, 255);">AV_PIX_FMT_RGB24</span>, SWS_BICUBIC, NULL, NULL, NULL);
YUV转换的结果是RGB24,但是这样出来的RGB数据,写成的BMP文件,显然是颜色错误。

修改成BRG24问题得到解决,如下:

_swsContext = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,   
        pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_BGR24, SWS_BICUBIC, NULL, NULL, NULL);


今后还是需要确认一下BMP文件内部不压缩图片的格式是BGR24,而不是RGB24.





发布了21 篇原创文章 · 获赞 18 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/sweibd/article/details/52516123