计算机视觉课的作业,挺有意思的,记录一下。

一 问题描述

对输入的一个彩色视频与3张以上照片,用OpenCV实现以下功能要求:
1. 将输入视频vi与多张图片pics处理成相同长宽后,合在一起生成一个视频vo;
2. 图片pics合成到视频中时需要编程实现图片切换效果,如幻灯片中的渐入、飞入等;
3. 在新视频中vo中需要完全编程实现一段片头,如编程绘制一个动图;
4. 最后以输入视频vi的两倍播放输出视频vo,并在视频底部打上含自己姓名的字幕。

二 实现过程

第1步:构造输出视频

首先我们需要确定最终输出视频的参数信息,包括视频的长宽、帧率、编码等信息,这里我们使用输入视频的参数来确定输出视频vo的参数信息。代码如下:

VideoCapture cap = VideoCapture("in.avi");
double fps = cap.get(CV_CAP_PROP_FPS);
Size size = Size(cap.get(CV_CAP_PROP_FRAME_WIDTH), cap.get(CV_CAP_PROP_FRAME_HEIGHT));
VideoWriter writer;
writer.open("out.avi", CV_FOURCC('M', 'J', 'P', 'G'), fps*2, size);

初始化VideoCapture实例读入输入视频”in.avi”,这里的输入视频是截取的蓝色星球2的前600帧制作的,然后使用VideoCapture的get方法分别获取视频流的帧率fps和图像大小size,有了这些参数信息我们就可以使用VideoWriter的open方法来构造输出视频了,“out.avi”指定了视频的文件名,CV_FOURCC(‘M’, ‘J’, ‘P’, ‘G’)指定了视频的编码格式,fps*2指定了视频的帧率(乘以2是因为输出视频需要以两倍原视频的速度播放,因此我们设置输出视频的帧率为输入视频帧率的两倍即可),size指定了视频的长宽。构造出一个空的视频之后,接下来的工作就只需要按要求将一个个的帧插入到视频中就可以了。输入视频:

第2步:制作片头

因为刚开始接触OpenCV,啥高大上的技术也不会,片头这部分我就是参考OpenCV的例子程序画个旋转的原子图了。在图中有一个不动的原子核和四个围绕原子核旋转的电子,简单起见四个电子旋转时投影在二维平面上的大小没有做变化,虽然这样做看上去也就没有什么立体的效果了(后期可以再优化一下)。有了想法之后实现起来就比较容易了,主要就是在电子的旋转路径上依次把电子画出来就好了,而电子的旋转路径在二维平面看上去也就是围绕在原子核周边的一个个椭圆了。具体代码如下所示:

void rotateAtom2(Size size) {cout << "rotateAtom2..." << endl;Point center;       // 原子的中心位置center.x = size.width / 2;center.y = size.height / 2;Size axes;          // 椭圆的半长轴和半短轴大小axes.width = min(size.width, size.height) / 4;axes.height = min(size.width, size.height) / 16;int radius = min(size.width, size.height) / 32;             // 原子核的半径大小int lineType = 8;for (int i = 0; i < 720; i++) {Mat img = Mat(size.height, size.width, CV_8UC3, Scalar(0, 0, 0));           // 初始化一个背景为黑色的帧circle(img, center, radius, Scalar(0, 0, 255), -1, lineType);               // 画原子核:在center位置画一个半径为radius的圆ellipse(img, center, axes, 0, 0, 360, Scalar(255, 0, 0), 1, lineType);      // 画四个电子的旋转路径:一个椭圆ellipse(img, center, axes, 45, 0, 360, Scalar(255, 0, 0), 1, lineType);ellipse(img, center, axes, 90, 0, 360, Scalar(255, 0, 0), 1, lineType);ellipse(img, center, axes, 135, 0, 360, Scalar(255, 0, 0), 1, lineType);Point p0(center.x + axes.width * cos(i*PI / 180), center.y + axes.height * sin(i*PI / 180));            // 电子0的位置//cout << "p0:x " << p0.x << ",y " << p0.y << endl;Point p = Point(center.x + axes.width * cos((i+30)*PI / 180), center.y + axes.height * sin((i+30)*PI / 180));       // 用来计算电子123位置的点(不同点的起始位置不同)Point p1((p.x - center.x)*cos(45 * PI / 180) - (p.y - center.y)*sin(45 * PI / 180) + center.x, (p.y - center.y)*cos(45 * PI / 180) + (p.x - center.x)*sin(45 * PI / 180) + center.y);       // 电子1的位置(p按center顺时针旋转45度后的位置)p = Point(center.x + axes.width * cos((i + 60)*PI / 180), center.y + axes.height * sin((i + 60)*PI / 180));Point p2((p.x - center.x)*cos(90 * PI / 180) - (p.y - center.y)*sin(90 * PI / 180) + center.x, (p.y - center.y)*cos(90 * PI / 180) + (p.x - center.x)*sin(90 * PI / 180) + center.y);p = Point(center.x + axes.width * cos((i + 90)*PI / 180), center.y + axes.height * sin((i + 90)*PI / 180));Point p3((p.x - center.x)*cos(135 * PI / 180) - (p.y - center.y)*sin(135 * PI / 180) + center.x, (p.y - center.y)*cos(135 * PI / 180) + (p.x - center.x)*sin(135 * PI / 180) + center.y);circle(img, p0, radius / 2, Scalar(0, 255, 0), -1, lineType);           // 按照电子的位置分别将电子画出来circle(img, p1, radius / 2, Scalar(0, 255, 0), -1, lineType);circle(img, p2, radius / 2, Scalar(0, 255, 0), -1, lineType);circle(img, p3, radius / 2, Scalar(0, 255, 0), -1, lineType);//waitKey(25);//imshow("in", img);drawImg(img);           // 将该帧写入到输出视频中}
}

稍微麻烦一点的是计算电子的位置,因为好久没碰数学了,还是在网上找的公式自己推了一遍。。。需要注意的是计算机视觉里的坐标与常用的坐标位置不太一样。最后做出来的效果还算凑活,不足之处是电子旋转的不是很流畅,一方面是因为帧数低,另一方面是因为轨迹的坐标有一点误差。
这里的drawImg()是把VideoWriter的write()封装了一下,在这个方法中多了一步将字幕写入到帧中的过程,后文会简单介绍。效果预览:

第3步:插入图片并实现切换效果

在做好片头之后,接下来我们来做图片切换。因为一开始对如何实现毫无头绪,所以看了一下别人的代码[3],总结出来切换效果其实就是按照一定规则将下张图片中的像素点画在上张图片上。这里我就简单实现了一种百叶窗载入效果和随机方块载入效果,代码如下:

void randTransition(Mat pic0, Mat pic1) {           // 随机方块载入cout << "randTransition..." << endl;int x_max = pic0.cols / 48, y_max = pic0.rows / 27;// 将图片分为x_max*y_max个方块,每个方块大小为27*48int cnt = x_max * y_max;                        int* a = new int[cnt];                          // a[i]用来存放第i次载入的方块位置for (int i = 0; i < cnt; i++) {a[i] = i;}shuffle(a, a + cnt, default_random_engine());   // 打乱载入顺序for (int r = 0; r < cnt; r++) {//cout << r << endl;int y = a[r] / x_max, x = a[r] % x_max;     // (x, y)为第r次载入方块的位置for (int i = y*27; i < (y+1)*27; i++) {     // 将pic1(x, y)处的方块写入到pic0中的相同位置uchar* data = pic0.ptr<uchar>(i);uchar* p1 = pic1.ptr<uchar>(i);for (int j = x*48; j < (x+1)*48; j++) {data[3 * j] = p1[3 * j];data[3 * j + 1] = p1[3 * j + 1];data[3 * j + 2] = p1[3 * j + 2];}}drawImg(pic0);                              // 将写入一个pic1方块的pic0写入到视频中}
}void blindTransition(Mat pic0, Mat pic1) {          // 百叶窗载入cout << "blindTransition" << endl;int x_max = pic0.cols, y_max = pic0.rows;int y_step = y_max / 5;                         // 每次载入5行,每行相距y_step行for (int r = 0; r < y_step; r++) {              // 共需载入y_step次for (int y = r; y < y_max; y += y_step) {   // 载入5行pic1uchar* data = pic0.ptr<uchar>(y);       // 取pic0第y行像素点的首地址uchar* p1 = pic1.ptr<uchar>(y);         // 取pic1第y行像素点的首地址for (int x = 0; x < x_max; x++) {       // 将pic1第y行像素点依次写入到pic0的第y行中data[3 * x] = p1[3 * x];            // 因为是三通道data[x * 3 + 1] = p1[x * 3 + 1];data[x * 3 + 2] = p1[x * 3 + 2];}}for (int i = 0; i < 3; i++) {drawImg(pic0);                          // 将新写入5行pic1的pic0写入到视频中}}
}

需要注意的是,在将图片插入到视频之前,我们需要先将图片处理成与视频相同的长宽,这里可以使用OpenCV的resize()来完成这个工作,代码如下:

Mat toSize(Size size, Mat out) {Mat tmp(size, CV_8UC3);    // 构造一个空的输出图像resize(out, tmp, size);    // 将out调整到size大小输出到tmp中return tmp;
}

将图片处理成相同大小后,我们就可以把图片放到上述的blindTransition()中,完成插入图片和切换效果了。效果预览:

第4步:插入原视频vi

这一步应该是最简单的了,我们只需要把之前载入的输入视频cap的每一帧依次插入到输出视频中就可以了,代码如下:

while (1) {cap >> frame;if (frame.empty())break;//imshow("in", frame);drawImg(frame);//cvWaitKey(fps);
}

第5步:添加字幕

添加字幕也比较简单,直接用OpenCV的putText()就可以了,如果要添加汉字的话还需要一些额外的操作,我这里用的是freetype完成的,代码如下:

void drawImg(Mat img) {Cv310Text text("simkai.ttf");           // 读入字体文件Size textsize = getTextSize("姓名:jyakaranda(网易云)  学号:12345678  手机号:12345678", FONT_HERSHEY_PLAIN, 1, 1, 0);Point org((img.cols - textsize.width) / 2, (img.rows - textsize.height) / 8 * 7);   // 字幕位置text.putText(img, "姓名:jyakaranda(网易云)  学号:12345678  手机号:12345678", org, Scalar(255, 255, 255)); // 将字幕写入到帧中//waitKey(25);//imshow("out", img);writer << img;                          // 将写入字幕的帧写入到视频中
}

总结

虽然有些粗糙,但至此我们也算是完成了最开始的各种要求了:编程画了一个旋转的原子图作为视频片头,做了一个图片的随机载入和百叶窗载入效果,将输入视频插入到输出视频并在视频中添加中文字幕。学习了使用OpenCV对视频的基本io操作以及对图像的简单处理,过程还是比较简单有趣的。

参考资料:

  1. OpenCV Installation in Windows
  2. OpenCV入门教程之一
  3. OpenCV实现图片的时钟和中心圆形扩大效果
  4. OpenCV drawing1例子程序
  5. VS2010中OpenCV 显示汉字
  6. 学习OpenCV

源码及输入输出:http://download.csdn.net/download/u013794793/10156392

OpenCV视频生成相关推荐

  1. OpenCV 视频与图片序列相互转换 VideoWriter生成视频流

    OpenCV学习笔记(四十七)--VideoWriter生成视频流highgui http://blog.csdn.net/yang_xian521/article/details/7440190 标 ...

  2. C++ OpenCV视频操作之图像输出文字

    前言 当我们视频分析时可能图像中需要有一个文字说明,OpenCV中本身也有这个API,我们就来看看OpenCV中的图像文字输出. 视频效果 函数API void putText( Mat& i ...

  3. 图像零交叉点,视频生成,视频识别,视频摘要,视频浓缩

    图像零交叉点,视频生成,视频识别,视频摘要,视频浓缩 一.视频生成,视频识别,视频摘要,视频浓缩 视频生成与视频识别 视频分析的两大任务,前者侧重于对下一帧的预测,而前者则侧重于视频内容的理解.由于视 ...

  4. 图像、视频生成大一统!MSRA+北大全华班「女娲」模型怒刷8项SOTA,完虐OpenAI DALL-E...

      视学算法报道   编辑:好困 小咸鱼 LRS [新智元导读]微软亚洲研究院.北京大学强强联合提出了一个可以同时覆盖语言.图像和视频的统一多模态预训练模型--NÜWA(女娲),直接包揽8项SOTA. ...

  5. 一个模型通杀8大视觉任务,图像、视频生成大一统!MSRA+北大全华班「女娲」模型...

    来源:新智元 太卷了,太卷了!微软亚洲研究院.北京大学强强联合提出了一个可以同时覆盖语言.图像和视频的统一多模态预训练模型--NÜWA(女娲),包揽8项SOTA,完虐OpenAI DALL-E! 照着 ...

  6. opencv视频处理和检测学习总结

    基于opencv的视频处理--基础数据结构 在一个封装的还算比较好的库中,一般都不会直接采用那些基本的数据结构像char, int 之类,一是 不具有可读性,二是不方便修改移植.通常是通过typede ...

  7. 【直播回放】60分钟了解各类图像和视频生成GAN结构

    大家好,欢迎来到我们的付费视频直播回放栏目,在这个专栏中我们会每一次针对某一个主题,做1-2个小时左右的直播,包含PPT讲解与答疑交流. 作者&编辑 | 言有三 本文主题与资源 主讲人:言有三 ...

  8. OpenCV视频加速Video acceleration的实例(附完整代码)

    OpenCV视频加速Video acceleration的实例 OpenCV视频加速Video acceleration的实例 OpenCV视频加速Video acceleration的实例 #inc ...

  9. OpenCV视频中的人脸标志检测

    OpenCV视频中的人脸标志检测 视频中的人脸标志检测 简介 命令参数说明 源代码 视频中的人脸标志检测 简介 此应用程序使您可以检测视频中检测到的面部的地标.此应用程序首先检测当前视频帧中的面部,然 ...

最新文章

  1. ​​《自然》2020年十大科学发现出炉:病毒,冷冻电镜与快速射电暴
  2. while循环的习题
  3. 循环卷积和周期卷积的关系_PSConv:多位一体、即插即用卷积单元
  4. python 打包发布网站_Python代码的打包与发布
  5. 因为apple无法检查其是否包含恶意软件_新Linux恶意脚本——清理其他恶意软件后再感染...
  6. 微信开放平台 公众号第三方平台开发 教程五 代公众号发起网页授权源码
  7. NFT赛车游戏F1® Delta Time启动第二轮2019赛车NFT质押活动
  8. idea Spring-boot 项目debug启动过慢 spring debug启动过慢解决办法:已解决
  9. matlab中fft定点运算,可用于嵌入式计算的定点FFT算法 (转载)
  10. Wireshark实验 - DNS
  11. mathtype删除注册表的方法
  12. 机器学习 ❀ 数据投毒攻击(数据投毒 / 模型投毒) 隐私攻击(数据隐私 / 模型隐私)
  13. SSH-keygen用法
  14. UserAgent个人整理
  15. WIN7下默认网关丢失该如何解决
  16. 跳转到QQ聊天界面和QQ群界面
  17. 建筑工地人脸识别门禁通道闸机如何安装 1
  18. matlab中unifrnd函数用法,概率和统计的MATLAB指令
  19. html里面点击重置按钮无反应,点击重置按钮后没反应.
  20. AE开发中“无法嵌入互操作类型*****,请改用适用的接口”解决办法

热门文章

  1. c语言制作java虚拟机_【C/C+】虚拟机实现:用C语言来写Java虚拟机
  2. 通俗易懂的Spring AOP术语
  3. 周记,本周前端的学习
  4. 不抱怨不解释--为了换一个吃饭的地方
  5. 豆豆趣事[2012年11月]
  6. latex图片跨双栏
  7. PHP 四种基础算法
  8. 关于mysql还原数据库贼鸡巴慢的解决办法,有效
  9. P2575 高手过招 (博弈、sg函数)
  10. 日语图片怎么翻译?分享个好用的翻译方法