初识OpenCV

初识OpenCV


定义

OpenCV (Open Source Computer Vision Library: http://opencv.org)
开源计算机视觉库

接口

  • 2.x 、3.x -> C++
  • 1.x -> c

模块化的架构

  • core
  • imgproc
  • video
  • calib3d
  • features2d
  • objdetect
  • highgui
  • videoio
  • gpu
  • 。。。

API说明

1、cv Namespace 命名空间

#include "opencv2/core.hpp"
using namespace cv;

当函数名冲突时 强制指定

cv::log(a, a);
a /= std::log(2.);

2、自动内存管理

//引用计数机制实现
// create a big 8Mb matrix
Mat A(1000, 1000, CV_64F);

// create another header for the same matrix;
// this is an instant operation, regardless of the matrix size.
Mat B = A; //B引用指向A

// create another header for the 3-rd row of A; no data is copied either
Mat C = B.row(3); //C指向A的第三行

// now create a separate copy of the matrix D是独立的一份拷贝 内存上
Mat D = B.clone(); 

// copy the 5-th row of B to C, that is, copy the 5-th row of A
// to the 3-rd row of A.
B.row(5).copyTo(C); //A的第五行拷贝到第三行

// now let A and D share the data; after that the modified version
// of A is still referenced by B and C.
A = D; //A引用指向D 但是之前的内存A'仍然被B和C引用不能释放!

// now make B an empty matrix (which references no memory buffers),
// but the modified version of A will still be referenced by C,
// despite that C is just a single row of the original A
B.release(); //B释放了对A'的引用 只剩下C对A'的引用

// finally, make a full copy of C. As a result, the big modified
// matrix will be deallocated, since it is not referenced by anyone
C = C.clone(); //C也独立了 没有对A'的引用了 引用计数为0 A'内存被释放!
类似于std::shared_ptr 智能指针
Ptr<T> ptr(new T(...));
Ptr<T> ptr = makePtr<T>(...);

3、输出数据的自动内存分配

g++ opencv_test.cpp -lopencv_core -lopencv_imgproc  -lopencv_highgui -g
//@filename: opencv_test.cpp
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"

using namespace cv;

int main(int argc, char** argv)
{
    VideoCapture cap(0);
    if(!cap.isOpened()) return -1;

    Mat frame, edges;
    namedWindow("edges",1);
    for(;;)
    {
        cap >> frame;
        cvtColor(frame, edges, COLOR_BGR2GRAY);//灰度转换
        GaussianBlur(edges, edges, Size(7,7), 1.5, 1.5);//高斯滤波
        Canny(edges, edges, 0, 30, 3);//边缘提取
        imshow("edges", edges);
        if(waitKey(30) >= 0) break;
    }
    return 0;
}

4、Mat类

  • cv::Mat
  • type:
    CV_8UC1、CV_16SC1、CV64FC4 (16或8…是像素值位数;C后是通道数;前面是类型,U是无符号 S是有符号 F是浮点数 当然64位浮点数就是双精度浮点数double)

  • create方法根据指定参数决定是否释放内存重新申请内存创建图像
  • Vec类可以表示向量 可以表达矩阵元素
  • 迭代器 MatIterator_: MatIterator_ a,b;
  • row(n)方法 提取一行
  • col(n)方法 提取一列
  • Range()类可以提取部分
//提取第 1 到 3 列(不包括 3) 
Mat B = A(Range::all(), Range(1, 3));//前面是行 后面是列
  • 也可以使用Rect方法指定左上右下坐标选取范围
  • Size(w, h)指定宽高
  • Mat Mat::diag(int d) const选择对角线元素
参数 d<0 时,表示取主对角线上方的次对角线
参数 d=0 时,表示取主对角线;
参数 d>0 是,表示取主对角线下方的次对角线,
参数 d=1 时,表示取主对角线下方,且紧贴主对角线的元素;

  • 支持运算

    • 加法,减法,取负:A+B,A-B,A+s,A-s,s+A,s-A,-A
    • 缩放取值范围:A*alpha
    • 矩阵对应元素的乘法和除法: A.mul(B),A/B,alpha/A
    • 矩阵乘法:A*B (注意此处是矩阵乘法,而不是矩阵对应元素相乘)
    • 矩阵转置:A.t()
    • 矩阵求逆和求伪逆:A.inv()
    • 矩阵比较运算:A cmpop B,A cmpop alpha,alpha cmpop A。此处 cmpop 可以是>,>=,==,!=,<=,<。如果条件成立,则结果矩阵(8U 类型矩 阵)的对应元素被置为 255;否则置 0。
    • 矩阵位逻辑运算:A logicop B,A logicop s,s logicop A,~A,此处 logicop 可以是&,|和^。
    • 矩阵对应元素的最大值和最小值:min(A, B),min(A, alpha),max(A, B), max(A, alpha)。
    • 矩阵中元素的绝对值:abs(A)
    • 叉积和点积:A.cross(B),A.dot(B)
  • Mat_类是对Mat类的封装 便于使用某些方法

    Mat M(600, 800, CV_8UC1);
    int i=1, j=2;
    double d1 = (double) ((i+j)%255); //用 at()读写像素时,需要指定类型 
    M.at<uchar>(i,j) = d1; 
    //下面代码错误,应该使用 at<uchar>() //但编译时不会提醒错误 
    //运行结果不正确,d2 不等于 d1 
    double d2 = M.at<double>(i,j);
    Mat_<uchar> M1 = (Mat_<uchar>&)M
    double d3 = (double) ((i+j)%255); 
    //直接使用Matlab 风格的矩阵元素读写,简洁 
    M1(i,j) = d3; 
    double d4 = M1(i,j);
  • 矩阵内存

    矩阵 = 矩阵头 + 一个指向存储所有像素值的矩阵的指针。
    矩阵头(包含矩阵尺寸,存储方法, 存储地址等信息)的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。复制矩阵数据往往花费较多时间,因此除非有必要,不要复制大的矩阵

  • IplImage 和 CvMat 转换Mat 版本兼容 略

5、数据获取与存储

5.1 图像读写

  • imread() 将图像读取到内存
    #失败,则Mat::data为NULL, Mat::empty()方法测试
    Mat imread(const string& filename, int flags=1 )
    
    很明显参数 filename 是被读取或者保存的图像文件名;在 imread()函数中,
    flag 参数值有三种情况:
    • flag>0,该函数返回 3 通道图像,如果磁盘上的图像文件是单通道的灰 度图像,则会被强制转为 3 通道;
    • flag=0,该函数返回单通道图像,如果磁盘的图像文件是多通道图像,则 会被强制转为单通道;
    • flag<0,则函数不对图像进行通道转换
  • imwrite() Mat对象以图像文件格式保存
bool imwrite(const string& filename, InputArray image, const vector<int>& params=vector<int>()
/***
1、推荐使用 PNG 文件格式,无损压缩
2、第三个参数 params 可以指定文件格式的一些细节信息。这
个参数里面的数值是跟文件格式相关的: 
  JPEG:表示图像的质量,取值范围从 0 到 100。数值越大表示图像质量 越高,当然文件也越大。默认值是 95。
  PNG:表示压缩级别,取值范围是从 0 到 9。数值越大表示文件越小, 但是压缩花费的时间也越长。默认值是 3。
  PPM,PGM或 PBM:表示文件是以二进制还是纯文本方式存储,取值为 0 或 1。如果取值为 1,则表示以二进制方式存储。默认值是 1
3、并不是所有的Mat 对象都可以存为图像文件,目前支持的格式只有 8U 类型
的单通道和 3 通道(颜色顺序为 BGR)矩阵。其他的可以转换,用 Mat::convertTo()函数或者 cvtColor()函数
4、在保存文件时,如果文件已经存在,imwrite()函数不会 进行提醒,将直接覆盖掉以前的文件
***/

eg. 图片缩放

g++ opencv_test.cpp -lopencv_imgproc -lopencv_highgui -lopencv_core -g
//@filename: opencv_test.cpp
#include<iostream>
#include<opencv2/imgproc/imgproc.hpp>
#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
#include<string>

using namespace std;
using namespace cv;


int main(){
	string imgname ="tiger.jpeg";
	//读取图片
	cv::Mat img =cv::imread(imgname.c_str());
	// //缩放成224,244
	cout<< img.rows << " , "<< img.cols <<endl;
	cout<<  "in size " << img.size() << endl;
	Size s(224, 224);
	Mat dst = Mat::zeros(s, CV_64FC3);//共有224*224个像素点 每个像素点有3个通道 每个通道使用64位进行表示 即每个像素点24字节大小(位深度)
	resize(img, dst, dst.size());
	cout<<  "out size " << dst.size() << endl; //[224 X 224]
	cout <<" and area :" << s.area() << endl;//面积 50176
	imwrite("test.png", dst);
	return 0;
}

5.2 视频读写

OpenCV 2 中提供了两个类来实现视频的读写。读视频的类是 VideoCapture,
写视频的类是 VideoWriter

VideoCapture 既可以从视频文件读取图像,也可以从摄像头读取图像 可以 使用该类的构造函数打开视频文件或者摄像头。如果 VideoCapture 对象已经创 建,也可以使用 VideoCapture::open()打开,VideoCapture::open()函数会自动调用 VideoCapture::release()函数,先释放已经打开的视频,然后再打开新视频

//打开第一个摄像头 
VideoCapture cap1(0);
//打开视频文件 
VideoCapture cap2("video.short.raw.avi");
//检查是否成功打开 
if(!cap.isOpened()) {
   exit(-1);   
}
for(;;) {
    Mat frame;
    //从 cap 中读一帧,存到frame 
    cap >> frame; 
    //如果未读到图像 
    if(frame.empty()) break;
    //将读到的图像转为灰度图 
    cvtColor(frame, edges, CV_BGR2GRAY); 
    //进行边缘提取操作 
    Canny(edges, edges, 0, 30, 3); 
    //显示结果 
    imshow("edges", edges); 
    //等待 30 秒,如果按键则推出循环 
    if(waitKey(30) >= 0) break;
}

需要在创建视频时设置一系列参数,包括:文件名,编解码器,帧率,宽度和高度等。编解码器 使用四个字符表示,可以是 CV_FOURCC(‘M’,‘J’,‘P’,‘G’)、CV_FOURCC(‘X’,‘V’,‘I’,‘D’)及 CV_FOURCC(‘D’,‘I’,‘V’,‘X’)等。
写视频类VideoWriter::write()函数,VideoWriter 类中也重载
了<<操作符

注意:待写入的图像尺寸必须与创建 视频时指定的尺寸一致

//定义视频的宽度和高度 
Size s(320, 240); 
//创建writer,并指定FOURCC 及 FPS 等参数 
VideoWriter writer = VideoWriter("myvideo.avi", CV_FOURCC('M','J','P','G'), 25, s);
if(!writer.isOpened()) exit(-1);

//视频帧 
Mat frame(s, CV_8UC3);//尺寸一致
//将图像置为黑色 
frame = Scalar::all(0);
char text[128]="I LOVE U";
//绘图
putText(frame, 
        text, 
        Point(s.width/3, s.height/3),
        FONT_HERSHEY_SCRIPT_SIMPLEX, 
        3, 
        Scalar(0,0,255), //BGR 红色!
        3, 
        8);
//将图像写入视频 
writer << frame;

参考及进阶资料

posted @ 2019-03-25 20:35  cloudren2020  阅读(254)  评论(0编辑  收藏  举报