初遇 opencv
初遇 opencv
调用电脑摄像
test1.cpp 文件内容如下:
#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
// 从摄像头读取视频
VideoCapture capture(0);
// 循环播放每一帧
while(1)
{
Mat frame; // 定义一个 Mat 变量,用于存储每一帧的图像
capture >> frame; // 读取当前帧
imshow("读取视频帧",frame); // 显示当前帧
waitKey(30); // 延时30ms
}
system("pause");
return 0;
}
本代码循环条件是 1 ,意味着会一直循环显示摄像,知道使用 ctrl+c 强制退出,要想要控制程序关闭退出,只需要修改循环条件,将 waitKey() 从循环中刷新,改为判断条件,在刷新的同时接收按键信息:
// 无限循环,只能强制退出
while(1)
// 可以按键退出
while(waitKey(30) != 按键对应的键值)
打开本地视频播放
如果不是直接调用电脑的摄像设备,而是调用电脑的中的视频播放,只需要修改一下部分即可:
// 需要修改的部分
VideoCapture capture(0);
// 修改后的部分
VideoCapture("视频路径");
编译与运行
gcc 编译
gcc 编译命令如下:
sudo g++ test1.cpp -o test1 `pkg-config --cflags --libs opencv`
我们是如何获取 opencv 头文件的,又是如何 lib 库文件路径的?
在上面的编译命令中,我们其实使用了一个工具 " pkg-config " ,主要有以下几个功能:
1.检查库的版本号,如果需要的库不满足要求,会打印出错误消息,避免链接错误版本的库文件,这也就是我们大多时候报错的原因
2.获得编译预处理参数,如宏定义,头文件的位置
3.获得链接参数,如库及依赖的其他库的位置,文件名及其他一些链接参数
4.自动加入所依赖的其他库
所有有了这个工具之后我们的编译就很方便了(不过在此之前你要确保你安装的OpenCV的安装链接库文件的目录下有一个 pkgconfig 文件夹,在该文件夹里面有个 opencv.pc 的文件,其实这就是 pkg-config 下 OpenCV 的配置文件)
使用 pkg-config 时,选项 --cflags 它是用来指定程序在编译时所需要头文件所在的目录,选项 --libs 则是指定程序在链接时所需要的动态链接库的目录
在上述编译过程中,实际上应该如下:
pkg-config --cflags --libs opencv-I/usr/local/include/opencv -I/usr/local/include -L/usr/local/lib -lopencv_calib3d -lopencv_contrib -lopencv_core -lopencv_features2d -lopencv_flann -lopencv_gpu -lopencv_highgui -lopencv_imgproc -lopencv_legacy -lopencv_ml -lopencv_nonfree -lopencv_objdetect -lopencv_ocl -lopencv_photo -lopencv_stitching -lopencv_superres -lopencv_ts -lopencv_video -lopencv_videostab -lrt -lpthread -lm -ldl
所以,如下编译可以有相同作用:
gcc main.c -I/usr/local/include/opencv -I/usr/local/include -L/usr/local/lib -lopencv_calib3d -lopencv_contrib -lopencv_core -lopencv_features2d -lopencv_flann -lopencv_gpu -lopencv_highgui -lopencv_imgproc -lopencv_legacy -lopencv_ml -lopencv_nonfree -lopencv_objdetect -lopencv_ocl -lopencv_photo -lopencv_stitching -lopencv_superres -lopencv_ts -lopencv_video -lopencv_videostab -lrt -lpthread -lm -ldl
Makefile 编译
建立 makefile 文件内容如下:
.PHONY:clean
CXX = g++
LIBS = `pkg-config --libs opencv`
CFLAGS = `pkg-config --cflags opencv`
CPPCFLAGS = -g -std = c++11 -Werror
SRCS = test1.cpp
TARGET = test1.out
OBJS = test1.o
# 编译生成可执行文件
$(TARGET): $(OBJS)
@echo "Start Compiling……"
@echo $(CXX)
$(CXX) $(INC) $(CPPFLAGS) $(OBJS) -O $(TARGET) $(LIBS)
@echo "Compile Done!"
# 下面是顺序编译生成 .o 文件
# 每个 .c 文件都会编译生成对应的 .o 文件
# < 表示目标依赖表中的第一个依赖文件; @ 表示生成的对应的目标文件
$(OBJS):%.o:%.c
$(CXX) $(INC) $(CPPFLAGS) -c $< -o $@
clean:
-rm -r *.o $(TARGET)
编译指令如下:
make
# 编译运行后,应删除编译产生文件,以便后续再次编译,删除命令如下:
make clean
运行
直接运行可执行程序,成功调用电脑摄像头,如下图:
变量介绍
Mat
Mat 是一个类,使用的非常广泛,下面只介绍他的默认构造函数
默认构造函数
cv::Mat::Mat()
默认构造函数:生成一个矩阵并由 OpenCV 提供的函数(一般是 Mat::create() 和 cv::imread() )来分配储存空间。
Mat 类可以分为两个部分:矩阵头和指向像素数据的矩阵指针
矩阵头:包括数字图像的矩阵尺寸、存储方法、存储地址和引用次数等,矩阵头的大小是一个常数,不会随着图像的大小而改变,但是保存图像像素数据的矩阵则会随着图像的大小而改变,通常数据量会很大,比矩阵头大几个数量级。这样,在图像复制和传递过程中,主要的开销是由存放图像像素的矩阵而引起的。因此,OpenCV 使用了引用次数,当进行图像复制和传递时,不再复制整个 Mat 数据,而只是复制矩阵头和指向像素矩阵的指针,例如:
CV::Mat a
a = CV::imread("图片路径")
CV::Mat b = a
上面的a,b有各自的矩阵头,但是其矩阵指针指向同一个矩阵,也就是其中任何一个改变了矩阵数据都会影响另外一个。那么,多个 Mat 共用一个矩阵数据,最后谁来释放矩阵数据呢?
这就是引用计数的作用,当 Mat 对象每被复制一次时,就会将引用计数加1,而每销毁一个 Mat 对象(共用同一个矩阵数据)时引用计数会被减 1,当引用计数为 0 时,矩阵数据会被清理。
通俗一点来说,就是通过矩阵来读入一个图片的所有像素,然后在显示的时候,在通过矩阵还原图片的像素点,达到显示图片的效果
waitkey
waitKey函数用于显示的延迟。例如,waitKey(0) 将无限显示窗口,直到按下任意按键退出延迟事件(适用于显示图像)。如果 delay 大于0,例如,waitKey(30) 将每隔至少 30ms 显示视频的一帧图像(适用于显示视频帧),如果要按键退出,则需要将 waitKey(30) 与一个按键值(ASCII码)比较。
waitKey() 函数的功能就是在不断刷新图像,频率为 delay ,单位是 ms ,返回值为当前键盘按下的值,没有按键时返回 -1
显示图片和视频时,会在 imshow()时,通常会在后面加上 while(cvWaitKey(n)==key) 为大于等于 0 的数即可,那么程序将在此处循环运行直到按键响应为 key 时之后继续,如果没有设置 key 值,只是设置了 waitKey ,则就会一直显示视频界面,不一会退出
delay : 为 0 时,则会一直显示这一帧,“delay”,在显示视频和摄像头时有用,用于设置在显示完一帧图像后程序等待“delay" ms 再显示下一帧视频
如果程序想响应某个按键,可利用 if(cvWaitKey(1)==Keyvalue);经常程序里面出现 if( cvWaitKey(10) >= 0 ) 是说 10ms 中按任意键进入此 if 块
注意:在 imshow 之后如果没有 waitKey 语句则不会正常显示图像。
报错处理
编译报错
一般编译 cpp 文件的过程中会出现如下报错:
/usr/bin/ld: /tmp/ccNS2ClW.o: undefined reference to symbol '_ZNSt8ios_base4InitD1Ev@@GLIBCXX_3.4'
/usr/bin/ld: /lib/x86_64-linux-gnu/libstdc++.so.6: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status
说明缺少需要的库,只要在编译时链接所需的库文件即可,上述报错缺少的是 libstdc++ 库,直接链接库文件即可
更直接的原因是:我们所编译的文件是 cpp 文件,应该使用 g++ 进行编译,而不是使用 gcc ,如果我们要强行使用 gcc 进行编译,就需要链接 libstdc++ 的库,如果直接使用 g++ 进行编译,就没有该报错问题
运行报错
如果在运行时报错如下:
[ WARN:0] VIDEOIO(V4L2:/dev/video0): can't open camera by index
terminate called after throwing an instance of 'cv::Exception'
what(): OpenCV(3.4.16) /home/ppqppl/文档/opencv/modules/highgui/src/window.cpp:382: error: (-215:Assertion failed) size.width>0 && size.height>0 in function 'imshow'
如果是打开图片:
尺寸都是0(前提是图片正确),说明没有读取到有效图片
根本原因是路径不正确,还有一个隐藏问题是 有中文路径
如果是打开并链接摄像:
可能是链接摄像失败,或者是电脑根本没有摄像设备
如果运行基于 Opencv 有显示图片的窗口时,在按键跳出循环时,出现如下报错:
Failed to load module "canberra-gtk-module"
不必担心,只是缺少 module ,安装对应的 module 即可:
sudo apt-get install libcanberra-gtk-module
用手机充当电脑摄像
如果电脑在 Ubuntu 环境下而且没有摄像头,又需要使用摄像,那该怎么办?
最简单的办法就是在手机和电脑上同时安装 DroidCam ,可以通过 WiFi/LAN 或 USB 连接手机作为摄像驱动而使用
DroidCam 驱动官网:dev-47
安装可以直接参考官网教程