前面我们讲解了MIL图像的加载和保存,很多时候我们在做机器视觉时要求实现录像的功能,对此MIL提供了基本的支持。本文详解了MIL开发包中如何录制视频和播放视频等功能。
简单来说,一个视频文件就是一系列顺序排列的图像文件的集合,播放时读取视频文件中保存的帧数和帧速,按照一定的时间间隔顺序显示图像就表现为视频。MIL中对视频的支持就是基于这个原理提供了简单的支持,我们可以用MbufExportSequence函数将一系列图像保存到一个视频文件中,用MbufImportSequence从一个视频文件中中读取顺序排列的一系列图像。MIL没有提供视频播放的相关函数,我们可以按照一定的时间间隔顺序更新显示图像来模拟播放视频,实际上这也是实际播放视频的大致原理。
和图像文件一样,视频文件也有不同的格式,不同的格式决定了其保存的图像序列的组织关系,同一种视频格式也有区别,同一种格式视频的每一帧图像可能是不同格式的图像。
视频文件构成示意图如下:
MIL只支持AVI格式的视频,视频的每帧图像格式MIL只支持M_AVI_DIB、M_AVI_MIL、M_AVI_MJPG格式。
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的水印吧)
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释放掉。