C++ OpenCV實現抖音"藍線挑戰"特效

前言

本文將使用OpenCV C++ 實現抖音上的特效“藍線挑戰”。雖然看起來覺得很牛的樣子,但如果瞭解其中的原理就非常簡單瞭。本案例是我自己對於這個特效實現過程的理解,僅供參考。

算法原理可以分為三個流程:

1、將視頻(圖像)從(頂->底)或(左->右)逐行(列)掃描圖像。

2、將掃描完成的行(列)像素重新生成定格圖像。

3、使用原幀圖像像素填充未掃描到的像素。

接下來就具體來看看是如何實現的吧。

一、圖像掃描

首先第一步,拿到一個視頻(很多幀圖像)可以簡單的看成圖像處理。我們需要將圖像從頂到底逐行進行像素掃描,當然也可以從左到右逐列掃描,這要看你想要實現什麼樣的效果。在這裡,我實現的是從上到下逐行掃描。效果如圖所示。

二、生成定格圖像

所謂生成定格圖像就是將我們每掃描到的行像素重新進行繪制。

    //從頂向下逐行掃描圖像
    if (h < height)
    {
        h++;
        //將掃描到的圖像像素進行重新繪制,生成新圖像
        for (int j = 0; j < width; j++)
        {
            for (int c = 0; c < 3; c++)
            {
                temp.at<Vec3b>(h, j)[c] = canvas.at<Vec3b>(h, j)[c];
            }
        }
        //繪制掃描過程
        line(canvas, Point(0, h), Point(width, h), Scalar(255, 255, 0), 2);
    }

如圖所示,這是使用上面代碼段實現的逐行掃描生成定格圖像。從效果上看,已經得到瞭我們想要的大致效果瞭。不過現在的問題是,經掃描到的行有像素填充,未掃描到的行還是漆黑一片。所以接下來我們需要做的就是將未掃描到的行用原圖進行填充。具體請看源碼註釋。

三、圖像混合

//將兩幅圖像進行線性混合
bool Linear_Blend(Mat src1, Mat src2, Mat& dst)
{
    /*
    參數說明:
    src1:生成的定格圖像。由於生成的定格圖像是從頂往下逐行掃描,故在掃描線下的像素為0
    src2:原視頻幀圖像
    dst:新生成的定格圖像。
    算法原理:經掃描到的像素用src1進行填充,未掃描到的像素用src2進行填充,這樣就可以得到我們所要的效果瞭。
    */

    for (int i = 0; i < src1.rows; i++)
    {
        for (int j = 0; j < src1.cols; j++)
        {
            for (int c = 0; c < 3; c++)
            {
                if (src1.at<Vec3b>(i, j)[0] != 0)
                {
                    dst.at<Vec3b>(i, j)[c] = src1.at<Vec3b>(i, j)[c];
                }
                else
                {
                    dst.at<Vec3b>(i, j)[c] = src2.at<Vec3b>(i, j)[c];
                }
            }
        }
    }

    return true;
}

四、效果顯示

如上圖所示,至此我們已經完成瞭案例所想要的效果。請參考源碼,註釋也比較詳細瞭。

五、源碼

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;

/*
抖音特效:藍線挑戰
算法原理:
    1、將視頻(圖像)從(頂->底)或(左->右)逐行(列)掃描圖像。
    2、將掃描完成的行(列)像素重新生成定格圖像
    3、使用原幀圖像像素填充未掃描到的像素
*/

//將兩幅圖像進行線性混合
bool Linear_Blend(Mat src1, Mat src2, Mat& dst)
{
    /*
    參數說明:
    src1:生成的定格圖像。由於生成的定格圖像是從頂往下逐行掃描,故在掃描線下的像素為0
    src2:原視頻幀圖像
    dst:新生成的定格圖像。
    算法原理:經掃描到的像素用src1進行填充,未掃描到的像素用src2進行填充,這樣就可以得到我們所要的效果瞭。
    */

    for (int i = 0; i < src1.rows; i++)
    {
        for (int j = 0; j < src1.cols; j++)
        {
            for (int c = 0; c < 3; c++)
            {
                if (src1.at<Vec3b>(i, j)[0] != 0)
                {
                    dst.at<Vec3b>(i, j)[c] = src1.at<Vec3b>(i, j)[c];
                }
                else
                {
                    dst.at<Vec3b>(i, j)[c] = src2.at<Vec3b>(i, j)[c];
                }
            }
        }
    }

    return true;
}

int main()
{
    VideoCapture capture;
    capture.open("test.avi");
    if (!capture.isOpened())
    {
        cout << "can not open the camera!" << endl;
        system("pause");
        return -1;
    }

    int width = capture.get(CAP_PROP_FRAME_WIDTH);//視頻幀寬
    int height = capture.get(CAP_PROP_FRAME_HEIGHT);//視頻幀高

    //保存視頻
    VideoWriter writer;
    int fourcc = writer.fourcc('m', 'p', '4', 'v'); //視頻編碼
    Size size(capture.get(CAP_PROP_FRAME_WIDTH), capture.get(CAP_PROP_FRAME_HEIGHT));
    writer.open("result.avi", fourcc, 30, size, true);

    int h = 0;//定義變量,代表當前掃描高度

    //用於生成定格照
    Mat temp = Mat::zeros(Size(width, height), CV_8UC3);
    
    Mat frame;
    while (capture.read(frame))
    {
        //將圖像拷貝一份,用於每幀更新
        Mat canvas = frame.clone();

        //從頂向下逐行掃描圖像
        if (h < height)
        {
            h++;
            //將掃描到的圖像像素進行重新繪制,生成新圖像
            for (int j = 0; j < width; j++)
            {
                for (int c = 0; c < 3; c++)
                {
                    temp.at<Vec3b>(h, j)[c] = canvas.at<Vec3b>(h, j)[c];
                }
            }
            //繪制掃描過程
            line(canvas, Point(0, h), Point(width, h), Scalar(255, 255, 0), 2);
        }

        Mat result = Mat::zeros(frame.size(), frame.type());//藍線挑戰最終定格圖
        Linear_Blend(temp, canvas, result); //將兩張圖像進行像素疊加

        //writer.write(temp);//進行視頻保存

        imshow("定格圖像", temp);
        imshow("原視頻幀", canvas);
        imshow("藍線挑戰", result);

        char key = waitKey(10);
        if (key == 27) break;
    }

    capture.release();
    system("pause");
    return 0;
}

總結

本文使用OpenCV C++ 實現抖音特效“藍線挑戰”,關鍵步驟有以下幾點。

1、將圖像進行逐行掃描

2、將掃描到的像素逐行生成定格圖像

3、將定格圖像與原圖像進行像素疊加。

以上就是C++ OpenCV實現抖音"藍線挑戰"特效的詳細內容,更多關於C++ OpenCV藍線挑戰特效的資料請關註WalkonNet其它相關文章!

推薦閱讀: