C++ OpenCV實現(xiàn)抖音"藍(lán)線挑戰(zhàn)"特效
前言
本文將使用OpenCV C++ 實現(xiàn)抖音上的特效“藍(lán)線挑戰(zhàn)”。雖然看起來覺得很牛的樣子,但如果了解其中的原理就非常簡單了。本案例是我自己對于這個特效實現(xiàn)過程的理解,僅供參考。
算法原理可以分為三個流程:
1、將視頻(圖像)從(頂->底)或(左->右)逐行(列)掃描圖像。
2、將掃描完成的行(列)像素重新生成定格圖像。
3、使用原幀圖像像素填充未掃描到的像素。
接下來就具體來看看是如何實現(xiàn)的吧。
一、圖像掃描
首先第一步,拿到一個視頻(很多幀圖像)可以簡單的看成圖像處理。我們需要將圖像從頂?shù)降字鹦羞M(jìn)行像素掃描,當(dāng)然也可以從左到右逐列掃描,這要看你想要實現(xiàn)什么樣的效果。在這里,我實現(xiàn)的是從上到下逐行掃描。效果如圖所示。
二、生成定格圖像
所謂生成定格圖像就是將我們每掃描到的行像素重新進(jìn)行繪制。
//從頂向下逐行掃描圖像 if (h < height) { h++; //將掃描到的圖像像素進(jìn)行重新繪制,生成新圖像 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); }
如圖所示,這是使用上面代碼段實現(xiàn)的逐行掃描生成定格圖像。從效果上看,已經(jīng)得到了我們想要的大致效果了。不過現(xiàn)在的問題是,經(jīng)掃描到的行有像素填充,未掃描到的行還是漆黑一片。所以接下來我們需要做的就是將未掃描到的行用原圖進(jìn)行填充。具體請看源碼注釋。
三、圖像混合
//將兩幅圖像進(jìn)行線性混合 bool Linear_Blend(Mat src1, Mat src2, Mat& dst) { ?? ?/* ?? ?參數(shù)說明: ?? ?src1:生成的定格圖像。由于生成的定格圖像是從頂往下逐行掃描,故在掃描線下的像素為0 ?? ?src2:原視頻幀圖像 ?? ?dst:新生成的定格圖像。 ?? ?算法原理:經(jīng)掃描到的像素用src1進(jìn)行填充,未掃描到的像素用src2進(jìn)行填充,這樣就可以得到我們所要的效果了。 ?? ?*/ ?? ?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; }
四、效果顯示
如上圖所示,至此我們已經(jīng)完成了案例所想要的效果。請參考源碼,注釋也比較詳細(xì)了。
五、源碼
#include<iostream> #include<opencv2/opencv.hpp> using namespace std; using namespace cv; /* 抖音特效:藍(lán)線挑戰(zhàn) 算法原理: ?? ?1、將視頻(圖像)從(頂->底)或(左->右)逐行(列)掃描圖像。 ?? ?2、將掃描完成的行(列)像素重新生成定格圖像 ?? ?3、使用原幀圖像像素填充未掃描到的像素 */ //將兩幅圖像進(jìn)行線性混合 bool Linear_Blend(Mat src1, Mat src2, Mat& dst) { ?? ?/* ?? ?參數(shù)說明: ?? ?src1:生成的定格圖像。由于生成的定格圖像是從頂往下逐行掃描,故在掃描線下的像素為0 ?? ?src2:原視頻幀圖像 ?? ?dst:新生成的定格圖像。 ?? ?算法原理:經(jīng)掃描到的像素用src1進(jìn)行填充,未掃描到的像素用src2進(jìn)行填充,這樣就可以得到我們所要的效果了。 ?? ?*/ ?? ?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;//定義變量,代表當(dāng)前掃描高度 ?? ?//用于生成定格照 ?? ?Mat temp = Mat::zeros(Size(width, height), CV_8UC3); ?? ? ?? ?Mat frame; ?? ?while (capture.read(frame)) ?? ?{ ?? ??? ?//將圖像拷貝一份,用于每幀更新 ?? ??? ?Mat canvas = frame.clone(); ?? ??? ?//從頂向下逐行掃描圖像 ?? ??? ?if (h < height) ?? ??? ?{ ?? ??? ??? ?h++; ?? ??? ??? ?//將掃描到的圖像像素進(jìn)行重新繪制,生成新圖像 ?? ??? ??? ?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());//藍(lán)線挑戰(zhàn)最終定格圖 ?? ??? ?Linear_Blend(temp, canvas, result); //將兩張圖像進(jìn)行像素疊加 ?? ??? ?//writer.write(temp);//進(jìn)行視頻保存 ?? ??? ?imshow("定格圖像", temp); ?? ??? ?imshow("原視頻幀", canvas); ?? ??? ?imshow("藍(lán)線挑戰(zhàn)", result); ?? ??? ?char key = waitKey(10); ?? ??? ?if (key == 27) break; ?? ?} ?? ?capture.release(); ?? ?system("pause"); ?? ?return 0; }
總結(jié)
本文使用OpenCV C++ 實現(xiàn)抖音特效“藍(lán)線挑戰(zhàn)”,關(guān)鍵步驟有以下幾點。
1、將圖像進(jìn)行逐行掃描
2、將掃描到的像素逐行生成定格圖像
3、將定格圖像與原圖像進(jìn)行像素疊加。
以上就是C++ OpenCV實現(xiàn)抖音"藍(lán)線挑戰(zhàn)"特效的詳細(xì)內(nèi)容,更多關(guān)于C++ OpenCV藍(lán)線挑戰(zhàn)特效的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
虛函數(shù)表-C++多態(tài)的實現(xiàn)原理解析
這篇文章主要介紹了虛函數(shù)表-C++多態(tài)的實現(xiàn)原理,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02C語言數(shù)據(jù)結(jié)構(gòu)之單向鏈表詳解分析
鏈表可以說是一種最為基礎(chǔ)的數(shù)據(jù)結(jié)構(gòu)了,而單向鏈表更是基礎(chǔ)中的基礎(chǔ)。鏈表是由一組元素以特定的順序組合或鏈接在一起的,不同元素之間在邏輯上相鄰,但是在物理上并不一定相鄰。在維護(hù)一組數(shù)據(jù)集合時,就可以使用鏈表,這一點和數(shù)組很相似2021-11-11C++11 call_once 和 once_flag的使用與區(qū)別
本文主要介紹了C++11 call_once 和 once_flag的使用與區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06c++ 求數(shù)組最大最小值函數(shù)的實現(xiàn)
這篇文章主要介紹了c++ 求數(shù)組最大最小值函數(shù)的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07