opencv安装
1、编译源码
总结一下:主要是下载源码,然后可以解压源码,在主目录下:
mkdir build
cd build
cmake-gui ..
跳出cmake编译界面,注意源码路径和编译路径,可以选择配置,之后编译和安装:
make -j4
sudo make install
2、python接口
编译安装好了,在/usr/local/lib/python2.7/site-packages底下有一个cv2.so文件,可以把这个目录添加到环境变量PYTHONPATH中:
export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python2.7/site-packages
这个在python中可以:
import cv2
如果是第三方python,则还需要将 /usr/local/lib/python2.7/site-packages 目录下的 cv2.so 复制到 第三方的python/usr/local/lib/python2.7/site-packages 目录下,这里以 anaconda为例:
sudo cp /usr/local/lib/python2.7/site-packages/cv2.so ~/anaconda/lib/python2.7/site-packages
使用第三方python导入编译好的包的过程中,有时候会出现以下类型的错误:
libstdc++.so.6:GLIBCXX_3.4.21 not found
这一般的原因是python 支持的编译器版本比依赖库的编译器版本低,在python中低版本调用了高版本的gcc库文件,从而导致了这类错误。错误1,错误2。
opencv文件结构
编译好的opencv会产生/bin/include/lib三个文件夹分别拷贝到cmake中选择的安装目录下(/usr/local/),我们可以从暴露的头文件看一下opencv的目录结构:
/usr/local/include中,我们使用tree -d查看目录结构,可以看到
├── opencv
└── opencv2
├── calib3d
├── core
│ └── cuda
│ └── detail
├── features2d
├── flann
├── hal
├── highgui
├── imgcodecs
├── imgproc
├── ml
├── objdetect
├── photo
├── shape
├── stitching
│ └── detail
├── superres
├── video
├── videoio
└── videostab
opencv文件是opencv1.0的核心,文件底下是一些头文件的集合,
opencv
├── cvaux.h
├── cvaux.hpp
├── cv.h
├── cv.hpp
├── cvwimage.h
├── cxcore.h
├── cxcore.hpp
├── cxeigen.hpp
├── cxmisc.h
├── highgui.h
└── ml.h
opencv2是新版opencv2系列的头文件的集合,底下有不同的组件的包。
opencv2
├── calib3d
├── core
│ └── cuda
│ └── detail
├── features2d
├── flann
├── hal
├── highgui
├── imgcodecs
├── imgproc
├── ml
├── objdetect
├── photo
├── shape
├── stitching
│ └── detail
├── superres
├── video
├── videoio
└── videostab
有时候我们经常在c++中写:
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
有时候也会写:
#include <opencv/cv.h>
主要是引用了不同版本的头文件。opencv的模块可以从include里面看出:一些简单的介绍如下:
【calib3d】——其实就是就是Calibration(校准)加3D这两个词的组合缩写。这个模块主要是相机校准和三维重建相关的内容。基本的多视角几何算法,单个立体摄像头标定,物体姿态估计,立体相似性算法,3D信息的重建等等。
【contrib】——也就是Contributed/Experimental Stuf的缩写, 该模块包含了一些最近添加的不太稳定的可选功能,不用去多管。2.4.8里的这个模块有新型人脸识别,立体匹配,人工视网膜模型等技术。
【core】——核心功能模块,包含如下内容:
- OpenCV基本数据结构
- 动态数据结构
- 绘图函数
- 数组操作相关函数
- 辅助功能与系统函数和宏
- 与OpenGL的互操作
【imgproc】——Image和Processing这两个单词的缩写组合。图像处理模块,这个模块包含了如下内容:
- 线性和非线性的图像滤波
- 图像的几何变换
- 其它(Miscellaneous)图像转换
- 直方图相关
- 结构分析和形状描述
- 运动分析和对象跟踪
- 特征检测
- 目标检测等内容
【features2d】 ——也就是Features2D, 2D功能框架 ,包含如下内容:
特征检测和描述
特征检测器(Feature Detectors)通用接口
描述符提取器(Descriptor Extractors)通用接口
描述符匹配器(Descriptor Matchers)通用接口
通用描述符(Generic Descriptor)匹配器通用接口
关键点绘制函数和匹配功能绘制函数
【flann】—— Fast Library for Approximate Nearest Neighbors,高维的近似近邻快速搜索算法库
【gpu】——运用GPU加速的计算机视觉模块
【highgui】——也就是high gui,高层GUI图形用户界面,包含媒体的I / O输入输出,视频捕捉、图像和视频的编码解码、图形交互界面的接口等内容
【legacy】——一些已经废弃的代码库,保留下来作为向下兼容
【ml】——Machine Learning,机器学习模块, 基本上是统计模型和分类算法
【video】——视频分析组件,该模块包括运动估计,背景分离,对象跟踪等视频处理相关内容。
更多模块介绍参考:博客1
简单来说,就是opencv打包好了很多有用的库,我们可以调用。
opencv使用入门(c++)
1、图像的表示
一副尺寸为M×N的图像可以表示成M×N的矩阵,矩阵上面的元素的值表示这个位置上面像素的亮度,一般像素址越大表示这个点越亮。
一般来说,灰度图用2维矩阵,彩色(多通道)图像用3维矩阵(M×N×3)表示。对于图像显示来说,现在主流的设备都适用无符号8位整数来(CV_8U)来表示像素亮度。图像在计算机的存储如下:
灰度图:
彩色图:
在opencv中,RGB图像的通道顺序为BGR,这点需要注意,可以看到彩色图中每列包含依次包含蓝、绿、红三个小列。
2、Mat类
在早期的opencv中,使用Ipllmage和CvMat这个两个数据集表示图像,它们都是c语言的结构,需要自己申请内存,释放内存,比较麻烦。新版本使用了Mat类,新的Mat类能够自己管理内存,Mat接口需要c++支持,Mat的关键属性如下:
class CV_EXPORTS Mat
{
public:
//一系列函数
...
/* flag 参数中包含许多关于矩阵的信息,如:
-Mat 的标识
-数据是否连续
-深度
-通道数目
*/
int flags;
//矩阵的维数,取值应该大于或等于 2
int dims;
//矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1
int rows, cols;
//指向数据的指针
uchar* data;
//指向引用计数的指针
//如果数据是由用户分配的,则为 NULL
int* refcount;
...
}
3、创建Mat对象的几种方式
//使用构造函数
Mat M(3,2,CV_8SC3,Scalar(0,0,4));
cout<<"M="<<M<<endl;
Mat M(600,800,CV_8UC1);
cout<<"M="<<M<<endl;
//使用create()函数创造,只申请,不初始化
Mat M(2,2, CV_8UC3);//构造函数创建图像
M.create(3,2, CV_8UC2);//释放内存重新创建图像
//Matlab风格创建
Mat Z = Mat::zeros(2,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl;
Mat O = Mat::ones(2, 3, CV_32F);
cout << "O = " << endl << " " << O << endl;
Mat E = Mat::eye(2, 3, CV_64F);
cout << "E = " << endl << " " << E << endl;
在这里注意:
Mat M(3,2,CV_8SC3,Scalar(0,0,4));
cout<<"M="<<M<<endl;
- 创造了一个行数(高度)是3,列数(宽度)是2的图像,图像的type是CV_8SC3,表示图像的元素是8位无符号整数,3个通道,图像所有的元素都被初始化位(0,0,255),由于opencv颜色的顺序是BGR,所以这张图片是全红色。
- 常用的构造函数经常涉及到类型 type。type 可以是 CV_8UC1,CV_16SC1,…, CV_64FC4 等。里面的 8U 表示 8 位无符号整数,16S 表示 16 位有符号整数,64F 表示 64 位浮点数(即 double 类型);C 后面的数表示通道数,例如 C1 表示一个 通道的图像,C4 表示 4 个通道的图像,以此类推。如果为表明通道数,则为如果你需要更多的通道数,需要用宏 CV_8UC(n),例如:Mat M(3,2, CV_8UC(5))
- Mat中重定义了<<操作符,可以直接输出所有像素值
4、图像矩阵的基本元素表达
对于单通道图像,图像矩阵的元素类型一般是8U,也可以是16S、32F等,这些类型我们可以直接使用uchar、short、float等基本数据类型表达。
对于多通道图像,如RGB彩色图像,需要用三个通道表示,如下图:
在这种情况,如果将图像仍然视为一个二维矩阵,每个元素所在位置包含了三个像素值,在Opencv中有模板类Vec,可以表示一个向量。Opencv中使用Vec类预定义一写向量,可以将之用于矩阵元素的表达:
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;
typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;
typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;
对于8U类型的RGB彩色图像来说,我们可以使用Vec3b来表示矩阵中元素:
Vec3b color; //用 color 变量描述一种 RGB 颜色
color[0]=255; //B 分量
color[1]=0; //G 分量
color[2]=0; //R 分量
这样对于一个type是8U的RBG3通道彩图,我们就可以仍然将它视为二维矩阵,矩阵每个位置的元素类型为Vec3b。
5、三种像素值读写方式
- at() 函数
Mat grayim(300,300,CV_8UC1);
uchar value=grayim.at<uchar>(i,j); //读出i行j列的像素值
grayim.at<uchar>(i,j)=128; //给i行j列的像素值赋值
可以使用at()函数的方式来对图像进行遍历:
#include <opencv2/core.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main(int argc,char* argv[]){
Mat grayim(600,800,CV_8UC1);
Mat colorim(600,800,CV_8UC3);
//遍历grayim
for(int i=0;i<grayim.rows;i++){
for(int j=0;j<grayim.cols;j++){
grayim.at<uchar>(i,j)=(i+j)%255;
}
}
//遍历colorim
for(int i=0;i<colorim.rows;i++){
for(int j=0;j<colorim.cols;j++){
Vec3b pixel;
pixel[0]=0;
pixel[1]=0;
pixel[2]=255;
colorim.at<Vec3b>(i,j)=pixel;
}
}
//显示图像
imshow("grayim",grayim);
imgshow("colorim",colorim);
return 0;
}
需要注意的是,如果要遍历图像,并不推荐使用 at()函数。使用这个函数的 优点是代码的可读性高,但是效率并不是很高。
- 使用迭代器
Mat添加了迭代器的支持,写起来很方便:
#include <opencv2/core.hpp>
#include <iostream>
using namespace std;
int main(int argc,char* argv[]){
cv::Mat grayim(600,600,CV_8UC1);
cv::Mat colorim(600,600,CV_8UC3);
cv::MatIterator_<uchar> grayit,grayend;
cv::MatIterator_<Vec3b> colorit,colorend;
//遍历grayimg
for(grayit=grayim.begin<uchar>(),grayend=grayim.begin<uchar>;grayit!=grayend;++grayit){
*grayit=rand()%255;
}
//遍历colorimg
for(colorit=colorim.begin<Vec3b>(),colorend=colorim.end<Vec3b>();colorit!=colorend;++colorit){
(*colorit)[0]=rand()%255;
(*colorit)[1]=rand()%255;
(*colorit)[2]=rand()%255;
}
cv::imshow("grayit",grayit);
cv::imshow("colorit",colorit);
return 0;
}
- 使用数据指针
C/C++中的指针操 作是不进行类型以及越界检查的,如果指针访问出错,程序运行时有时候可能看上去一切正常,有时候却突然弹出“段错误”(segment fault)。当程序规模较大,且逻辑复杂时,查找指针错误十分困难。对于不熟悉指针 的编程者来说,指针就如同噩梦。如果你对指针使用没有自信,则不建议直接通 过指针操作来访问像素。虽然 at()函数和迭代器也不能保证对像素访问进行充分 的检查,但是总是比指针操作要可靠一些。
如果你非常注重程序的运行速度,那么遍历像素时,建议使用指针。
#include <iostream>
#include <opencv2/core.hpp>
using namespace std;
using namespace cv;
int main(int argc,char* argv[]){
Mat grayim(600,600,CV_8UC1);
Mat colorim(600,600,CV_8UC3);
//遍历grayim
for(int i=0;i<grayim.rows;++i){
uchar* p=grayim.ptr<uchar>(i);
for(int j=0;j<grayim.cols;++j){
p[j]=(i+j)%255;
}
}
//遍历colorimg
for(int i=0;i<colorim.rows;++i){
Vec3b * p=colorim.ptr<Vec3b>(i);
for(int j=0;j<colorim.cols;++i){
p[j][0]=i%255;
p[j][1]=j%255;
p[j][2]=j%255;
}
}
//显示图片
imshow("colorim",colorim);
imshow("grayim",grayim);
return 0;
}
6、取图像中感兴趣的区域
//提取一行和一列
Mat grayim(600,600,CV_8UC1);
firstRow=grayim.row(0);
firstCol=grayim.col(0);
//使用Range和Rect
Mat roi1=grayim(Range(1,3),Range(0,2));
Mat roi2=grayim(Rect(1,0,3,2));
7、读写图片
Mat imread(const string& filename, int flags=1 )
很明显参数 filename 是被读取或者保存的图像文件名;在 imread()函数中, flag 参数值有三种情况:
- flag>0 ,该函数返回 3 通道图像,如果磁盘上的图像⽂文件是单通道的灰 度图像,则会被强制转为 3 通道;
- flag=0 ,该函数返回单通道图像,如果磁盘的图像⽂文件是多通道图像,则 会被强制转为单通道;
- flag<0 ,则函数不不对图像进⾏行行通道转换。
bool imwrite(const string& filename, InputArray image,
const vector<int>& params=vector<int>())
文件的格式由 filename 参数指定的文件扩展名确定。推荐使用 PNG 文件格 式。BMP 格式是无损格式,但是一般不进行压缩,文件尺寸非常大;JPEG 格式 的文件娇小,但是 JPEG 是有损压缩,会丢失一些信息。PNG 是无损压缩格式, 推荐使用。
并不是所有的 Mat 对象都可以存为图像文件,目前支持的格式只有 8U 类型 的单通道和 3 通道(颜色顺序为 BGR)矩阵;如果需要要保存 16U 格式图像,只 能使用 PNG、JPEG 2000 和 TIFF 格式。如果希望将其他格式的矩阵保存为图像文 件,可以先用 Mat::convertTo()函数或者 cvtColor()函数将矩阵转为可以保存的格 式。
另外需要注意的是,在保存文件时,如果文件已经存在,imwrite()函数不会 进行提醒,将直接覆盖掉以前的文件。
下面是一个读入图片,做边缘检测,并保存结果。
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[]){
//读入图片,并转换成单通道
Mat im=imread("./1.jpg",0);
//判断是否为空
if (im.empty()){
cout<<"Can not load the image"<<endl;
return -1;
}
Mat result;
//边缘检测
Canny(im,result,50,150);
//保存图片
imwrite("lena_canny.png",result);
}
8、读写视频
介绍 OpenCV 读写视频之前,先介绍一下编解码器(codec)。如果是图像文 件,我们可以根据文件扩展名得知图像的格式。但是此经验并不能推广到视频文 件中。有些 OpenCV 用户会碰到奇怪的问题,都是 avi 视频文件,有的能用 OpenCV 打开,有的不能。
视频的格式主要由压缩算法决定。压缩算法称之为编码器(coder),解压算 法称之为解码器(decoder),编解码算法可以统称为编解码器(codec)。视频文件能读或者写,关键看是否有相应的编解码器。编解码器的种类非常多,常用的 有 MJPG、XVID、DIVX 等,完整的列表请参考 FOURCC 网站 3 。因此视频文件的扩 展名(如 avi 等)往往只能表示这是一个视频文件。
OpenCV 2 中提供了两个类来实现视频的读写。读视频的类是 VideoCapture, 写视频的类是 VideoWriter。
- 读视频
VideoCapture 既可以从视频文件读取图像,也可以从摄像头读取图像。可以 使用该类的构造函数打开视频文件或者摄像头。如果 VideoCapture 对象已经创 建,也可以使用 VideoCapture::open()打开,VideoCapture::open()函数会自动调用 VideoCapture::release()函数,先释放已经打开的视频,然后再打开新视频。
如果要读一帧,可以使用 VideoCapture::read()函数。VideoCapture 类重载了>> 操作符,实现了读视频帧的功能。下面的例程演示了使用 VideoCapture 类读视 频。
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char** argv){
//打开一个视频
VideoCapture cap("./2.avi");
//检测是否打开
if(!cap.isOpened()){
cout<<"Can not open a camera or file"<<endl;
return -1;
}
Mat edges;
//创建窗口
namedWindow("edges",1);
for(;;){
Mat frame;
//读取一帧
cap>>frame;
//判断为空
if(frame.empty()){
break;
}
//转化为灰度图
cvtColor(frame,edges,CV_BGR2GRAY);
//边缘检测
Canny(edges,edges,0,30,3);
//显示
imshow("edges",edges);
if(waitKey(30)>=0){
break;
}
}
//退出时自动释放cap
return 0;
}
- 写视频
使用 OpenCV 创建视频也非常简单,与读视频不同的是,你需要在创建视频 时设置一系列参数,包括:文件名,编解码器,帧率,宽度和高度等。编解码器 使用四个字符表示,可以是 CV_FOURCC('M','J','P','G')、CV_FOURCC('X','V','I','D')及 CV_FOURCC('D','I','V','X')等。如果使用某种编解码器无法创建视频文件,请尝试其他的编解码器。
将图像写入视频可以使用 VideoWriter::write()函数,VideoWriter 类中也重载 了<<操作符,使用起来非常方便。另外需要注意:待写入的图像尺寸必须与创建 视频时指定的尺寸一致。
下面例程演示了如何写视频文件。本例程将生成一个视频文件,视频的第 0 帧上是一个红色的“0”,第 1 帧上是个红色的“1”,以此类推,共 100 帧。生成 视频的播放效果如 所示。
#include <stdio.h>
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char** argv){
//视频的高度和宽度
Size s(320,240);
//创建writer
VideoWriter writer=VideoWriter("myvideo.avi",CV_FOURCC('M','J','P','G'),25,s);
//判读创建成功没
if(!writer.isOpened()){
cout<<"Can not create video file.\n";
return -1;
}
//视频帧
Mat frame1(s,CV_8UC3);
for(int i=0;i<100;i++){
frame1=Scalar::all(0);
char text[128];
snprintf(text,sizeof(text),"%d",i);
//将数字画到画面上
putText(frame1,text,Point(s.width/3,s.height/3),
FONT_HERSHEY_SCRIPT_SIMPLEX,3,
Scalar(0,0,255),3,8);
//写入视频中
writer<<frame1;
}
//退出时自动关闭
return 0;
}
参考博客:
http://blog.csdn.net/llp1992/article/details/50066983
https://wenku.baidu.com/view/f45dc229910ef12d2bf9e7b2.html
http://www.cnblogs.com/yishujun/archive/2014/03/27/3629004.html
http://lib.csdn.net/article/opencv/42000