5.MIL中视频加载、播放和保存

前面我们讲解了MIL图像的加载和保存,很多时候我们在做机器视觉时要求实现录像的功能,对此MIL提供了基本的支持。本文详解了MIL开发包中如何录制视频和播放视频等功能。

简单来说,一个视频文件就是一系列顺序排列的图像文件的集合,播放时读取视频文件中保存的帧数和帧速,按照一定的时间间隔顺序显示图像就表现为视频。MIL中对视频的支持就是基于这个原理提供了简单的支持,我们可以用MbufExportSequence函数将一系列图像保存到一个视频文件中,用MbufImportSequence从一个视频文件中中读取顺序排列的一系列图像。MIL没有提供视频播放的相关函数,我们可以按照一定的时间间隔顺序更新显示图像来模拟播放视频,实际上这也是实际播放视频的大致原理。

和图像文件一样,视频文件也有不同的格式,不同的格式决定了其保存的图像序列的组织关系,同一种视频格式也有区别,同一种格式视频的每一帧图像可能是不同格式的图像。

视频文件构成示意图如下:


MIL只支持AVI格式的视频,视频的每帧图像格式MIL只支持M_AVI_DIB、M_AVI_MIL、M_AVI_MJPG格式

1.一系列图像保存为AVI视频

当使用MbufExportSequence转存视频时,需要 指明每帧图像格式、待保存图像序列起始地址、总帧数、帧速。演示代码如下:
	MIL_ID	MilApplication	= M_NULL,  
			MilSystem		= M_NULL,       
			MilDisplay		= M_NULL,      
			MilBufferAvi[BUFFER_NUMBER]  = {M_NULL};

	/************************************************************************/
	/*分配默认的应用、系统、显示,注意默认的显示是显示在MIL内建的演示windows窗口*/
	/************************************************************************/
	MappAllocDefault(M_SETUP, &MilApplication, &MilSystem, &MilDisplay, M_NULL, M_NULL);

	int i;
	char szBuffer[256];

	/************************************************************************/
	/*读入图片*/
	/************************************************************************/
	printf("按<Enter>键读入图像同时模拟播放效果\n");
	getchar();
	for (i = 0; i < BUFFER_NUMBER; i++)
	{
		sprintf(szBuffer, ".\\images\\%d.bmp", i);
		MbufRestore(szBuffer, MilSystem, &MilBufferAvi[i]);
		
		//在读入同时模拟播放效果
		MdispSelect(MilDisplay, MilBufferAvi[i]);
		Sleep(500);
	}

	/************************************************************************/
	/*一次性将buffer转存为BMP AVI文件*/
	/************************************************************************/
	printf("按<Enter>键转存为1.AVI文件\n");
	getchar();
	MbufExportSequence("1.avi", M_AVI_DIB, M_NULL, M_NULL, M_NULL, M_OPEN);
	MbufExportSequence("1.avi", M_AVI_DIB, MilBufferAvi, BUFFER_NUMBER, 5, M_WRITE);
	MbufExportSequence("1.avi", M_AVI_DIB, M_NULL, M_NULL, M_NULL, M_CLOSE);

	/************************************************************************/
	/*每次只保存一个buffer转存为BMP AVI文件*/
	/************************************************************************/
	printf("按<Enter>键转存为2.AVI文件\n");
	getchar();
	MbufExportSequence("2.avi", M_AVI_DIB, M_NULL, M_NULL, M_NULL, M_OPEN);
	for (i= 0; i < BUFFER_NUMBER; i++)
	{
		MbufExportSequence("2.avi", M_AVI_DIB, &MilBufferAvi[i], 1, 5, M_WRITE);
	}
	MbufExportSequence("2.avi", M_AVI_DIB, M_NULL, M_NULL, M_NULL, M_CLOSE);

	/************************************************************************/
	/*一次性添加全部buffer到BMP AVI文件*/
	/************************************************************************/
	printf("按<Enter>键转存为3.AVI文件\n");
	getchar();
	MbufExportSequence("3.avi", M_AVI_DIB, MilBufferAvi, BUFFER_NUMBER, 5, M_APPEND);

	/************************************************************************/
	/*每次只添加一个buffer到BMP AVI文件*/
	/************************************************************************/
	printf("按<Enter>键转存为4.AVI文件\n");
	getchar();
	for (i= 0; i < BUFFER_NUMBER; i++)
	{
		MbufExportSequence("4.avi", M_AVI_DIB, &MilBufferAvi[i], 1, 5, M_APPEND);
	}

	/************************************************************************/
	/*一次性将buffer转存为JPG AVI文件,这里只演示一种情况,其他同上*/
	/************************************************************************/
	printf("按<Enter>键转存为5.AVI文件\n");
	getchar();
	MbufExportSequence("5.avi", M_AVI_MJPG, M_NULL, M_NULL, M_NULL, M_OPEN);
	MbufExportSequence("5.avi", M_AVI_MJPG, MilBufferAvi, BUFFER_NUMBER, 5, M_WRITE);
	MbufExportSequence("5.avi", M_AVI_MJPG, M_NULL, M_NULL, M_NULL, M_CLOSE);
这里我们首先读入5幅渐进改变的图片,这是一个小球往前平移的5帧图像如下(忽略CSDN的水印吧)
第一帧
第二帧
第三帧
第四帧
第五帧

我们在读入的过程中,通过间隔一段时间显示图片来模拟播放的效果,大家可以自行下载代码查看效果。这里是通过在for循环中插入Sleep语句实现停顿,每帧间间隔500ms。
导出的AVI视频测试可以用Windows Media Player或SPlayer打开,基本上常见的视频播放器应该都能够打开,这里我用格式工厂转成GIF格式图贴在这里
每帧图像为BMP的AVI

每帧图像为JPG的AVI

这里特别需要说明的是MIL可选择保存的每帧图像格式为M_AVI_DIB、M_AVI_MIL、M_AVI_MJPG:其中,M_AVI_DIB为对图片不压缩,保存的视频文件较大,Windows Media Player是支持的,在Splayer中发现混乱可能是解码器的原因;M_AVI_MIL这里没有演示,每帧图像格式为TIFF 6.0,不常用; M_AVI_MJPG对图片做了压缩,保存的视频文件较小,测试结果保存为BMP为377KB,保存为JPG为10K,压缩比非常大,但是这里由于为了展示都转成了GIF的原因,实际上用Windows Media Player播放器播放两个视频时会明显发现 前者比后者要清晰的多。同样这里存在内存存储方式和图像文件存储格式的一个匹配转换问题,以后这样的地方都存在同样的问题,不再赘述。
如果我们的内存足够大,我们可以把所有帧的图像都存在内存中,然后一次性存到视频文件中,然而对于实时采集到的大量数据往往内存是不够使的,这时候可以采用 每次采集一帧存入一帧的图像,再采集一帧图像存入一帧的方式来减少内存占用
MIL中保存视频文件和C语言操作文件类似,先打开(M_OPEN),操作(M_WRITE)完后,再关闭(M_CLOSE)。我们也可以使用添加的(M_APPEND)的方式,这样每次会自动打开、添加和关闭,但是这样每次操作均要打开和关闭,对性能有较大损耗。
推荐的录制方式为, 对于高速录制的视频,内存采集存储格式为M_DIB,保存为M_AVI_DIB格式视频,这样减少了转存次数,提高性能,至于保存的文件太大就用格式工厂转存一下吧,对于普通的录制,自由选择采集存储格式,保存为较小的M_AVI_MJPG格式视频。尽量少用M_APPEND方式。

2.读入和播放AVI

前面再导入视频帧所需要的图片时已经演示了视频播放的方式,这里读入AVI无非就是从一个固定的文件读入一系列图片和间隔更新显示以模拟播放。
演示代码如下:
	MIL_ID	MilApplication	= M_NULL,  
			MilSystem		= M_NULL,       
			MilDisplay		= M_NULL,      
			MilBufferAvi1[BUFFER_NUMBER] = {M_NULL},
			MilBufferAvi2	= M_NULL,
			MilBufferAvi3   = M_NULL;

	/************************************************************************/
	/*分配默认的应用、系统、显示,注意默认的显示是显示在MIL内建的演示windows窗口*/
	/************************************************************************/
	MappAllocDefault(M_SETUP, &MilApplication, &MilSystem, &MilDisplay, M_NULL, M_NULL);

	/************************************************************************/
	/*一次性的读入1.AVI文件,自动确定图像格式和分配buffer*/
	/************************************************************************/
	printf("按<Enter>键一次性的读入1.AVI文件,自动确定图像格式和分配buffer\n");
	getchar();
	long bufferNums = MbufDiskInquire("1.avi", M_NUMBER_OF_IMAGES, M_NULL);
	double frameRate = MbufDiskInquire("1.avi", M_FRAME_RATE, M_NULL);
	MbufImportSequence("1.avi", M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_OPEN);
	MbufImportSequence("1.avi", M_DEFAULT, M_RESTORE, MilSystem, MilBufferAvi1, 0, bufferNums, M_READ);
	MbufImportSequence("1.avi", M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_CLOSE);

	//播放刚刚加载的视频
	printf("按<Enter>键播放刚刚加载的视频\n");
	getchar();
	for (i= 0; i < bufferNums; i++)
	{
		MdispSelect(MilDisplay, MilBufferAvi1[i]);
		Sleep(long(1.0 / frameRate * 1000));
	}

	/************************************************************************/
	/*每次只读入一帧2.AVI文件并显示,自动确定图像格式和分配buffer*/
	/************************************************************************/
	printf("按<Enter>键每次只读入一帧2.AVI文件并显示\n");
	getchar();
	bufferNums = MbufDiskInquire("2.avi", M_NUMBER_OF_IMAGES, M_NULL);
	frameRate = MbufDiskInquire("2.avi", M_FRAME_RATE, M_NULL);
	MbufImportSequence("2.avi", M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_OPEN);
	for (i= 0; i < bufferNums; i++)
	{
		if (M_NULL != MilBufferAvi2)
		{
			MbufFree(MilBufferAvi2);
		}
		MbufImportSequence("2.avi", M_DEFAULT, M_RESTORE, MilSystem, &MilBufferAvi2, i, 1, M_READ);

		MdispSelect(MilDisplay, MilBufferAvi2);
		Sleep(long(1.0 / frameRate * 1000));
	}
	MbufImportSequence("2.avi", M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_CLOSE);

	/************************************************************************/
	/*预先分配好图像大小,一次性或每次读入一帧3.avi文件并显示,
	  这里只演示每次读取一帧并显示*/
	/************************************************************************/
	bufferNums = MbufDiskInquire("3.avi", M_NUMBER_OF_IMAGES, M_NULL);
	frameRate = MbufDiskInquire("3.avi", M_FRAME_RATE, M_NULL);
	long xWidth = MbufDiskInquire("3.avi",  M_SIZE_X, M_NULL);
	long yHeight = MbufDiskInquire("3.avi",  M_SIZE_Y, M_NULL);

	printf("按<Enter>键每次只读入一帧3.AVI文件并显示\n");
	getchar();
	MbufImportSequence("3.avi", M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_OPEN);
	for (i= 0; i < bufferNums; i++)
	{
		//预分配buffer
		if (M_NULL == MilBufferAvi3)
		{
			MbufAlloc2d(MilSystem, xWidth, yHeight, 16+M_UNSIGNED, M_DISP+M_IMAGE, &MilBufferAvi3);
		}

		MbufImportSequence("3.avi", M_DEFAULT, M_LOAD, M_NULL, &MilBufferAvi3, i, 1, M_READ);
		
		MdispSelect(MilDisplay, MilBufferAvi3);
		Sleep(long(1.0 / frameRate * 1000));
	}
	MbufImportSequence("3.avi", M_DEFAULT, M_NULL, M_NULL, M_NULL, M_NULL, M_NULL, M_CLOSE);
同样,如果你的PC内存足够的话,可以把AVI所有帧都读到内存中,然后间隔显示,对于较大 的视频文件显然这种方式实不可取的。实际上我们可以考虑,打开视频文件后, 每次读取一帧并显示然后再读取一帧显示,在实际视频播放器中均是这样做的,当然他们可能做了缓存,这是后话啦。在MIL中还有一个问题,是先分配一个对应视频文件的固定大小Buffer还是在加载过程中动态确定图像格式和大小动态分配加载,MIL提供了类似图像加载的M_LOAD和M_RESTORE选择, 使用M_RESORE一定要注意在下一次restore发生之前要把上一次自动分配的buffer释放掉
推荐的视频播放方法是, 预先分配一个图像buffer每次Load一帧图像并播放


这里要注意的是,本文是在同一个线程for循环中读入视频帧并播放,所以过程中并不好控制, 要想实现播放中暂停、继续等功能,需要使用多线程,这在下一节讲完MIL的实时采集后,我会放出一个笔者为实验室高速相机写的包含MIL 实时采集、图像保存、视频打开保存播放功能的小软件,坐等福利吧。

博客中完整演示代码 下载链接

原创,转载请注明来自 http://blog.csdn.net/wenzhou1219



  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值