from http://blog.csdn.net/u012162613/article/details/38779445 OpenCV Demo系列文章将按照opencv2模块core、imgproc、highgui、calib3d、feature2d、video、objdetect、ml、gpu的顺序,记录和分析一些常用的函数、功能及注意事项,后期打算加入一些具体的应用实例。常用的demo将整理到github,欢迎交流与指正。
core模块(1)图像读取与显示
这篇文章将介绍opencv2中读取和显示图像的函数,作为opencv系列的第一篇文章,本文顺带介绍一下opencv2与旧版本opencv的一些区别,下面的demo会给出opencv1和opencv2的两种实现方式。
1、数据类型与函数说明
Mat
存放矩阵或者图像,包括矩阵信息头、矩阵指针。
opencv2引入的C++接口,Mat是一个类,有它的数据和函数成员。
Mat自动分配和释放内存,是opencv1到opencv2的一个巨大改进(opencv1需要手动释放内存)
imread()
读取图像,用法:imread("图像路径")
namedWindow()
创建显示窗口,用于显示图片或者视频,用法:namedWindow("窗口名称")
imshow()
显示图像,用法:imshow("用于显示的窗口名称",图像名称)
waitKey()
等待按键按下,并且返回按键的值。
用法:key=waitKey(delay),等待delay毫秒,期间有键按下则将其返回给key。
若delay为0或者为负,无限等待
2、demo
这个demo实现图像的读取和显示,分为非命令行模式与命令行模式,本质上是一样的,着重了解一下命令行模式。
2.1 非命令行模式
非命令行模式,直接在程序中传参数,而不是在运行的时候再传递参数。
- <span style="font-size:18px;">#include<iostream>
- #include"opencv2\core\core.hpp"
- #include"opencv2\highgui\highgui.hpp"
- using namespace cv;
- using namespace std;
-
- int main(){
- /*opencv2版本,矩阵结构Mat,函数imread(),nameWindow(),imshow()*/
- Mat img1=imread("D:\\1.jpg"); //注意是双反斜杠,否则读不到图片
- if(!img1.data) {cout<<"error1";return -1;} //检查有没有读到图片
- namedWindow("图片1");//有两个参数,第二个有默认形参
- imshow("图片1",img1);
- waitKey(10);
-
- /*opencv老版本,数据结构IplImage,函数cvLoadImage(),cvNamedWindow(),cvShowImage()*/
- IplImage* img2=cvLoadImage("D:\\2.jpg");
- if(!img2) {cout<<"error2";return -1;}
- cvNamedWindow("图片2");
- cvShowImage("图片2",img2);
- cvWaitKey(10);
- cvReleaseImage(&img2);
- cvDestroyWindow("图片2"); //老版本opencv需要手动释放内存
-
- return 0;
- }</span>
opencv1与opencv2的区别?
观察opencv1版本的函数可以发现,所有函数名都有前缀cv,如cvLoadImage()。而在opencv2中,已经将所有函数和变量名放入命名空间cv,因此,opencv2版本中与opencv兼容的所有函数都无须加前缀cv。
观察opencv2的函数名,可以发现它们遵循驼峰命名准则。也就是第一个字母为小写并且后续单词首字母大写(如 waitKey() ),若是单个单词作为函数名则第一个字母大写,如 Canny()。
2.2 命令行模式
- <span style="font-size:18px;">#include<iostream>
- #include"opencv2\core\core.hpp"
- #include"opencv2\highgui\highgui.hpp"
- using namespace cv;
- using namespace std;
-
- int main2(int argc,char** argv){
- /*opencv2版本,矩阵结构Mat,函数imread(),nameWindow(),imshow()*/
- Mat img1=imread(argv[1]); //注意是双反斜杠,否则读不到图片
- if(!img1.data) {cout<<"error1";return -1;} //检查有没有读到图片
- namedWindow("图片1");//有两个参数,第二个有默认形参
- imshow("图片1",img1);
- waitKey(10);
-
- /*opencv老版本,数据结构IplImage,函数cvLoadImage(),cvNamedWindow(),cvShowImage()*/
- IplImage* img2=cvLoadImage(argv[2]);
- if(!img2) {cout<<"error2";return -1;}
- cvNamedWindow("图片2");
- cvShowImage("图片2",img2);
- cvWaitKey(10);
- cvReleaseImage(&img2);
- cvDestroyWindow("图片2"); //老版本opencv需要手动释放内存
-
- return 0;
- }</span>
什么是命令行程序?
命令行模式程序,是运行在命令行界面的程序,比如windows系统下的dos,linux下的bash都是命令行界面。
看上面的main函数,有两个形参,argc表示后面接的参数个数,argv[0]表示函数本身的地址,argv[1]表示第一个参数,argv[2]表示第二个参数.....以此类推。
比如本程序编译后生成imgRead_Show.exe文件,则可以在命令行模式下运行:
- <span style="font-size:18px;">imgRead_Show.exe D:\\1.jpg D:\\2.jpg</span>
则传入参数:argc=3,argv[1]="D:\\1.jpg",argv[2]="D:\\2.jpg",与非命令行程序本质是一样的。
在dos中的运行效果图:
3、再谈Mat
介绍Mat的特性,特别是Mat的复制构造。前面提到Mat包括信息头以及矩阵指针,矩阵指针指向的就是图片的内存空间,当我们要复制一张图片或者复制一个矩阵的时候,有很多种方法,但是必须注意复制有“浅复制”和“深复制”两种,哪些是“浅”哪些是“深”?通过实例来看:
- #include"opencv2\core\core.hpp"
- #include"opencv2\highgui\highgui.hpp"
- using namespace cv;
-
- int main(){
- Mat A=imread("D:\\1.jpg");
- Mat B=A;
- Mat C(A);/*B、C都是浅拷贝,只复制矩阵指针,它们指向的是同一个内存空间,改变其中一个,剩下的两个也会受影响*/
- Mat D=A.clone();
- Mat E;
- A.copyTo(E); /*D、E是深拷贝,将图像数据也复制了一份,改变A不会影响D、E*/
-
- namedWindow("原来的A");imshow("原来的A",A);
- A=A-100;
- namedWindow("修改后的A");imshow("修改后的A",A);
- namedWindow("B");imshow("B",B);
- namedWindow("C");imshow("C",C);
- namedWindow("D");imshow("D",D);
- namedWindow("E");imshow("E",E);
-
- waitKey(0);
- return 0;
- }
上面程序中已经注释哪些是“浅”/“深”。
总结一下,当调用Mat对象的函数成员clone()、copyTo()时,实现的是深拷贝,是将原矩阵的数据完全复制一份并且另外开辟一个内存空间来存储,如D、E。当用赋值或者构造函数来进行复制时,只是复制矩阵的指针,它们指向的是同一个内存空间,如B、C。
运行效果图:
|