1. 背景
在生活工作當中,很多時候我們都有裁剪、水印、旋轉等視頻編輯的需求。作為一個程序員,這些需求我們常常用ffmpeg命令工具搞定。但是ffmpeg命令工具可見性和可操作性差。
現在隨着深度學習和人工智能熱門,大量的技術涌現,但opencv作為老牌的圖像視頻庫,一直是在大量的生產環境(包括嵌入式設備)中應用,不管你用什麼深度學習的平台,opencv都是作為圖像圖像領域及佳的選擇,可以很方便的與第三方深度學習框架結合 ,提供基礎算法支持。
而用過或者學習過QT的同學們都知道這是c++程序員必須學習的技能,包括現在熱門的Python也是在大量的應用QT來做界面,QT的設計及其精美,他的信號槽機制很好的將界面與業務隔離開來,並且界面可以使用類似CSS的設置做得很炫,不會像MFC一樣自動生成的代碼和你手寫的代碼融合在一起,而且QT還有一項跨平台能力(包括Windows、Linux、Mac、iphone,Android等平台)。
今天我們基於OpenCV+QT開發一款帶UI界面的視頻編輯工具。在滿足我們功能的基礎上,充分了解和學期opencv及QT技術。
2. 功能介紹
編輯工具的功能主要包含:
- 視頻畫面添加水印;
- 視頻畫面亮度調整;
- 視頻畫面對比度調整;
- 視頻畫面旋轉;
- 視頻畫面鏡像;
- 視頻尺寸調整;
- 視頻圖像模糊;
- 兩路視頻融合。
編輯工具操作界面如下圖所示:
3. OpenCV實戰
3.1 OpenCV 環境搭建
今天我們用的是3.4版本,基於Mac環境搭建。下載源碼後執行如下命令編譯:
git clone https://github.com/opencv/opencv.git
cd opencv
mkdir build
cd build
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local/opencv3 -D BUILD_opencv_world=ON -D WITH_GSTREAMER=OFF -D OPENCV_ENABLE_NONFREE=ON ..
make -j8
sudo make install
如果編譯順利的話,最終opencv相關lib庫,頭文件include均會安裝到/usr/local/opencv3下
3.2 OpenCV核心類型Mat介紹
Mat類是Opencv中儲存圖像的一種數據結構。Mat類可以看做是存放矩陣的容器,他包含了兩部分,分別是用來存放圖片信息的信息頭,和一個指向圖片儲存矩陣的指針。信息頭往往佔用空間比較小,而且各個圖片之間的信息頭是完全獨立的。而圖片儲存矩陣往往佔用較大的空間,並且可以多個圖片的矩陣指針指向同一個內存空間。下面主要減少利用Mat創建矩陣。
3.2.1 利用Mat類的構造函數創建矩陣
Mat類有很多構造函數可以用來創建矩陣結構,並且給與賦值,這裏距離介紹一種,其函數定義為
Mat(int rows, int cols, int type, const Scalar& s);
這個構造函數具有四個參數,其特點是能夠定義矩陣的結構並且能夠賦予初值
- 第一個參數表示矩陣的行數
- 第二個參數表示矩陣的列數
- 第三個參數表示矩陣儲存數據的類型,具有格式 CV_[位數] [有無符號] [數據類型] [通道數],如
CV_8UC3表示存儲數據為8位無符號char類型,並且具有三個通道 - 第四個參數是一種向量類型的變量,能夠給予矩陣賦予初值,這個向量最多有四個維度。
3.2.2 利用成員函數create創建矩陣
使用這種方法創建的矩陣只是一種開闢內存空間,而不能賦予初值
Mat m;
m.create(3, 3, CV_8UC3);
3.3 OpenCV圖像處理實戰
上面我們功能介紹裏面提到了旋轉、裁剪都功能均可以利用OpenCV提供的現成函數實現。用到頭文件:
#include <opencv2/imgcodecs.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;
cv::Mat src1, src2;
cv::Mat dest; //src1.copyTo(dest);
3.3.1 旋轉
//旋轉90度
rotate(dest, dest, ROTATE_90_COUNTERCLOCKWISE);
//旋轉180度
rotate(dest, dest, ROTATE_180);
3.3.2 翻轉
//左右上下翻轉
flip(dest, dest, -1);
//上下翻轉
flip(dest, dest, 1);
//左右翻轉
flip(dest, dest, 0);
3.3.3 修改大小
cv::resize(dest, dest, Size(width, height));
3.3.4 裁剪
dest = dest(Rect(x, y, w, h));
3.3.5 灰度
cvtColor(dest,dest, COLOR_BGR2GRAY);
3.3.6 混合
addWeighted(src2, a, dest, 1-a,0,dest);
3.4 OpenCV視頻IO接口
上面介紹的均為基於圖片的操作,我們要操作的是視頻,其實視頻都是有一幀一幀的圖像組成,圖像知道怎麼處理了就可以開始處理視頻了。OpenCV為我們提供了視頻的IO接口:
3.4.1 打開視頻源
VideoCapture cap1;
bool ret = cap1.open(file);
//獲取幀率
fps = cap1.get(CAP_PROP_FPS);
//獲取視頻寬度
width = cap1.get(CAP_PROP_FRAME_WIDTH);
//獲取視頻高度
height = cap1.get(CAP_PROP_FRAME_HEIGHT);
3.4.1 讀取視頻幀
Mat mat1;
cap1.read(mat1);
4. QT實戰
4.1 環境搭建
基於官方教程https://doc.qt.io/qt-5/macos.html安裝QTCreator即可。
新建視頻編輯工程,在pro配置文件中增加opencv庫路徑:
DEFINES += QT_MULTIMEDIA_LIB QT_WIDGETS_LIB
LIBS += -L"/usr/local/opencv3/lib" \
-lopencv_core \
-lopencv_highgui \
-lopencv_imgproc \
-lopencv_ml \
-lopencv_objdetect \
-lopencv_video \
-lopencv_dnn \
-lopencv_imgcodecs \
-lopencv_shape \
-lopencv_videoio \
4.2 繪製視頻
視頻繪製我們基於QT提供的QOpenGLWidget,通過QOpenGLWidget提供的機制將Mat中的圖像內容渲染到屏幕:
QImage img;
void CustomQOpenGLWidget::SetImage(cv::Mat mat){
QImage::Format fmt = QImage::Format_RGB888;
int pixSize = 3;
if(mat.type() == CV_8UC1){
fmt = QImage::Format_Grayscale8;
pixSize = 1;
}
if(img.isNull() || img.format() != fmt){
delete img.bits();
uchar *buf = new uchar[width()*height() * pixSize];
img = QImage(buf, width(), height(), fmt);
}
Mat des;
cv::resize(mat, des, Size(img.size().width(), img.size().height()));
if(pixSize > 1){
cv::cvtColor(des, des, COLOR_BGR2RGB);
}
memcpy(img.bits(), des.data, des.rows*des.rows*des.elemSize());
update();
}
void CustomQOpenGLWidget::paintEvent(QPaintEvent *e){
QPainter p;
p.begin(this);
p.drawImage(QPoint(0, 0),img);
p.end();
}
5. 總結
至此我們已經基於OpenCV+QT實現了一個簡單實用的視頻編輯工具。當然我們也可以基於ffmpeg的視頻IO + OpenGL實現;也可以基於OpenGl + OpenCV實現Android、iOS平台的編輯工具。方法有很多,我們選擇用最少的代碼進行最快的實現。
當然裏面也會涉及很多細節,包括視頻的同步,線程的同步,以及音頻合成以及音視頻同步。後面的文章我們在介紹這方面的內容。