Bootstrap

基于OpenCV+QT开发超实用的视频编辑器

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实战

OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉和机器学习软件库,可以运行在Linux、Windows、Android和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

OpenCV用C++语言编写,它具有C ++,Python,Java和MATLAB接口,并支持Windows,Linux,Android和Mac OS,OpenCV主要倾向于实时视觉应用,并在可用时利用MMX和SSE指令, 如今也提供对于C#、Ch、Ruby,GO的支持

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均会安装到下

3.2 OpenCV核心类型Mat介绍

Mat类是Opencv中储存图像的一种数据结构。Mat类可以看做是存放矩阵的容器,他包含了两部分,分别是用来存放图片信息的信息头,和一个指向图片储存矩阵的指针。信息头往往占用空间比较小,而且各个图片之间的信息头是完全独立的。而图片储存矩阵往往占用较大的空间,并且可以多个图片的矩阵指针指向同一个内存空间。下面主要减少利用Mat创建矩阵。

3.2.1 利用Mat类的构造函数创建矩阵

Mat类有很多构造函数可以用来创建矩阵结构,并且给与赋值,这里距离介绍一种,其函数定义为

 Mat(int rows, int cols, int type, const Scalar& s);

这个构造函数具有四个参数,其特点是能够定义矩阵的结构并且能够赋予初值

  • 第一个参数表示矩阵的行数

  • 第二个参数表示矩阵的列数

  • 第三个参数表示矩阵储存数据的类型,具有格式 CV_[位数] [有无符号] [数据类型] [通道数],如表示存储数据为8位无符号char类型,并且具有三个通道

  • 第四个参数是一种向量类型的变量,能够给予矩阵赋予初值,这个向量最多有四个维度。

3.2.2 利用成员函数create创建矩阵

使用这种方法创建的矩阵只是一种开辟内存空间,而不能赋予初值

Mat m;
m.create(3, 3, CV_8UC3);

3.3 OpenCV图像处理实战

上面我们功能介绍里面提到了旋转、裁剪都功能均可以利用OpenCV提供的现成函数实现。用到头文件:

#include 
#include 
#include 
#include 

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实战

Qt 是一个1991年由Qt Company开发的跨平台C++图形用户界面应用程序开发框架。它既可以开发GUI程序,也可用于开发非GUI程序,比如控制台工具和服务器。Qt是面向对象的框架,使用特殊的代码生成扩展(称为元对象编译器(Meta Object Compiler, moc))以及一些宏,Qt很容易扩展,并且允许真正地组件编程。

2008年,Qt Company科技被诺基亚公司收购,Qt也因此成为诺基亚旗下的编程语言工具。2012年,Qt被Digia收购。

2014年4月,跨平台集成开发环境Qt Creator 3.1.0正式发布,实现了对于iOS的完全支持,新增WinRT、Beautifier等插件,废弃了无Python接口的GDB调试支持,集成了基于Clang的C/C++代码模块,并对Android支持做出了调整,至此实现了全面支持iOS、Android、WP,它提供给应用程序开发者建立艺术级的图形用户界面所需的所有功能。基本上,Qt 同 X Window 上的 Motif,Openwin,GTK 等图形界面库和 Windows 平台上的 MFC,OWL,VCL,ATL 是同类型的东西。

QT目前在移动端应用较少,主要是由于它新增的包体积,以及C++对于移动端开发者的门槛。但是在桌面端,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平台的编辑工具。方法有很多,我们选择用最少的代码进行最快的实现。

当然里面也会涉及很多细节,包括视频的同步,线程的同步,以及音频合成以及音视频同步。后面的文章我们在介绍这方面的内容。