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平台的编辑工具。方法有很多,我们选择用最少的代码进行最快的实现。
当然里面也会涉及很多细节,包括视频的同步,线程的同步,以及音频合成以及音视频同步。后面的文章我们在介绍这方面的内容。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。