OpenCV Matlab生成視頻倒放功能

引言

相信不少朋友在各大短視頻平臺看到很多運動健身達人的訓練視頻,本人比較喜歡的運動達人有搬磚小偉大師兄歐克等,街頭徒手健身實則美妙,既能考驗人的意志 ,又能強健體魄。不得不說,運動UFC競技武術拳擊)和藝術書法繪畫歌曲舞蹈)著實能給人帶來直觀的真實體驗,也能激發自身的運動潛力,對於經常久坐從事腦力運動的人,他們的閑暇時間除瞭關註科技軍事生活娛樂還應該接受藝術和體育的熏陶,適當地進行體力勞動也是長壽的秘訣,有利於個人和社會的可持續發展。每天鍛煉一小時,健康工作五十年,幸福生活一輩子!!!

1、需求分析

互聯網上海量的文本(plain text)圖片(picture or image)聲音(audio)視頻(video)等文件大量湧入,層出不窮,這些數據在網絡上存儲、傳輸和下載,各種硬件設備、傳感器技術的發展使得數據獲取方式變得越來越多樣化。這裡關註視頻文件,視頻是由一系列連續的幀按照時間順序組合排列而形成的,因此它的本質還是一個又一個幀,一個幀就是一個畫面,一個畫面就是一張圖片,因此連續流暢的視頻需要保證每秒的幀數大於等於24,而對於經常玩大型網絡三維遊戲(如吃雞PLAYERUNKNOWN'S BATTLEGROUNDSCSGOCF)的玩傢而言,他們可能對這個更有瞭解,就是遊戲實時畫面都會顯示當前幀率FPS(Frame Per Second,每秒的幀數),更高的幀率(普通電腦60以上,遊戲本150~220,發燒本可能達到300)能給高端玩傢帶來精致的遊戲體驗。因此視頻處理的本質源於對各張圖片(每幀畫面)的處理。
與此對應,視頻倒放的核心思想就是將原始視頻中的圖片倒序,主要分為兩個步驟:
(1)獲取原始視頻的每一幀圖片以從小到大的序號命名後保存到本地
(2)將所有圖片按照從大到小的順序,設置與原始視頻同樣或自定義的幀率FPS,寫入視頻

2、環境配置(Win11+ VS 2015 + OpenCV 2.4.9 / Matlab R2021a)

Win 11 64位

Visual Studio 2015

OpenCV 2.4.9

Matlab R2021a

3、OpenCV實現視頻倒放(C++)

3.1 輸入原始視頻(帶聲音)

原始視頻文件VID_20210801_205259.mp4展示瞭我國短跑飛人蘇炳添在2020東京奧運會男子100米半決賽中以9.83秒刷新亞洲紀錄,一站封神,他創造瞭亞洲人百米賽跑的記錄,成為瞭首位跑進10秒大關的亞洲本土選手、首位踏上世界大賽百米飛人大戰決賽的亞洲選手、亞洲紀錄保持者。身高1米72的蘇神在百米賽道上要比博爾特多跑7步,因此他隻有付出比別人更多的努力,才能和其他選手站到同一起跑線上。讓我們向蘇神致敬,向蘇神學習,功夫不負有心人,勇敢拼搏無極限,不設限的人生更精彩!!!

3.2 原始視頻轉聲音(mp4轉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 << "視頻總幀數=" << 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;//讀取當前幀
			if (frame.data)
			{
				i++;
				int num_channels = frame.channels();//通道數
				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);//顯示當前幀
				imshow("B通道", mergedBImage);
				imshow("G通道", mergedGImage);
				imshow("R通道", mergedRImage);
				imshow("被Canny後的視頻", edges);//顯示經過處理後的當前幀
				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運行結果

(a)前20米

(b)後80米

(c)開始讀取視頻

(d)讀取視頻結束

(e)結果文件

(f)圖片文件夾

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

4、Matlab實現視頻倒放

首先介紹一個Matlab生成動態視頻示例:

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; % 可設置開始時間
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讀取視頻並逆轉

需要在原視頻文件夾新建一個pics文件夾,然後運行以下代碼(實測適用於.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; % 可設置開始時間
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('藍色分量');
    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運行結果


R分量

G分量

B分量

RGB視頻

5、總結及應用

本文主要通過利用OpenCV和Matlab兩種工具來實現視頻中圖片R、G、B三分量的提取、保存和逆轉,同時視頻加工本質是對圖片幀的處理,利用這兩種圖像處理API還可實現視頻截取(通過幀率fps和時間計算所需的幀並拼接成視頻)、多張圖片合成自定義視頻多個視頻拼接分類目標提取追蹤特征檢測視頻邊緣檢測添加字幕等功能,可應用於短視頻剪輯、創作、應用系統演示、錄課、科研、公共安全等多個領域。

到此這篇關於OpenCV Matlab生成倒放視頻的文章就介紹到這瞭,更多相關OpenCV Matlab視頻倒放內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: