OpenCV Matlab生成視頻倒放功能
引言
相信不少朋友在各大短視頻平臺看到很多運(yùn)動健身達(dá)人的訓(xùn)練視頻,本人比較喜歡的運(yùn)動達(dá)人有搬磚小偉、大師兄歐克等,街頭徒手健身實則美妙,既能考驗人的意志 ,又能強(qiáng)健體魄。不得不說,運(yùn)動(UFC競技、武術(shù)、拳擊)和藝術(shù)(書法、繪畫、歌曲、舞蹈)著實能給人帶來直觀的真實體驗,也能激發(fā)自身的運(yùn)動潛力,對于經(jīng)常久坐從事腦力運(yùn)動的人,他們的閑暇時間除了關(guān)注科技、軍事、生活娛樂,還應(yīng)該接受藝術(shù)和體育的熏陶,適當(dāng)?shù)剡M(jìn)行體力勞動也是長壽的秘訣,有利于個人和社會的可持續(xù)發(fā)展。每天鍛煉一小時,健康工作五十年,幸福生活一輩子?。?!
1、需求分析
互聯(lián)網(wǎng)上海量的文本(plain text)、圖片(picture or image)、聲音(audio)、視頻(video)等文件大量涌入,層出不窮,這些數(shù)據(jù)在網(wǎng)絡(luò)上存儲、傳輸和下載,各種硬件設(shè)備、傳感器技術(shù)的發(fā)展使得數(shù)據(jù)獲取方式變得越來越多樣化。這里關(guān)注視頻文件,視頻是由一系列連續(xù)的幀按照時間順序組合排列而形成的,因此它的本質(zhì)還是一個又一個幀,一個幀就是一個畫面,一個畫面就是一張圖片,因此連續(xù)流暢的視頻需要保證每秒的幀數(shù)大于等于24,而對于經(jīng)常玩大型網(wǎng)絡(luò)三維游戲(如吃雞PLAYERUNKNOWN'S BATTLEGROUNDS、CSGO、CF)的玩家而言,他們可能對這個更有了解,就是游戲?qū)崟r畫面都會顯示當(dāng)前幀率FPS(Frame Per Second,每秒的幀數(shù)),更高的幀率(普通電腦60以上,游戲本150~220,發(fā)燒本可能達(dá)到300)能給高端玩家?guī)砭碌挠螒蝮w驗。因此視頻處理的本質(zhì)源于對各張圖片(每幀畫面)的處理。
與此對應(yīng),視頻倒放的核心思想就是將原始視頻中的圖片倒序,主要分為兩個步驟:
(1)獲取原始視頻的每一幀圖片以從小到大的序號命名后保存到本地;
(2)將所有圖片按照從大到小的順序,設(shè)置與原始視頻同樣或自定義的幀率FPS,寫入視頻。
2、環(huán)境配置(Win11+ VS 2015 + OpenCV 2.4.9 / Matlab R2021a)

Win 11 64位

Visual Studio 2015

OpenCV 2.4.9

Matlab R2021a
3、OpenCV實現(xiàn)視頻倒放(C++)
3.1 輸入原始視頻(帶聲音)
原始視頻文件VID_20210801_205259.mp4展示了我國短跑飛人蘇炳添在2020東京奧運(yùn)會男子100米半決賽中以9.83秒刷新亞洲紀(jì)錄,一站封神,他創(chuàng)造了亞洲人百米賽跑的記錄,成為了首位跑進(jìn)10秒大關(guān)的亞洲本土選手、首位踏上世界大賽百米飛人大戰(zhàn)決賽的亞洲選手、亞洲紀(jì)錄保持者。身高1米72的蘇神在百米賽道上要比博爾特多跑7步,因此他只有付出比別人更多的努力,才能和其他選手站到同一起跑線上。讓我們向蘇神致敬,向蘇神學(xué)習(xí),功夫不負(fù)有心人,勇敢拼搏無極限,不設(shè)限的人生更精彩!?。?/p>


3.2 原始視頻轉(zhuǎn)聲音(mp4轉(zhuǎn)mp3)
不少帶聲音視頻的后綴名往往都是.mp4,那么如何獲取里面的音頻呢?其實有一種簡單取巧的方法,只需將mp4視頻文件的后綴名.mp4改為mp3音頻文件的后綴名.mp3就可以了,實測證明該辦法具有一定的有效性。
3.3 OpenCV代碼
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
string GetFileNameString(string inputfilename)
{
string s = "";
int length = 0;
while (inputfilename[length] != '\0')
{
length++;
}
for (length = length - 1; length >= 0; length--)
{
if (inputfilename[length] == '\\')
break;
}
while (inputfilename[++length] != '\0')
{
if (inputfilename[length] != '.')
s += inputfilename[length];
else
break;
}
return s;
}
string GetFolderString(string inputfilename)//Obtain Path in FileNameWithPathString
{
string s = "";
int length = 0;
while (inputfilename[length] != '\0')
{
length++;
}
for (length = length-1; length >= 0; length--)
{
if (inputfilename[length] == '\\')
break;
}
for (int i = 0; i <= length; i++)
s += inputfilename[i];
return s;
}
string Replace_folder(string inputfilename) // replace \\ to /
{
string s = "";
int length = 0;
bool flag = true;
while (inputfilename[length] != '\0')
{
if (inputfilename[length] != '\\')
{
s += inputfilename[length];
}
else
{
s += '/';
}
length++;
}
return s;
}
int main()
{
string inputVideofilename = "F:\\Users\\VID_20210801_205259.mp4";
string videopath = GetFolderString(inputVideofilename);
string picspath = videopath + ("pics_" + GetFileNameString(inputVideofilename));
string command = "mkdir " + picspath;
system(command.c_str());//Create the folder which is named pics in current folder
string picfolder = Replace_folder(picspath) + "/", suffixname = ".jpg";
cout << "圖片和視頻保存位置為:"+picfolder << endl;
VideoCapture inputVideo(inputVideofilename);//Obtain input video
if (!inputVideo.isOpened())
{
cout << "Could not open the input video." << inputVideofilename << endl;
return -1;
}
else
{
double width = inputVideo.get(CV_CAP_PROP_FRAME_WIDTH); // width of frame
double height = inputVideo.get(CV_CAP_PROP_FRAME_HEIGHT); //height of frame
double frameRate = inputVideo.get(CV_CAP_PROP_FPS); //frame per second
double totalFrames = inputVideo.get(CV_CAP_PROP_FRAME_COUNT); //total number of frames
cout << "視頻寬度=" << width << endl;
cout << "視頻高度=" << height << endl;
cout << "視頻總幀數(shù)=" << totalFrames << endl;
cout << "幀率=" << frameRate << endl;
namedWindow("RGB視頻", CV_WINDOW_NORMAL);
namedWindow("B通道", CV_WINDOW_NORMAL);
namedWindow("G通道", CV_WINDOW_NORMAL);
namedWindow("R通道", CV_WINDOW_NORMAL);
namedWindow("被Canny后的視頻", CV_WINDOW_NORMAL);
Mat lastframe;
int i = 0;
while (1)
{
Mat frame;// 定義一個Mat變量,用于存儲每一幀的圖像
inputVideo >> frame;//讀取當(dāng)前幀
if (frame.data)
{
i++;
int num_channels = frame.channels();//通道數(shù)
Mat channels[3];
split(frame, channels);
Mat zeroRChannel = channels[2].clone();//將R通道全部置0
zeroRChannel.setTo(0);
Mat zeroGChannel = channels[1].clone();//將G通道全部置0
zeroGChannel.setTo(0);
Mat zeroBChannel = channels[0].clone();//將B通道全部置0
zeroBChannel.setTo(0);
Mat BChannels[3] = { channels[0] , zeroGChannel , zeroRChannel };
Mat mergedBImage;
merge(BChannels, 3, mergedBImage);
Mat GChannels[3] = { zeroBChannel , channels[1] , zeroRChannel };
Mat mergedGImage;
merge(GChannels, 3, mergedGImage);
Mat RChannels[3] = { zeroBChannel , zeroGChannel , channels[2] };
Mat mergedRImage;
merge(RChannels, 3, mergedRImage);
Mat edges;
cvtColor(frame, edges, COLOR_BGR2GRAY);
blur(edges, edges, Size(5, 5));
Canny(edges, edges, 0, 30, 3);
imshow("RGB視頻", frame);//顯示當(dāng)前幀
imshow("B通道", mergedBImage);
imshow("G通道", mergedGImage);
imshow("R通道", mergedRImage);
imshow("被Canny后的視頻", edges);//顯示經(jīng)過處理后的當(dāng)前幀
imwrite(picfolder + to_string(i) + suffixname, frame);
}
else
break;
waitKey(2);
}
cout << i << "張圖片生成成功,開始逆序合成視頻!" << endl;
Mat frame;
Mat src0 = imread(picfolder + to_string(i) + suffixname);
Size size = src0.size();
VideoWriter writer;
writer.open(Replace_folder(videopath) + GetFileNameString(inputVideofilename)+"_NiZhuan.avi", CV_FOURCC('M', 'J', 'P', 'G'), frameRate, size, true);
int j = i;
for (; j >0; j--)
{
string path = picfolder + to_string(j) + suffixname;
Mat src = imread(path);
if (!src.empty())
{
writer.write(src);
cout << "正在合成第" << j << "張照片" << endl;
}
else
break;
}
if (j == 0)
std::cout << "合成逆序視頻 Successed!" << std::endl;
else
std::cout << "合成逆序視頻 Failed!" << std::endl;
return 0;
}
}
3.4 OpenCV運(yùn)行結(jié)果

(a)前20米

(b)后80米

(c)開始讀取視頻

(d)讀取視頻結(jié)束

(e)結(jié)果文件

(f)圖片文件夾

代碼支持mp4、wmv格式的輸入視頻,在原始視頻文件夾中會看到生成的視頻文件結(jié)果VID_20210801_205259_NiZhuan.avi,將avi后綴名改為mp4后綴名也可播放。

4、Matlab實現(xiàn)視頻倒放
首先介紹一個Matlab生成動態(tài)視頻示例:

Z = peaks;
surf(Z);
axis tight manual
set(gca,'nextplot','replacechildren');
v = VideoWriter('peaks.avi');
v.Quality = 95;
v.FrameRate = 40;
open(v);
for k = 1:200
surf(sin(2*pi*k/20)*Z,Z)
frame = getframe(gcf);
writeVideo(v,frame);
end
close(v);
4.1 Matlab代碼
4.1.1 Matlab讀取視頻并播放(三選一)
vidObj = VideoReader('1234.wmv');
vidWidth = vidObj.Width;
vidHeight = vidObj.Height;
vidFps = vidObj.FrameRate;
% 第一種播放方式
while hasFrame(vidObj)
vidFrame = readFrame(vidObj);
imshow(vidFrame)
pause(1/vidObj.FrameRate);
end
% 第二種播放方式
currAxes = axes;
while hasFrame(vidObj)
vidFrame = readFrame(vidObj);
image(vidFrame, 'Parent', currAxes);
currAxes.Visible = 'off';
pause(1/vidFps);
end
% 第三種播放方式(推薦使用)
mov = struct('cdata',zeros(vidHeight,vidWidth,3,'uint8'),'colormap',[]);
vidObj.CurrentTime = 2.5; % 可設(shè)置開始時間
k = 1;
while hasFrame(vidObj)
mov(k).cdata = readFrame(vidObj);
imwrite(mov(k).cdata,['pics/', num2str(k),'.jpg']);
k = k+1;
end
hf = figure;
set(hf,'position',[90 60 vidWidth vidHeight]);
movie(hf,mov,1,vidFps);
4.1.2 Matlab讀取視頻并逆轉(zhuǎn)
需要在原視頻文件夾新建一個pics文件夾,然后運(yùn)行以下代碼(實測適用于.mp4和.wmv格式的輸入視頻文件):VideoProcessTest.m
filepath = 'D:/Program Files (x86)/MATLAB/myworkspace/';
filename = 'VID_20210801_205259';
suffixname = '.mp4';
vidObj = VideoReader([filepath,filename,suffixname]);
vidWidth = vidObj.Width;
vidHeight = vidObj.Height;
vidFps = vidObj.FrameRate;
% vidObj.CurrentTime = 2.5; % 可設(shè)置開始時間
k = 1;
while hasFrame(vidObj)
frame = readFrame(vidObj);
imwrite(frame,['pics/', num2str(k),'.jpg']);
k = k+1;
end
v_all = VideoWriter([filepath,filename,'_NiZhuanMovie_ALL.avi']);
v_all.Quality = 95;
v_all.FrameRate = vidFps;
open(v_all);
v_rgb = VideoWriter([filepath,filename,'_NiZhuanMovie_RGB.avi']);
v_rgb.Quality = 95;
v_rgb.FrameRate = vidFps;
open(v_rgb);
v_r = VideoWriter([filepath,filename,'_NiZhuanMovie_R.avi']);
v_r.Quality = 95;
v_r.FrameRate = vidFps;
open(v_r);
v_g = VideoWriter([filepath,filename,'_NiZhuanMovie_G.avi']);
v_g.Quality = 95;
v_g.FrameRate = vidFps;
open(v_g);
v_b = VideoWriter([filepath,filename,'_NiZhuanMovie_B.avi']);
v_b.Quality = 95;
v_b.FrameRate = vidFps;
open(v_b);
set(gca,'nextplot','replacechildren');
for i = k-1:-1:1
filename = ['D:/Program Files (x86)/MATLAB/myworkspace/pics/', num2str(i),'.jpg'];
img = imread(filename);
[m,n]=size(img(:,:,1));
zero=zeros(m,n);
rgb_r=img(:,:,1);
rgb_g=img(:,:,2);
rgb_b=img(:,:,3);
R_img=cat(3,rgb_r,zero,zero);
G_img=cat(3,zero,rgb_g,zero);
B_img=cat(3,zero,zero,rgb_b);
RGB_img=cat(3,rgb_r,rgb_g,rgb_b);
subplot(2,2,1),imshow(R_img),title('紅色分量');
subplot(2,2,2),imshow(G_img),title('綠色分量');
subplot(2,2,3),imshow(B_img),title('藍(lán)色分量');
subplot(2,2,4),imshow(RGB_img);
frame = getframe(gcf);
imwrite(frame.cdata,['./pics/ALL',num2str(i),'.jpg']);
imwrite(RGB_img,['./pics/RGB',num2str(i),'.jpg']);
imwrite(R_img,['./pics/R',num2str(i),'.jpg']);
imwrite(G_img,['./pics/G',num2str(i),'.jpg']);
imwrite(B_img,['./pics/B',num2str(i),'.jpg']);
writeVideo(v_all,frame.cdata);
writeVideo(v_rgb,RGB_img);
writeVideo(v_r,R_img);
writeVideo(v_g,G_img);
writeVideo(v_b,B_img);
end
close(v_all);
close(v_rgb);
close(v_r);
close(v_g);
close(v_b);
4.2 Matlab運(yùn)行結(jié)果





R分量

G分量

B分量

RGB視頻
5、總結(jié)及應(yīng)用
本文主要通過利用OpenCV和Matlab兩種工具來實現(xiàn)視頻中圖片R、G、B三分量的提取、保存和逆轉(zhuǎn),同時視頻加工本質(zhì)是對圖片幀的處理,利用這兩種圖像處理API還可實現(xiàn)視頻截取(通過幀率fps和時間計算所需的幀并拼接成視頻)、多張圖片合成自定義視頻、多個視頻拼接、分類、目標(biāo)提取追蹤、特征檢測、視頻邊緣檢測、添加字幕等功能,可應(yīng)用于短視頻剪輯、創(chuàng)作、應(yīng)用系統(tǒng)演示、錄課、科研、公共安全等多個領(lǐng)域。
到此這篇關(guān)于OpenCV Matlab生成倒放視頻的文章就介紹到這了,更多相關(guān)OpenCV Matlab視頻倒放內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言數(shù)據(jù)結(jié)構(gòu)順序表中的增刪改(尾插尾刪)教程示例詳解
這篇文章主要為大家介紹了C語言數(shù)據(jù)結(jié)構(gòu)順序表中的增刪改教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02
json格式解析和libjson的用法介紹(關(guān)于cjson的使用方法)
下面小編就為大家?guī)硪黄猨son格式解析和libjson的用法介紹(關(guān)于cjson的使用方法)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-12-12

