【征文】基于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. 功能介绍

编辑工具的功能主要包含:

  1. 视频画面添加水印;
  2. 视频画面亮度调整;
  3. 视频画面对比度调整;
  4. 视频画面旋转;
  5. 视频画面镜像;
  6. 视频尺寸调整;
  7. 视频图像模糊;
  8. 两路视频融合。

编辑工具操作界面如下图所示:

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平台的编辑工具。方法有很多,我们选择用最少的代码进行最快的实现。

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