实验二 图像文件的读写和转换(AVI转YUV)

  本实验中 AVI 文件读取的示例比较简单,流程如下

Created with Raphaël 2.1.0 打开文件 得到数据流 得到帧,取出RGB数据 RGB转YUV 清理内存

  通过调用几个库函数实现每一帧的读取。这里不去分析其复杂的文件头结构,仅说明如何读取AVI文件中的视频信息。整个过程中使用的几个函数和数据类型如下表

存储的数据数据类型读取方式
文件指针 aviFileIAVIFile* (或PAVIFile)AVIFileOpen
数据流 aviStreamIAVIStream* (或PAVIStream)AVIFileGetStream
数据流中的信息 strInfoAVISTREAMINFOAVIStreamInfo
视频流中的信息 info_hBITMAPINFOHEADERAVIStreamReadFormat
帧指针 pgfPGETFRAMEAVIStreamGetFrameOpen
一帧画面的指针 frameDataBITMAPINFO*AVIStreamGetFrame
帧的颜色数据 colorDataunsigned char*frameData->bmiColors


//--------main_avi2yuv.cpp--------
#include <windows.h>
#include <vfw.h>
//其它头文件等略过
...
char* aviFileName = argv[1];
char* yuvFileName = argv[2];
FILE* yuvFile = fopen(yuvFileName, "wb");
//对输出文件打开的检查略过
...
AVIFileInit();//初始化AVI类库
IAVIFile* aviFile;//AVI文件指针
IAVIStream* aviStream;//AVI数据流指针
//打开视频文件和输入数据流
if (AVIFileOpen(&aviFile, aviFileName, OF_READ, NULL))
...
if (AVIFileGetStream(aviFile, &aviStream, streamtypeVIDEO, 0))
...

  读取AVI文件时要使用库 vfw.h ,这个库针对需要用到视频的程序提供了接口。使用时不仅要包含这个库,还要对项目属性进行设置。打开项目属性页->配置属性->链接器->输入->附加依赖项,加入 vfw32.lib,否则编译时会报错。如图1所示。

这里写图片描述
图1 链接库的加入
  包含好库之后,对于AVI文件就不能像之前那样简单地使用 fopen 函数打开。首先需要使用 AVIFileInit 函数进行初始化,否则AVI文件不会被读取。AVI文件要放在 IAVIFile* 类型中,这是一个指向 AVI 文件的指针。对于视频文件中的视频流和音频流,有专门的数据类型 IAVIStream* 指向这些数据流。
  创建好这些变量之后就可以使用 AVIFileOpen 函数打开文件了。使用这些函数的时候注意查看函数原型,参数是传递变量本身还是地址值,如图2。
这里写图片描述
图2 AVIFileOpen 函数原型
  以 AVIFileOpen 为例,第一个参数为 PAVIFILE* 类型,为一个指向 PAVIFILE 类型的指针,而 PAVIFILE 相当于 IAVIFile*,因此这里要使用 aviFile 的地址做参数。后面两个参数使用固定的值。
  打开文件之后,使用 AVIFileGetStream 函数,将第三个参数设为 streamtypeVIDEO 以打开视频流。这些函数如果没有正确地读取文件信息会返回非 0 值,判断一下是否成功读取从而方便调试,if 语句内部即不成功时的输出等略过,后面的代码也采取这样的方式。
  另外需要说明一点的是,AVIFileOpen 函数中第二个参数这里使用了 char* 类型,为了能让这个函数正确读取这个字符串而不报错,需要打开项目属性页->配置属性->常规->字符集,选择“使用多字节字符集”,如图3所示。
这里写图片描述
图3 字符集的设置

//读取流的格式和信息
AVISTREAMINFO strInfo;//AVI数据流信息
BITMAPINFOHEADER info_h;//BMP信息头
LONG lStreamSize = sizeof(info_h);//数据流的大小
if (AVIStreamReadFormat(aviStream, 0, &info_h, &lStreamSize))
...
if (AVIStreamInfo(aviStream, &strInfo, sizeof(strInfo)))
...
//读取流的起始位置、长度和宽高
int start = AVIStreamStart(aviStream);
int len = AVIStreamLength(aviStream);
int width = info_h.biWidth;
int height = info_h.biHeight;
printf("The input file is %d x %d,%d frames @ %d Hz.\n", 
    width, height, len, strInfo.dwRate / strInfo.dwScale);

  打开了视频流之后,就要从视频流中获取视频的一些基本信息。视频流信息使用 AVISTREAMINFO 类型。使用 BMP 文件的结构保存每一帧的信息,因此定义一个 BITMAPINFOHEADER 类型的信息头。从视频流中通过 AVIStreamStart 和 AVIStreamLength 函数获取视频流的起始位置和长度。

//为了简单,设每一帧都是24位R8G8B8的图像
info_h.biBitCount = 24;
info_h.biClrImportant = 0;
info_h.biClrUsed = 0;
info_h.biCompression = BI_RGB;
info_h.biHeight = height;
info_h.biPlanes = 1;
info_h.biSize = sizeof(info_h);
info_h.biWidth = width;
info_h.biXPelsPerMeter = 0;
info_h.biYPelsPerMeter = 0;
info_h.biSizeImage = width*height * 3;

  AVI 文件的量化位数也像 BMP 那样有 8、16、24、32 位等,这里仅仅对常见的 24 位的 AVI 文件进行读取操作。

//得到帧指针
PGETFRAME pgf = AVIStreamGetFrameOpen(aviStream, &info_h);
//缓冲区建立略过
...
for (i = 0; i < len; i++)//len为视频流的长度,也就是有多少帧视频
{
    BITMAPINFO* frameData = (BITMAPINFO*)AVIStreamGetFrame(pgf, i + start);//从视频流中取出一帧画面
    if (frameData == NULL)
        printf("Frame %d of %d failed.\n", i, len);//如果画面为空则提示
    unsigned char* colorData = (unsigned char*)frameData->bmiColors;//每一帧中的颜色数据
    if (frameData->bmiHeader.biWidth != width || frameData->bmiHeader.biHeight != height)//检查宽高是否一致
    ...
    if (frameData->bmiHeader.biCompression != BI_RGB)//检查是否为RGB类型
    ...

  使用 AVIStreamGetFrameOpen 函数从视频流中得到指向起始帧的指针 pgf,然后使用 AVIStreamGetFrame 函数循环地取出每一帧画面,帧数据中的 bmiColors 属性为颜色数据。

    //由颜色指针获取RGB值
    for (j = 0; j < height*width; j++)
    {
        *(rgbBuf + 3 * j) = *(colorData + 3 * j);//B
        *(rgbBuf + 3 * j + 1) = *(colorData + 3 * j + 1);//G
        *(rgbBuf + 3 * j + 2) = *(colorData + 3 * j + 2);//R
    }
    //RGB2YUV
    if (RGB2YUV(width, height, rgbBuf, yBuf, uBuf, vBuf, 0))
    //对YUV文件中超出16~240范围的值进行处理,写入文件,关闭缓冲区等略过
    ...
}
AVIFileRelease(aviFile);//释放AVI文件指针
...

  得到颜色数据之后就可以依次获取帧中每一个像素的R,G,B值,写入到 rgbBuf 缓冲区中,再使用实验一中的 RGB2YUV 函数转成 YUV 数据,写入到文件中。最后的实验结果如下

AVI文件YUV文件的第一帧YUV文件的最后一帧
这里写图片描述
这里写图片描述

这里写图片描述
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值