邹昆cszoukun@foxmail.com主要内容:安装OpenCV库读取、显示和存储图像深入理解cv::Mat数据结构定义ROI区域(感兴趣区域)2访问OpenCV官方网站(Winpack)3下载exe后,运行解压,内有文件夹:◦build文件夹:内有编写OpenCV程序所需要的头文件、lib文件和dll文件Include文件夹:内有OpenCV库头文件x64文件夹(针对64位应用程序):vc14文件夹:内有针对VS2015的lib文件(位于lib文件夹下)和dll文件(位于bin文件夹下)vc15文件夹:内有针对VS2017的lib文件(位于lib文件夹下)和dll文件(位于bin文件夹下)较新版本的OpenCV库只提供了针对VS2015和VS2017的64位库,如果需要针对其他VS版本的库或是32位库,需要利用提供的源码,自行编译生成相应版本的库◦sources文件夹:OpenCV源文件,如果需要生成其他版本的lib和dll文件,则需要利用这些源文件,使用cmake生成相应版本的VS项目,并编译运行4将OpenCV库的dll文件路径添加到环境变量Path中(需要重启或注销一下),这样OpenCV程序运行时才能够找到这些dll5从OpenCV3开始,OpenCV库分为两个主要部分:第一部分是成熟的OpenCVAPI库,或称为标准库;第二部分是近期加入的OpenCV算法库(contrib模块)。官方下载的OpenCV库只包含第一部分的库,如果要使用一些更先进的算法,就需要下载contrib模块,并自行编译生成OpenCV库自行编译OpenCV库的方法请参照OpenCV安装配置指南6使用VisualStudio创建空的Win32控制台应用程序7项目属性设置◦VC++目录添加包含目录添加库目录注意:opencv文件夹的位置取决于你的解压路径库目录的设置以VS2012为例(vc11),VS2013为vc12,VS2015为vc14,VS2017为vc158项目属性设置(续)◦链接器输入添加附加依赖项注意:图中以OpenCV3.4.2版本和Debug配置为例在之前添加的库目录下,我们会看到很多lib文件,其中以d结尾的为针对Debug的版本,其他是针对Release的版本9OpenCV模块OpenCV库分为多个模块,其中常用的模块有:◦opencv_core模块,包含了程序库的核心功能,特别是基本的数据结构和算法函数;◦opencv_highgui模块,包含图像、视频读写函数和部分用户界面函数;◦opencv_imgproc模块,包含了主要的图像处理函数;◦……10OpenCV模块(续)每个模块都有一个对应的头文件(位于include目录中),因此在使用这些模块时,需要在程序中包含对应的头文件,例如:#includeopencv2/core.hpp#includeopencv2/imgproc.hpp#includeopencv2/highgui.hpp注:其实core.hpp已经在highgui.hpp中已包含了,可不用写出来11什么是数字图像?12真彩图像13图像可以定义为二维函数f(x,y)x,y―空间(平面)坐标f―强度或灰度x,y,f均为有限、离散量该图像为数字图像数字图像处理:用计算机处理数字图像像素(Pixel):数字图像中的元素14图像坐标系统在数字图像处理中,传统上使用原点(u=0,v=0)位于左上角的坐标系,坐标u、v分别代表图像的列和行。对于一幅尺寸为M×N的图像,最大的列索引是umax=M-1,最大的行索引是vmax=N-115任务:从文件中读取一幅输入图像,在窗口中显示图像,应用一个处理函数,然后把输出图像存储到磁盘。准备工作:创建一个空的控制台应用程序,并按照之前的方法设置项目属性,并添加一个cpp文件编写程序:头文件包含:#includeopencv2/core.hpp//核心库(可省略)#includeopencv2/highgui.hpp//GUI库16编写程序(续):main函数:◦首先定义一个图像类变量:cv::Matimage;//创建一个空图像(尺寸为0×0)◦通过访问cv::Mat的属性来验证该图像大小:std::cout图像大小为image.rowsximage.colsstd::endl;备注:需要增加头文件包含:#includeiostream◦此时运行下程序看看看不到结果,闪退了???Ctrl+F5试试17编写程序(续):◦找一图像文件放到项目文件夹下,将其读入到程序:image=cv::imread(puppy.bmp);◦检查读取是否成功:if(image.empty()){//图像为空?std::cout读取图像失败!std::endl;exit(EXIT_FAILURE);//退出程序}◦定义窗口并显示图像:cv::namedWindow(“原始图像”);//创建窗口(可省)cv::imshow(“原始图像”,image);//显示图像窗口名窗口名图像对象如果imshow中指定的窗口之前未定义,则会自动创建该窗口18编写程序(续):◦运行下看看,能看到窗口么?Ctrl+F5管用不?◦增加一个额外的highgui函数,需要用户按键才能结束程序://0或缺省表示永远地等待按键,正数表示等待指定的毫秒数cv::waitKey(0);◦Tryagain!用不用Ctrl+F5已不重要19编写程序(续):◦对图像进行处理,以将图像水平翻转为例,以下代码加在waitKey之前:cv::Matresult;//创建另一个空的图像//水平翻转图像cv::flip(image,//输入图像result,//输出图像1);//正数表示水平翻转,0表示垂直翻转,负数表示水平和垂直同时翻转flip函数输出图像参数也可设为和输入图像相同,这样输入图像将被修改20编写程序(续):◦在另一个窗口中显示处理结果,这次我们不事先定义窗口:cv::imshow(输出图像,result);◦运行下看看,同时测试下flip第3个参数取不同值的情况21编写程序(续):◦把处理过的图像存储到磁盘里:cv::imwrite(output.bmp,result);图像的格式由文件名后缀决定◦测试下看看图像是否成功保存◦注意:当在程序中使用相对路径名时,如果通过VS运行,当前目录为项目文件所在目录如果双击exe运行,当前目录为exe所在目录文件路径名图像对象22实现原理:◦在OpenCV的C++API中,所有类和函数都在命名空间cv内定义。我们有两种方法可以访问它们:第一种方法是在定义main函数前使用如下声明:usingnamespacecv;第二种方法是使用命名空间规范给所有OpenCV的类和函数加上前缀cv::,使用前缀可让OpenCV的类和函数更容易识别。23实现原理:◦在使用imread函数装载图像时,可以通过设置选项把它转为灰度图像,有些计算机视觉算法是必须使用灰度图像的image=cv::imread(puppy.bmp,cv::IMREAD_GRAYSCALE);这样生成的图像由无符号字节(C++中为unsignedchar)构成,OpenCV中用定义的常量CV_8U表示。◦Haveatry!24实现原理:◦对于灰度图像,有时仍需要在读入时把它转换成三通道彩色图像:image=cv::imread(puppy.bmp,cv::IMREAD_COLOR);此情况为默认情况◦这样创建的图像中每个像素有3字节,OpenCV中用CV_8UC3表示。◦如果输入的图像文件是灰度图像,这三个通道值相同25实现原理:◦如果想读入图像时不改变图像的格式,则:image=cv::imread(puppy.bmp,cv::IMREAD_UNCHANGED);◦可用channels方法检查图像的通道数:std::cout该图像有image.channels()通道std::endl;26实现原理:◦highgui模块非常适合构建原型程序,在生成最终版本的程序时,你很可能会用到IDE提供的GUI模块,这样会让程序看起来更专业扩展:◦highgui模块中可设置鼠标回调函数,对鼠标事件进行响应,也可在图像上绘制图形或写入文本27扩展1:鼠标回调函数的使用◦鼠标回调函数原型:voidonMouse(intevent,//事件类型intx,inty,//鼠标位置intflags,//事件发生时按下了鼠标哪个键void*param//设置回调函数时指定的参数);event取值:cv::EVENT_LBUTTONDOWN,cv::EVENT_LBUTTONUP,cv::EVENT_MOUSEMOVE等等28扩展1:鼠标回调函数的使用(续)实现鼠标左键单击时在命令控制台输出点击位置的像素值(暂时针对灰度图)◦定义鼠标回调函数:voidonMouse(intevent,intx,inty,intflags,void*param){cv::Mat*im=reinterpret_castcv::Mat*(param);switch(event){//什么鼠标事件?casecv::EVENT_LBUTTONDOWN://鼠标左键按下事件//输出(x,y)处像素值std::cout坐标(x,y)处像素值为:static_castint(im-atuchar(cv::Point(x,y)))std::endl;break;}}29扩展1:鼠标回调函数的使用(续)实现鼠标左键单击时在命令控制台输出点击位置的像素值(暂时针对灰度图)◦在main函数中设置鼠标回调函数:cv::setMouseCallback(“原始图像”,//捕获鼠标事件的窗口onMouse,//鼠标回调函数名reinterpret_castvoid*(&image)//传图像对象指针);◦测试下吧,注意使用灰度图像(对于彩色图像只输出了一个通道的像素值),同时验证下图像的坐标系是怎样的30扩展2:在图像上绘图和写入文本◦基本的形状绘制函数有circle、ellipse、line、rectangle等。使用circle函数的例子:cv::circle(image,//目标图像cv::Point(155,110),//中心点坐标65,//半径0,//颜色(这里用黑色,针对灰度图像)3//线的粗细);31扩展2:在图像上绘图和写入文本◦写入文本的例子:cv::putText(image,//目标图像“Iamnotadog!”,//文本(中文兼容不好)cv::Point(40,200),//文本位置cv::FONT_HERSHEY_PLAIN,//字体类型(字体很有限)2.0,//字体大小255,//字体颜色(这里用白色,对于彩色图像则是蓝色)2//文本粗细);32扩展2:在图像上绘图和写入文本◦在main函数中加入上述绘图和写入文本的代码测试下,注意得重新调用imshow才能显示更新后的图像33用来操作图像和矩阵的关键类(从计算机和数学的角度看,图像其实就是矩阵)cv::Mat有两个组成部分:◦头部:包含了矩阵大小、通道数量、数据类型等属性信息◦数据块:包含了图像中所有像素的值◦头部有一个指向数据块的指针,即data属性◦只有在明确要求时,数据块才会被复制。大多数操作仅仅复制了cv::Mat的头部,因此多个对象会共享同一个数据块,这种方式可提高程序运行效率,避免内存泄漏,但也必须了解其带来的后果(后面通过例子体会)34cv::Mat对象的创建:◦创建240行×320列的灰度图cv::Matimage1(240,//行数,图像高度320,//列数,图像宽度CV_8U,//每个像素对应1