opencv 制作簡筆畫視頻


本教程介紹了如何使用opencv生成一副簡筆畫視頻,包括片頭、如何做畫等。

1、視頻包括:

(1)片頭:包括學號姓名,同時會出現"I Love CV"在學號和姓名的中央,而且他們是以動畫方式“飛入”視頻的,其中姓名從頂部“飛”到屏幕1/3處,學號信息從下“飛”到1/3處,I LOVE CV從左向右飛入。在片頭顯示完后,會停頓越三秒鍾后,片頭消失,正片開始。

下圖為片頭停頓處截圖:

 

 

(2)正片:正片主要畫了一頭可愛的小熊和一頭胖胖的小豬以及一個桃心(預示着小熊.....),其中所有的鏡頭最小單位都是像素,是一個點一個點畫計算並畫出的,並沒有調用內置的畫圖函數(畫點函數除外)以下是最終效果截圖:

 

 

 

一、開發環境

1、開發環境:vs2015

2、Opencv庫:opencv2.4

3、操作系統:windows10

二、實現方法

我把整個實現分為三步:

初始化、工具函數的編寫、畫圖

下面將分開說明各部分實現步驟:

1、初始化:首先定義兩個全局變量ViedoWriter writer和Mat image,並對其初始化,分別在規定路徑創建一個空視頻(writer)和空矩陣或圖片(image)。我的想法是一個點一個點的畫圖,所以我需要每畫一個點或者一個規定步長的點就網writer里寫一幀,所以我封裝了一個函數putPicture(Mat img)用於將參數傳入writer中。在以后的畫圖工具函數中,我每描出一個點就調用一次putPicture函數。

 

2、對於工具函數的撰寫,我主要寫了以下幾個函數:

(1)drawPoint(Mat img, Point center,Scalar color,int thick)

功能:可以再指定的center上畫不同大小的點(大小由thick參數指定)。該函數是畫所有圖的核心函數

實現:調用opencv的circle函數

(2)drawLine(Mat mat, Point start, Point end,Scalar color,int thick)

功能:通過起始終止點畫出一條線段並將每個點以一幀的形式存入視頻。

實現:使用DDA算法畫直線。

改進:DDA有舍入誤差會影響精度,可以使用bersenham算法,但我沒有時間了。。。

(3)drawArc(Mat img, Point center, double radius, double start_angle, double end_angle, Scalar color,int thick)

功能:通過給定參數逐點畫圓弧並將每個點以一幀的形式存入視頻。

實現:用圓的參數方程逐點描出

改進:可使用bersenham算法,但我個人覺得針對此問題沒必要,增加很多算法上的負擔。因為此問題不太要求精度。

(4)drawEarc(Mat img, Point center, double radius, double start_angle, double end_angle, float a,float b,Scalar color,int thick,bool is_x)

功能:通過給定參數畫橢圓弧並將每個點以一幀的形式存入視頻,其中通過is_x變量可以指定焦點位置,同時thick可以指定寬度,color指定顏色

實現:同樣使用參數方程

(5)drawStar(Mat img,Point center,int a,Scalar color,int thick)

功能:通過給定參數畫心形並將每個點以一幀的形式存入視頻,其中a可指定心形的大小。

實現:

arc.x = center.x + a*i*sin(PI*sin(i) / i);

arc.y = center.y + a*abs(i)*cos(PI*sin(i) / i);

並沒有使用經典的笛卡爾心形公式,而是使用的這個形狀更好看的參數方程。

(6)drawBackground(bool flag)

功能:畫一個白色的大矩形覆蓋整個屏幕,起到畫背景和清屏的作用

 

3、有了以上制作視頻思路和工具最后的畫圖部分考驗的更多的是時間,我主要分了三部分來畫

(1)drawBear()

功能:顧名思義畫熊。對於這個萌萌噠的小熊:

 

 

他的身子是橢圓的一部分弧,耳朵是圓的一部分,鼻子是一個小橢圓但是我增加了線的厚度。

改進:需要改進的部分太多了,由於我是“憑空”使用我的工具畫的,並沒有使用ps等工具具體的挖出精確的點來畫,所以導致很多部分無法完成。這也導致只熊的身子和腿很丑。

(2)drawRh()

功能:畫豬

 

 

這個豬的構造很簡單,我沒有選擇有復雜曲線的豬,選擇了這個可以由圓構成的豬。

改進:可以使用參數曲線算法來豐富他的身體。

(3)drawText()

功能:制作片頭

實現方法:使用內置的puttext函數,加上清屏函數組合來完成文字的滑動,即每移動一次字幕調用清屏函數,最后使用臨時變量保存的算法使三行字幕停頓幾秒鍾。

三、心得體會與優缺點:

缺點與不足:

1、沒有對於中文的處理,本來學號和姓名我很想使用中文的,但是opencv不支持,后來又使用freetype包卻出現很多編譯錯誤,沒辦法由於時間緊迫沒有實現

2、畫圖工具封裝的太少,只做了幾個最基本的函數,對於高級的函數如參數曲線等由於能力問題並沒有用到

3、沒有對顏色的處理

下面附上源代碼:

 

復制代碼
#include <opencv2/opencv.hpp>  
#include<string>  
#include<io.h>  
#include<math.h>
#include"base.h"
using namespace std;
using namespace cv;
#define NUM_FRAME 300  
#define SIZE 5  
#define W 1080
#define H 720
#define PI 3.1415926
char path[100];//輸入文件路徑  
VideoWriter writer;
Mat image,temp;
Point s, e;
void convert(Point &a)
{
    a.x = a.x + W / 2;
    a.y = -a.y + H / 2;
}
void rconvert(Point &a)
{
    a.x = a.x - W / 2;
    a.y = -a.y + H / 2;
}

void init()
{
    //image= Mat::zeros(W, W, CV_8UC3);//創建一張空圖像
    image = Mat(H, W, CV_8UC3);
    temp = Mat(H, W, CV_8UC3);
    strcpy(path, "G:\\image\\viedo.avi");
    writer = VideoWriter(path, CV_FOURCC('X', 'V', 'I', 'D'), 30, Size(W, H));
}
void putPicture(Mat img)
{
    writer.write(Mat(img));
}
void delay(int k)
{
    for (int i = 0; i <= k; i++)
    {
        putPicture(image);
    }
}
void play()
{
    VideoCapture capture(path);
    if (!capture.isOpened())
        cout << "fail to open!" << endl;
    ////獲取整個幀數
    //long totalFrameNumber = capture.get(CV_CAP_PROP_FRAME_COUNT);
    //cout << "整個視頻共" << totalFrameNumber << "幀" << endl;

    //設置開始幀()
    long frameToStart = 0;
    capture.set(CV_CAP_PROP_POS_FRAMES, frameToStart);
    cout << "從第" << frameToStart << "幀開始讀" << endl;

    //獲取幀率
    double rate = capture.get(CV_CAP_PROP_FPS);
    cout << "幀率為:" << rate << endl;
    //承載每一幀的圖像
    Mat frame;
    //顯示每一幀的窗口
    namedWindow("Viedo");
    //兩幀間的間隔時間:
    int delay = 1000 / rate;
    long currentFrame = frameToStart;

    while (1)
    {
        //讀取下一幀
        if (!capture.read(frame))
        {
            break;
            return;
        }
        //這里加濾波程序
        imshow("Extracted frame", frame);
        int c = waitKey(delay);
        //按下按鍵后會停留在當前幀,等待下一次按鍵
        if (c >= 0)
        {
            waitKey(0);
        }
        currentFrame++;
    }
    //關閉視頻文件
    capture.release();
}
void drawLine(Mat mat, Point start, Point end,Scalar color,int thick)
{
    //printf("start Point: x=%d y=%d\n", start.x, start.y);
    convert(start);
    convert(end);
    int x1, y1,step;
    Point center=start;
    x1 = start.x;
    y1 = start.y;
    double footx,footy;
    int dx = end.x - start.x;
    int dy = end.y - start.y;
    drawPoint(mat, center, color,thick);
    putPicture(mat);
    if (abs(dx) > abs(dy))
    {
        step = abs(dx);
    }
    else 
    {
        step = abs(dy);
    }
    footx = (double)dx / step;
    footy = (double)dy / step;
    //printf("footy = %f\n", footy);
    for (int i = 0; i<step; i++)
    {
        if (footx > 0) {
            x1 += int(footx + 0.5);
        }
        if (footx < 0)
        {
            x1 += int(footx - 0.5);
        }
        if (footy > 0)
        {
            y1 += int(footy + 0.5);
        }
        if (footy < 0)
        {
            y1 += int(footy - 0.5);
        }
        center.x = x1;
        center.y = y1;
        drawPoint(mat, center, color,thick);
        //printf("x=%d y=%d\n", center.x, center.y);
        putPicture(mat);
    }
    rconvert(center);
    //printf("end Point: x=%d y=%d\n", center.x, center.y);
    //printf("\n");
}
void drawLine2(Mat mat, Point start, Point end, Scalar color,int thick)
{
    convert(start);
    convert(end);
    int x1, y1, step;
    double k;
    Point center = start;
    x1 = start.x;
    y1 = start.y;
    int dx = end.x - start.x;
    int dy = end.y - start.y;
    if (dx != 0)
    {
        k = dy / dx;
    }
    else
        k = 0;
    double d = k - 0.5;
    drawPoint(mat, center, color,thick);
    printf("start Point: x=%d y=%d\n", center.x, center.y);
    putPicture(mat);
    while(x1!=end.x)
    {
        x1++;
        if (d >= 0)
        {
            y1++;
            d = d + k - 1;
        }
        else
        {
            d = d + k;
        }
        center.x = x1;
        center.y = y1;
        drawPoint(mat, center, color,thick);
        //printf("x=%d y=%d\n", center.x, center.y);
        putPicture(mat);
    }
    printf("end Point: x=%d y=%d\n", center.x, center.y);
}
void drawBackground(bool flag)
{
    rectangle(image,
        Point(0, 0),
        Point(W, W),
        Scalar(255, 255, 255),
        -1,
        8);
    if (flag == true)
    {
        putPicture(image);
    }
    rectangle(image,
        Point(0, 550),
        Point(W, W),
        Scalar(0, 255, 0),
        -1,
        8);
}
void drawText()
{
    int i = 0;
    int count = 30;
    String name = "name    :   Sunke";
    String number = "Student ID: 317040001";
    String other = "I Love CV";
    while(1)
    {
        i++;
        putText(image, number, Point(W / 3,H-i*(H/3)/count), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(255, 0, 0));
        putText(image, name, Point(W/3, 0 + i*(H / 3) / count), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255));
        putPicture(image);
        if (i == count)
        {
            image.copyTo(temp);
            for (int j = 0; j <= count; j++)
            {
                temp.copyTo(image);
                putText(image, other, Point(75+j*(W/3)/count, H/2), CV_FONT_HERSHEY_TRIPLEX, 1.5, Scalar(0, 255, 0));
                putPicture(image);
                if (j == count)//片頭延時
                {
                    delay(200);
                }
                drawBackground(false);
            }
            break;
        }
        else
        {
            drawBackground(false);//只是重置image變量,並不畫這樣避免了閃屏
        }
    }
    putPicture(image);
}
void drawArc(Mat img, Point center, double radius, double start_angle, double end_angle, Scalar color,int thick)
{
    convert(center);
    Point arc;
    double foot = 0.02;
    for (double r = start_angle; r <= end_angle; r = r + foot)
    {
        arc.x = center.x + radius*cos(r);
        arc.y = center.y + radius*sin(r);
        if (r == start_angle)
        {
            s = arc;
        }
        if (r == end_angle)
        {
            s = arc;
        }
        drawPoint(img, arc, color,thick);
        putPicture(image);
    }
}
void drawEarc(Mat img, Point center, double radius, double start_angle, double end_angle, float a,float b,Scalar color,int thick,bool is_x)
{
    convert(center);
    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);
        putPicture(image);
    }
}
void drawBear()
{
    Point left_eye(190, 160);
    Point right_eye(230, 160);
    convert(left_eye);
    convert(right_eye);
    drawLine(image, Point(150, 100), Point(150, 180), Scalar(0, 0, 0),1);
    //drawLine(image, Point(150, 220), Point(190, 200), Scalar(0, 0, 0),1);
    drawArc(image, Point(165, 180), 15, PI,2*PI,Scalar(0, 0, 0), 1);
    drawEarc(image, Point(210, 180), 50, PI, 2 * PI, 30, 8, Scalar(0, 0, 0),1,true);
    drawArc(image, Point(255, 180), 15, PI, 2 * PI, Scalar(0, 0, 0), 1);

    //drawLine(image, Point(230, 180), Point(270, 220), Scalar(0, 0, 0),1);
    drawLine(image, Point(270, 180), Point(270, 100), Scalar(0, 0, 0),1);
    //臉
    drawPoint(image, left_eye, Scalar(0, 0, 0),5);
    drawPoint(image, right_eye, Scalar(0, 0, 0),5);
    putPicture(image);

    drawEarc(image, Point(210, 140), 50, 0, 2 * PI, 6, 3, Scalar(0, 0, 0),5,true);
    drawLine(image, Point(210, 143), Point(210, 125), Scalar(0, 0, 0), 1);
    drawLine(image, Point(210, 125), Point(200, 115), Scalar(0, 0, 0), 1);
    drawLine(image, Point(210, 125), Point(220, 115), Scalar(0, 0, 0), 1);
    //手
    drawLine(image, Point(150, 100), Point(90, 0), Scalar(0, 0, 0), 1);
    drawLine(image, Point(270, 100), Point(330, 0), Scalar(0, 0, 0), 1);
      //左
    drawLine(image, Point(50, 0), Point(70, -20), Scalar(0, 0, 0), 1);
    drawLine(image, Point(70, -20), Point(150, 40), Scalar(0, 0, 0), 1);
      //右
    drawLine(image, Point(370, 0), Point(350, -20), Scalar(0, 0, 0), 1);
    drawLine(image, Point(350, -20), Point(270, 40), Scalar(0, 0, 0), 1);
    //身體
    //左
    drawEarc(image, Point(155, -40), 50, -1.5*PI, -0.5*PI, 100, 30, Scalar(0, 0, 0), 1,false);
    drawLine(image, Point(155, -140), Point(190, -140), Scalar(0, 0, 0), 1);
    drawLine(image, Point(190, -140), Point(190, -90), Scalar(0, 0, 0), 1);
    //右
    drawEarc(image, Point(265, -40), 50, -0.5*PI, 0.5*PI, 100, 30, Scalar(0, 0, 0), 1, false);
    drawLine(image, Point(270, -140), Point(240, -140), Scalar(0, 0, 0), 1);
    drawLine(image, Point(240, -140), Point(240, -90), Scalar(0, 0, 0), 1);

    //drawLine(image, Point(190, -90), Point(240, -90), Scalar(0, 0, 0), 1);
    drawEarc(image, Point(215, -90), 50, PI, 2 * PI, 25, 8, Scalar(0, 0, 0), 1, true);

}
void drawStar(Mat img,Point center,int a,Scalar color,int thick)
{
    convert(center);
    Point arc;
    double foot=2*PI/360;

    for (double i = -PI; i <= PI; i=i+foot)
    {
        /*arc.x = center.x + a*(2 * cos(i) - cos(2 * i));
        arc.y = center.y + a*(2 * sin(i) - sin(2 * i));*/
        arc.x = center.x + a*i*sin(PI*sin(i) / i);
        arc.y = center.y + a*abs(i)*cos(PI*sin(i) / i);
        drawPoint(img, arc, color, thick);
        putPicture(image);
    }
}
void drawRh()
{
    Point left_eye(-240, 160);
    Point right_eye(-180, 160);
    Point left_nose(-240,85);
    Point right_nose(-180,85);
    convert(left_eye);
    convert(right_eye);
    convert(left_nose);
    convert(right_nose);
    drawArc(image, Point(-210, 130), 100, 0, 2 * PI, Scalar(0, 0, 0), 1);
    //眼眶
    drawArc(image, Point(-170, 170), 20, 0, 2 * PI, Scalar(0, 0, 0), 1);
    drawArc(image, Point(-250, 170), 20, 0, 2 * PI, Scalar(0, 0, 0), 1);
    //眼球
    drawPoint(image, left_eye, Scalar(0, 0, 0), 6);
    putPicture(image);
    drawPoint(image, right_eye, Scalar(0, 0, 0), 6);
    putPicture(image);

    //鼻子
    drawEarc(image, Point(-210, 85), 50, 0, 2 * PI, 60, 30, Scalar(0, 0, 0), 1, true);

    //鼻孔
    drawPoint(image, left_nose, Scalar(0, 0, 0), 9);
    putPicture(image);
    drawPoint(image, right_nose, Scalar(0, 0, 0), 9);
    putPicture(image);

    //左耳朵
    drawLine(image, Point(-280, 207), Point(-280, 240), Scalar(0, 0, 0), 1);
    drawLine(image, Point(-280, 240), Point(-260, 220), Scalar(0, 0, 0), 1);
    //右耳朵
    drawLine(image, Point(-140, 207), Point(-140, 240), Scalar(0, 0, 0), 1);
    drawLine(image, Point(-140, 240), Point(-160, 220), Scalar(0, 0, 0), 1);

    //身子
    drawEarc(image, Point(-210, 95), 170, 0, 2*PI, 185,160,Scalar(0, 0, 0), 2,true);
    //腳
    drawArc(image, Point(-290, -80), 30, PI, 2 * PI, Scalar(0, 0, 0), 1);
    drawLine(image, Point(-320, -80), Point(-260, -80), Scalar(0, 0, 0), 1);

    drawArc(image, Point(-130, -80), 30, PI, 2 * PI, Scalar(0, 0, 0), 1);
    drawLine(image, Point(-160, -80), Point(-100, -80), Scalar(0, 0, 0), 1);


}
int main()
{
    printf("正在生成視頻,請稍后");
    init();
    drawBackground(true);
    drawText();
    drawBear();
    drawRh();
    drawStar(image, Point(0, 300), 40, Scalar(255, 255, 0), 2);
    drawStar(image, Point(300, 300), 50, Scalar(255, 0, 255), 2);
    drawStar(image, Point(0, 0), 35, Scalar(255, 0, 0), 2);
    drawStar(image, Point(-350, 250), 35, Scalar(255, 0, 255), 2);
    imshow("pig", image);
    play();
    waitKey();
    return 0;
}
復制代碼

 

base.cpp

復制代碼
#include"base.h"
#include<math.h>

void MyLine(Mat img, Point start, Point end)
{
    int thickness = 2;
    int lineType = 8;
    line(img,
        start,
        end,
        Scalar(255, 255, 255),
        thickness,
        lineType);
}
void drawPoint(Mat img, Point center,Scalar color,int thick)
{
    circle(img,
        center,
        thick,
        color,
        -1);
}
void drawEye(Mat img, Point center, Scalar color)
{
    circle(img,
        center,
        5,
        color,
        -1);


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM