一.先讲讲opencv和ffmpeg之间的关系
1.1它们之间的联系
我们知道opencv主要是用来做图像处理的,但也包含视频解码的功能,而在视频解码部分的功能opencv是使用了ffmpeg。所以它们都是可以处理图像和视频的编解码,我个人感觉两个的侧重点不一样。
1.2它们之间的区别
这就要提上面我所说的它们的侧重点是不一样。
OpenCV专注处理图像,以及图像相关的处理应用,不严谨地可以认为是PhotoShop。Opencv主要做一些识别 跟踪机器视觉应用。
FFmpeg专注处理视频、音频的编解码、转换等,不严谨地可以认为是格式工厂和PotPlayer的结合体。主要应用是编解码,各种格式转换。
二.opencv和ffmpeg版本匹配
在搭建环境之前,我着重强调下两者版本匹配的问题,因为在我本人搭建环境的时候由于版本不匹配问题踩了太多的坑,怕了。所以一开始把这些干扰因素处理好,后面可以省很多事。
2.1如果版本不匹配可能会出哪些问题呢?
会在编译opencv的时候出现各种识别不到某些定义的错误,导致编译不过。
具体有:
2.1.1问题一
error: ‘CODEC_ID_H264’ was not declared in this scope
{ CODEC_ID_H264, MKTAG('H', '2', '6', '4') }
网友给的就解决方法是,编译的时候不开启ffmpeg的编译:
-D WITH_FFMPEG=OFF
我试过,可以编译通过,但同时也如同自断一臂,给使用ffmpeg功能(视频处理类)带来巨大麻烦,或者干脆是用不了。因此我认为,最好的办法还是,版本匹配,完美编译,完美使用,这才用的安心,根本之道。
2.1.2问题二
error: 'CODEC_FLAG_GLOBAL_HEADER' was not declared in this scope
这种解决办法是,补充添加未定义的宏,这个如果你了解不深刻里面的原理机制,一般人想不到,也只有是大佬才能理解并找到的方法。
总之基本上出问题的都是ffmpeg和opencv版本匹配问题,为啥我们不一开始就做好呢,是吧。
2.2如何知道ffmpeg和opencv的匹配的版本
为了解决这个问题,本人也是走了一大圈,才整清楚,可能最终说起来也就几句话的事,但过程是非常曲折的,不过正是经历了这种过程,才更加深刻吧,也同时也锻炼了下自己的耐心,加强了一点专研的精神,也不错哈!下面进入主题!
根据网友提供的方法进入opencv源码目录
按理说这里面应该会有一个叫ffmpeg_version.cmake的文件,里面会列出opencv相对应的ffmpeg版本号,类似下图:
那些带数字的就是某个版本的ffmpeg下,各个组件对应的版本,我们到ffmpeg官网去FFmpeg下载对应的版本源码即可。
上图只是为了说明问题,并没有去专门匹配版本号哈。
现在问题是,我的文件下没有ffmpeg_version.cmake啊,当时也是一脸懵。没办法,我就打开ffmpeg.cmake看看,是否能发现什么线索。
看到里面的内容,有几条线索,图片上已经标注出来了,这里总结下:
1.可以看到opencv需要的ffmpeg版本号对应的tag标签,以及commit id号;
2.ffmpeg.cmake其实是从上述网址***raw.git***上下载下来的;
为什么我的电脑上没有下载下来,一波查询,发现是访问不了该网址,网友也支招,在host文件加入ip什么的。我的办法是用github.com替代,其实是一样的。
进入另外的链接:
GitHub - opencv/opencv_3rdparty: OpenCV - 3rdparty
点击进去
你会发现opencv的commit id也匹配上了。
这样就找到了。关于版本匹配就讲这么多,当了解了整个过程是不是简单许多了,但如果不是自己一步一步摸索出来的,你是体会不到那种,遇到问题的焦躁以及解决完问题豁然开朗的感觉。
接下来就是分别下载ffmpeg和opencv的源码了,ffmpeg官网链接上面提供了,Releases - OpenCV是opencv的源码下载链接,自己选好版本,切记它们之间的版本匹配,要不我上面唠叨那么多是为了啥,然后编译安装,咱们继续往下走。
三.ffmpeg的安装流程
安装流程
这里建议先安装ffpmeg再安装opencv,因为安装opencv会用到ffmpeg。
先解压ffmpeg源码,然后进入到源码目录,输入如下指令:
这里说下,输入指令前先对支持库的安装
sudo apt-get install -y autoconf automake build-essential git libass-dev libfreetype6-dev libsdl2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev libxcb-xfixes0-dev pkg-config texinfo wget zlib1g-dev
再是参数配置指令输入
./configure --disable-x86asm --enable-shared --prefix=/usr/local/ffmpeg
参数含义是把ffmpeg安装到/usr/local/ffmpeg目录下,完了之后再输入
make
中间编译的时间会有点长,依电脑的性能而定
sudo make install
所有走完之后,会在/usr/local目录下看到ffmpeg文件夹,如下:
ffmpeg的工具和动态库都在里面了,为了编译时能找到ffmpeg的动态库,还要做如下处理:
创建文件ffmpeg.conf
sudo vi /etc/ld.so.conf.d/ffmpeg.conf
输入如下内容(ffmpeg动态库的路径)
/usr/local/ffmpeg/lib
最后使能生效
sudo ldconfig
看到下面内容说明安装成功
可以测试使用ffplayg工具播放一段视频
/usr/local/ffmpeg/bin/ffplay **/**/***.mp4 (视频文件目录)
当然我们可以将ffmpeg的bin添加到全局变量,这样可以随时调用,不用加上绝对地址,编辑 profile 文件(sudo vi /etc/profile)在文件末尾添加:
export FFMPEG_HOME=/usr/local/ffmpeg
export PATH=$FFMPEG_HOME/bin:$PATH
ffmpeg的安装到此结束,接下来是opencv的安装,继续往下走!
四.opencv的安装流程
安装流程
同样,将opencv的压缩包解压,进入源码目录,创建一个pc_build(如果以后要使用交叉编译,就换一个arm_build,扯远了)文件夹:
这里我是使用cmake图形化编译的,先安装一个cmake工具:
sudo apt-get install cmake cmake-qt-gui cmake-curses-gui
然后在pc_build目录下执行
cmake-gui
出现界面
这里仅演示ubuntu环境,不说交叉编译的
点击finish,然后再点击configure,配置一些参数,如下图
出现下图框出的版本信息,说明识别到了ffmpeg,如果没识别到怎么办呢?留个悬念后面讲。
最后点击generate。
小提示:
cmake过程中如果遇到卡住的情况,缺少文件需要下载,却一直下载不下来的情况
如:opencv源码安装文件下载问题:ippicv_2017u3_lnx, face_landmark_model.dat, tiny-dnn
配置:打开${opencv_folder}/3rdparty/ippicv/ippicv.cmake,
第47行 "https://raw.githubusercontent.com/opencv/opencv_3rdparty/${IPPICV_COMMIT}/ippicv/",
改成:"file://${path}",比如我的就是"file:///home/downloads/"
下载地址:https://github.com/opencv/opencv_3rdparty/tree/ippicv/
按照自己缺少的版本下载,下载慢的话可以把链接拷贝到迅雷里面下载。
开始编译
回到命令界面,先不要急着输入 make。首先在源码目录 3rdparty/protobuf/src/google/protobuf/stubs/common.cc 这个文件下第 33 行添加#define HAVE_PTHREAD 宏定义才可以编译的过。具体原因是 HAVE_PTHREAD 宏定义了 pthread 库。
cd ..
// 返回 opencv 源码顶层目录
vi 3rdparty/protobuf/src/google/protobuf/stubs/common.cc
再进入pc_build目录输入指令
make -j 16
漫长的等待编译编译完之后,安装
sudo make install
所有成功之后会在/usr/local目录下生成相应的文件
里面包含头文件和动态库
同样为了找到opencv的动态库做如下处理
创建文件opencv.conf
sudo vi /etc/ld.so.conf.d/opencv.conf
输入如下内容(ffmpeg动态库的路径)
/usr/local/lib
最后使能生效
sudo ldconfig
到此opencv编译安装完成!
前面挖的坑
前面提到一个坑,是在cmake的时候没有出现ffmpeg的版本怎么办,我的做法是先编译,不管,编译通过之后,将ffmpeg的pkgconfig里面的所有pc文件复到/usr/local/lib/pkgconfig里面,这文件里面是opencv的pc文件。
sudo cp /usr/local/ffmpeg/lib/pkgconfig/*.pc /usr/local/lib/pkgconfig
然后按上面的操作在重新编译一次opencv,是不是有点崩溃,没办法。
当然我也想了一些操作,有网友说安装完ffmpeg后做如下操作,再去编译opencv
sudo vi /etc/profile
添加
export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:/usr/local/ffmpeg/lib/pkgconfig
让环境变量生效
source /etc/profile
我这么处理了,貌似没用,也不知道怎么回事,有知道的网友,还请麻烦留言告知下。
到这里ffmpeg和opencv都安装好了,是不是按捺不住内心的激动,跃跃欲试。接下来走几个小栗子,磨磨刀,哈哈哈。
五.实践操作
是骡子是马拉出来溜溜,前面做了这么多就是为了学习和实践操作,接下来就写几个小程序跑一跑功能。
5.1显示一张图片
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc,char** argv)
{
Mat image = imread("11.png", 1 );//加载
cv::namedWindow("picture",CV_WINDOW_AUTOSIZE);
cv::imshow("picture", image);//显示图片
waitKey(5000);//等待
return 0;
}
5.2播放一段视频
#include<opencv2/opencv.hpp>
using namespace cv;
int main()
{
//从摄像头读取视频
VideoCapture capture("video.mp4");
//循环显示每一帧
while (1)
{
Mat frame;//定义一个Mat变量,用于存储每一帧的图像
capture >> frame;//读取当前帧
imshow("读取视频帧", frame);//显示当前帧
waitKey(30);//延时30ms
}
system("pause");
return 0;
}
5.3使用笔记本内置的摄像头,拍摄一段视频并保存
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/core/core.hpp"
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc,char** argv)
{
//打开电脑摄像头
VideoCapture capture(0);
if(!capture.isOpened()){
cout<<"error"<<endl;
waitKey(0);
return 0;
}
//获得分辨率
int w = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_WIDTH));
int h = static_cast<int>(capture.get(CV_CAP_PROP_FRAME_HEIGHT));
cout<<"w="<<w<<endl;
cout<<"h="<<h<<endl;
Size videoSize(w,h);
VideoWriter writer;
writer.open("video.mp4",CV_FOURCC('M','J','P','G'),25,videoSize);
if(!writer.isOpened()){
cout<<"fail"<<endl;
return -1;
}
Mat frame;
int key;
char startorstop=1;
char flag=0;
while(1){
capture >> frame;
if(key == 32){//按下空格开始录制、暂停录制 可以来回切换
startorstop = 1-startorstop;
if(startorstop == 0){
flag = 1;
}
}
if(key == 27){//按下ESC退出整个程序,保存视频文件到磁盘
cout << "exit" << endl;
break;
}
if(startorstop == 0 && flag == 1){
writer << frame;
cout << "recording" << endl;
}else if(startorstop == 1){
flag = 0;
cout << "end recording" << endl;
}
imshow("picture",frame);
key=waitKey(100);
cout<<"key="<<key<<endl;
}
capture.release();
writer.release();
destroyAllWindows();
return 0;
}
编译脚本
g++ test.cpp -o test -L/usr/local/lib -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_videoio -lopencv_imgcodecs
好了,到这里就告一段落了,一路走下来感觉要踩的坑我绝大部分都踩了一遍,不容易啊!所以后面的路,尽情发挥你的创造力吧!
如存在有误之处,还请广大网友指正!