zoukankan      html  css  js  c++  java
  • CV HW1

    开发软件说明:

    OpenCV 2.1.0

    Visual Studio 2019

    1. 内容简介

    • 本次作业实现生成一个视频,包含片头,中间动画,片尾动画三部分,然后读取生成的视频并按下空格可以暂停
      • 片头是个人信息+倒计时
      • 中间动画,画了一个小猪佩奇追气球的故事
      • 片尾动画,是四个彩色方块交替出现

    2. 视频生成过程

    • 创建视频

      • 使用VideoWrite类,生成一个名叫test.avi的视频,同时配置大小,帧率等参数

        VideoWriter video("test.avi", CV_FOURCC('X', 'V', 'I', 'D'), 270, Size(1280, 720));
        
    • 制作片头

      • 片头是由几个图片组合而成的,需要把图片作为视频中的一帧,插入进去

        • 第一张封面是HW1
        • 第二张是我的个人信息和含有校徽的背景
        • 第三张是我的个人照片(5岁)
        • 第四到六张是一个倒计时
        • 第七张是Let's start,表示动画开始
      • 设计delay函数,可以在video中,插入time数量个image图片

        void delay(Mat image, VideoWriter video, int time) {
        	for (int i = 0; i < time; i++) {
        		video << image;
        	}
        }
        
      • 同时图与图的切换,本次实验中设计了百叶窗切换方法

        • 其原理就是在图片1中根据一定规律载入图片2的像素,生成一帧图片,再插入到video中
        • 百叶窗实现要点:
          • 将图形中点的y坐标分为5层,每一层相隔为r,5层像素一起由pic0变为pic1,实现百叶窗变换
        • 注意两个矩阵赋值时,因为是3通道,所以每一像素要赋三个值
        void blindTransition(Mat pic0, Mat pic1, VideoWriter video) {          // 百叶窗载入
        	int x_max = pic0.cols, y_max = pic0.rows;
        	int y_step = y_max / 5;                        
        	for (int r = 0; r < y_step; r++) {              
        		for (int y = r; y < y_max; y += y_step) {  
        			uchar* data = pic0.ptr<uchar>(y);      
        			uchar* p1 = pic1.ptr<uchar>(y);         
        			for (int x = 0; x < x_max; x++) {       
        				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++) {
        			video << pic0;
        		}
        	}
        }
        
      • 实现上述两个准备函数后,就可以读入已有的图片,经过百叶窗切换形成视频

        • 在插入图片之前要将图片的size设为一致,否则会出现矩阵数组溢出的情况

          	for (size_t i = 2; i <= count; i++)
          	{
          		stringstream str1, str2;
          		str1 << i-1 << ".png";
          		str2 << i << ".png";
          		Mat image1 = imread(img_path + str1.str());
          		Mat image2 = imread(img_path + str2.str());
          		if (!image1.empty() && !image2.empty())
          		{
          			resize(image1, image1, Size(1280, 720));
          			resize(image2, image2, Size(1280, 720));
          			blindTransition(image1, image2, video);
          
          			//让第二帧停留的久一点
          			if (i == 2) {
          				delay(image2, video, 50);
          			}
          			video << image2;
          			cout << "正在处理第" << i << "帧" << endl;
          		}
          	}
          
    • 制作动画

      • 作业中设计的动画内容是小猪佩奇追气球的情节,分为以下几个部分

        • 画小猪佩奇和气球
        • 小猪佩奇带着气球向右走
        • 小猪佩奇不动,气球向左飞
        • 小猪佩奇向左走追气球
        • 小猪佩奇追不到气球,在画面中间静止,并变成哭脸
      • 需要的函数

        • void drawEarc(Mat img, VideoWriter video, Point center, double radius, double start_angle, double end_angle, float a, float b, Scalar color, int thick, bool is_x, bool is_drawing)

          • 参数

            • 在img上绘制椭圆
            • 椭圆的圆点在center,弧度为radius, 起始度数为start_angle, 终点度数为end_angel, 长半轴(水平)为a, 短半轴(竖直)为b
            • 颜色为color, 线的宽度为thick
            • is_drawing代表是否把img插入video
          • 应用

            • 如果画横线或竖线,可以令短半轴或长半轴为0, 绘制度数为(PI, 2*PI)或(0.5 * PI, 1.5 * PI)
            • 画弧线或圆可以调整参数画出适合的图案
          • 实现

            • 在其中,应用椭圆的一些公式进行绘制

              	Point arc;
              	double foot = 0.02;
              	for (double r = start_angle; r <= end_angle; r = r + foot)
              	{
              		if (is_x)
              		{
              			arc.x = center.x + a * cos(r);
              			arc.y = center.y + b * sin(r);
              		}
              		else
              		{
              			arc.x = center.x + b * cos(r);
              			arc.y = center.y + a * sin(r);
              		}
              
              		if (r == start_angle)
              		{
              			s = arc;
              		}
              		if (r == end_angle)
              		{
              			s = arc;
              		}
              		
              		drawPoint(img, arc, color, thick);
              
        • Mat drawPeppa(Mat image, VideoWriter video, int x, int y, bool is_drawing, int frame, bool is_cry)

          • 参数

            • 实现在image上绘制佩奇,并把Image插入video
            • 以Point(x, y)作为佩奇的左上角进行绘制
            • is_drawing代表是否显示画的过程
            • 因为动画中有佩奇走路,frame代表当前走路是第几帧,一共有3帧,站着,抬左腿,抬右腿
            • is_cry代表当前佩奇是哭脸还是笑脸
            • 函数中所有的线条都是用drawEarc来实现的
          • 实现步骤,这里以佩奇的头为例

            	//佩奇的头
            	drawEarc(image, video, Point(x + 265, y + 150), 50, 0.2 * PI, 0.6 * PI, 30, 30, Scalar(0, 0, 0), 1, true, is_drawing);
            	drawEarc(image, video, Point(x + 210, y + 180), 50, 0, PI, 50, 50, Scalar(0, 0, 0), 1, true, is_drawing);
            	drawEarc(image, video, Point(x + 250, y + 180), 50, PI, 1.6 * PI, 90, 50, Scalar(0, 0, 0), 1, true, is_drawing);
            	drawEarc(image, video, Point(x + 282, y + 150), 50, 0, 2 * PI, 19, 19, Scalar(0, 0, 0), 1, true, is_drawing);
            

            画出佩奇的头,如下

            image-20201130162045355.png

            同理画出身体和其他细节

          • 实现佩奇走路

            共有3中情况,站着,抬左腿,抬右腿,只要循环实现站着->抬左腿->站着->抬右腿,就能实现佩奇走路了。

        • Mat drawballon(Mat image, VideoWriter video, int x, int y, bool is_drawing)

          • 实现在image上绘制气球,并把image插入video
          • 以Point(x, y)作为气球的左上角进行绘制
          • is_drawing代表是否显示画的过程
        • Mat convertPappe(Mat image, Mat result, int c_x)

          • 翻转佩奇,把image中的像素点以x = c_x这条直线进行翻转,结果存放在result中
      • 绘制的过程

        • 根据之前设计的动画情节,在不同的坐标上绘制图形,以下以前两个情景的代码为例,其他情景类似,只需要控制佩奇和气球的不同坐标即可

        • 初始化并绘制佩奇

          • //初始化画板
              	Mat image = Mat(Size(1280, 720), CV_8UC3);
              	
              	//绘制佩奇和气球
              	drawPeppa(image, video, 400, 200, true, 0, false);
              	drawballon(image, video, 400, 200, true);
              	delay(image, video, 5);
            
        • 佩奇带着气球右走5步

          • 通过drawPeppa函数中参数frame的值来控制佩奇的腿实现走路的动作
          	//佩奇往右走5步
          	int location1;
          	for (location1 = 0; location1 < 4 * 5; location1++) {
          		Mat image = Mat(Size(1280, 720), CV_8UC3);
          		image = drawPeppa(image, video, 400 + location1 * 10, 200, false, location1 % 4, false);
          		image = drawballon(image, video, 400 + location1 * 10, 200, false);
          		delay(image, video, 10);
          	}
          
    • 片尾动画

      • 片尾动画是画面中间4个不同颜色的小正方形交替变换

      • 关键函数Mat drawBox(Mat image, int x, int y, int B, int G, int R)

        • 在image上, 以Point(x, y)为左上角,画一个边长为50像素,颜色为Scalar(B, G, R),的正方形
      • 实现

        • 交替出现四个方块, 共10个循环

          	for (int i = 0; i < 4 * 10; i++) {
          		Mat image_end = Mat(Size(1280, 720), CV_8UC3);
          		if (i % 4 == 0)		//blue
          			image_end = drawBox(image_end, -60, -60, 0x2d, 0x85, 0xf0);
          		else if (i % 4 == 1)		//red
          			image_end = drawBox(image_end, -60, 0, 0xf4, 0x43, 0x3c);
          		else if (i % 4 == 2)		//yellow
          			image_end = drawBox(image_end, 0, 0, 0xff, 0xbc, 0x32);
          		else if (i % 4 == 3)		//green
          			image_end = drawBox(image_end, 0, -60, 0xa, 0xa8, 0x58);
          		delay(image_end, video, 80);
          	}
          

    3. 视频读取过程

    • 读取视频文件

      • 使用VideoCapture类, 读取之前生成的视频test.avi
    • 展示每一帧

      • 读取每一帧,再使用imshow函数输出每一帧
    • 设置空格暂停

      • 如果没有按键,waitKey(delay)返回-1,不执行waitKey(0),进入下一次循环。

      • 如果有按键,返回按键的ASCII值,waitKey(delay)>=32为true,执行waitKey(0),程序暂停,直到有键盘输出才进行下一次循环。

      • int delay = 4;
        if (delay >= 0 && waitKey(delay) >= 32)
        		waitKey(0);
        
    • 使用release方法,释放资源

    4. 实验结果展示与分析

    • 百叶窗切换

      • 下图就是第一帧和第二帧的切换,可以看到实现了百叶窗的效果

      (包含个人信息,这张图就不放了)

    • 绘制佩奇的过程

      • 可以看到下图中,逐步画出佩奇的效果

        image-20201130163307911.png

        image-20201130163328956.png

        image-20201130163406035.png

    • 一些动画的场景

      • 佩奇回头

        image-20201130163511846.png

      • 佩奇哭脸

        image-20201130163541350.png

      • 佩奇走路

        这里图片不能表现出来,详见视频

    • 片尾

      • 如下的四个彩色方块循环出现消失,组成了片尾

      image-20201130163659383.png

      image-20201130163720609.png

    5. 编程体会

    • 本次作业中,我了解了opencv最基础的应用,比如读图片,读视频,生成视频,把图像一帧一帧插入图像等等
    • 还学会了把一些简单的图形计算公式应用于图像显示上面,比如画椭圆, 画方块等等
    • 还学会了一些基本的画图和矩阵操作,最基本的图形变换,比如百叶窗变换,图像翻转等
    • 最后还学会通过简单的帧的变换做一些动画,比如小猪佩奇走路,最后的片尾动画

    借鉴的网页 https://blog.csdn.net/u013794793/article/details/78787409

  • 相关阅读:
    对java中接口的简单理解
    jqgrid
    sed跨行匹配替换
    linux 安装 mysql
    mysql 导入或导出(mysqldump)数据
    spring boot slf4j + logback
    原码、反码、补码
    Java线程池(一)
    springboot 多环境配置及打包资源
    springboot自定义yaml配置文件
  • 原文地址:https://www.cnblogs.com/CGCY/p/14102662.html
Copyright © 2011-2022 走看看