Opencv是一个用户基础非常多的视觉开发库,可以用来实现人脸识别等功能,由于涉及到大量的调用与计算,所以对硬件的条件要求很高,并且还需要时时刻刻注意内存溢出这个问题,怎么样?很刺激吧。
从这篇文章开始我们从最基础学起,不同于其他学习博客,这个系列文章真的会是从最基础的开始讲解。
本文主要讲解Opencv的数据类型,已经最关键的类:Mat所拥有的强大的特性。
Opencv的C++类和函数都定义在命名空间CV中,有两种方法可以访问他们,在main函数前加入下面这句语句:
using namespace cv;
或者也可以在类和函数名前面添加cv::命名空间,这种情况下会需要打很多次cv::,所以还是推荐第一种方法。
opencv专门定义了一个用来保存图像以及其他矩阵数据的数据结构:cv::Mat;为什么是数据“矩阵”?因为从根本上来说,一张图像是一个由数值组成的矩阵。默认情况下其尺寸为0,若需要初始化尺寸的大小,可以使用下面这个语句:
cv:Mat ima(110,110,CV_8U,cv::Scalar(100));
在初始化时还需要指定矩阵数据的类型,矩阵中元素的类型:CV_8U对应的是单字节的像素图像。直接U意味着无符号的(Unsigned),也可以使用S声明带符号的类型(Signed),对于彩色图像,需要指定三个通道(CV_8UC3)。你也可以声明16位或32位的整数图像,如CV_16SC3。
当cv::Mat对象离开作用域后,分配的内存将自动释放,这是Mat类所拥有的强大特性,这样可以避免内存泄漏的困扰。cv::Mat另一个强大的特性是实现了引用计数和浅拷贝,当图像之间进行赋值时,图像数据并没有进行复制,两个对象都指向同一块内存块。这也可用于参数传值得的图像,以及返回值传值的图像。引用计数的作用是当所有引用数据的对象都被析构后,才会释放内存块,若需要创建的图像拥有原始图像的崭新拷贝,则可以使用copyTo()方法。
cv::Mat img2,img3; img2 = result;//两个图拥有同一份数据 result.copyTo(img3);//创建新的拷贝
若这时再次翻转输出图像,并显示两幅额外的图像,那么你将会发现img2夜壶受到变换的影响(因为它指向的内存数据与输出图像是同一份),而img3保持原样,因为它包含的是一份新的拷贝。cv::Mat的内存分配的模型也意味着你自己成功的编写返回图像的函数或类的方法:
cv::Mat function(){ cv::Mat ima(110,110,CV_8U,cv::scalar(100));//创建图像 return ima; } //调用该函数 cv::Mat gray = function();
gray变量将包含函数中创建的图像内容,而不涉及额外的内存分配。事实上,函数返回的cv::Mat对象在转移到gray时仅发生浅拷贝。函数中的声明局部变量ima离开作用域是背析构,但是由于他所关联的引用计数表示内部图像正在被另一个对象gray所引用,因此内存块并不会被释放。
下面是一个小程序,不需要了解他的功能,你只需要大概了解下程序的框架就可以了:
#include "opencv2/imgproc.hpp" //包含的头文件,最简单直接的方法是直接包含:#include<opencv2/opencv.hpp>
。这个hpp里面已经将所有功能模块的hpp都包含进去了。但是如果你需要使用其他的c++或c函数,还需要将对应的头文件包含进去。
#include "opencv2/highgui.hpp"
using namespace cv;
int main(int, char**)
{
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;
}