1、邹昆主要内容:安装OpenCV库读取、显示和存储图像深入理解cv:Mat数据结构定义ROI区域(感兴趣区域)2访问OpenCV官方网站http:/opencv.org下载OpenCV 3.4.2(Win pack)3下载exe后,运行解压,内有文件夹: build文件夹:内有编写OpenCV程序所需要的头文件、lib文件和dll文件 Include文件夹:内有OpenCV库头文件 x64文件夹(针对64位应用程序): vc14文件夹:内有针对VS2015的lib文件(位于lib文件夹下)和dll文件(位于bin文件夹下) vc15文件夹:内有针对VS2017的lib文件(位于lib文件夹下)和
2、dll文件(位于bin文件夹下) 较新版本的OpenCV库只提供了针对VS2015和VS2017的64位库,如果需要针对其他VS版本的库或是32位库,需要利用提供的源码,自行编译生成相应版本的库 sources文件夹:OpenCV源文件,如果需要生成其他版本的lib和dll文件,则需要利用这些源文件,使用cmake生成相应版本的VS项目,并编译运行4将OpenCV库的dll文件路径添加到环境变量Path中(需要重启或注销一下),这样OpenCV程序运行时才能够找到这些dll5从OpenCV 3开始,OpenCV库分为两个主要部分:第一部分是成熟的OpenCV API库,或称为标准库;第二部分是
3、近期加入的OpenCV算法库(contrib模块)。官方下载的OpenCV库只包含第一部分的库,如果要使用一些更先进的算法,就需要下载contrib模块,并自行编译生成OpenCV库自行编译OpenCV库的方法请参照OpenCV安装配置指南6使用Visual Studio创建空的Win32控制台应用程序7项目属性设置 VC+目录 添加包含目录 添加库目录 注意: opencv文件夹的位置取决于你的解压路径 库目录的设置以VS2012为例(vc11),VS2013为vc12,VS2015为vc14,VS2017为vc158项目属性设置(续) 链接器 输入 添加附加依赖项 注意: 图中以OpenC
4、V 3.4.2版本和Debug配置为例 在之前添加的库目录下,我们会看到很多lib文件,其中以d结尾的为针对Debug的版本,其他是针对Release的版本9OpenCV 模块OpenCV库分为多个模块,其中常用的模块有: opencv_core模块,包含了程序库的核心功能,特别是基本的数据结构和算法函数; opencv_highgui模块,包含图像、视频读写函数和部分用户界面函数; opencv_imgproc模块,包含了主要的图像处理函数; 10OpenCV 模块(续)每个模块都有一个对应的头文件(位于include目录中),因此在使用这些模块时,需要在程序中包含对应的头文件,例如:#in
5、clude #include #include 注:其实core.hpp已经在highgui.hpp中已包含了,可不用写出来11什么是数字图像?12真彩图像13图像可以定义为二维函数图像可以定义为二维函数 f(x,y)x, y空间空间(平面平面)坐标坐标f 强度或灰度强度或灰度x,y,f 均为有限、离散量均为有限、离散量该图像为数字图像该图像为数字图像数字图像处理:用计算机处理数字图像数字图像处理:用计算机处理数字图像像素像素(Pixel):数字图像中的元素:数字图像中的元素14图像坐标系统在数字图像处理中,传统上使用原点在数字图像处理中,传统上使用原点(u=0,v=0)位于左上)位于左上角的
6、坐标系,坐标角的坐标系,坐标u、v分别代表图像的列和行。对于一幅尺分别代表图像的列和行。对于一幅尺寸为寸为MN的图像,最大的列索引是的图像,最大的列索引是umax=M-1,最大的行索引,最大的行索引是是vmax=N-115任务:从文件中读取一幅输入图像,在窗口中显示图像,应用一个处理函数,然后把输出图像存储到磁盘。准备工作:创建一个空的控制台应用程序,并按照之前的方法设置项目属性,并添加一个cpp文件编写程序:头文件包含:#include / 核心库(可省略)#include / GUI库16编写程序(续):main函数: 首先定义一个图像类变量:cv:Mat image;/ 创建一个空图像(
7、尺寸为00) 通过访问cv:Mat的属性来验证该图像大小:std:cout 图像大小为 image.rows x image.cols std:endl;备注:需要增加头文件包含:#include 此时运行下程序看看 看不到结果,闪退了? Ctrl+F5试试17编写程序(续): 找一图像文件放到项目文件夹下,将其读入到程序: image = cv:imread(puppy.bmp); 检查读取是否成功: if (image.empty() / 图像为空? std:cout 读取图像失败! std:endl; exit(EXIT_FAILURE); / 退出程序 定义窗口并显示图像: cv:na
8、medWindow(“原始图像”); / 创建窗口(可省) cv:imshow(“原始图像”, image); / 显示图像窗口名窗口名图像对象如果如果imshow中指定的窗口之前未定义,则会中指定的窗口之前未定义,则会自动创建该窗口自动创建该窗口18编写程序(续): 运行下看看,能看到窗口么?Ctrl+F5管用不? 增加一个额外的highgui函数,需要用户按键才能结束程序:/ 0或缺省表示永远地等待按键,正数表示等待指定的毫秒数 cv:waitKey(0); Try again! 用不用Ctrl+F5已不重要19编写程序(续): 对图像进行处理,以将图像水平翻转为例,以下代码加在waitK
9、ey之前: cv:Mat result; / 创建另一个空的图像 / 水平翻转图像 cv:flip(image, / 输入图像 result, / 输出图像 1); / 正数表示水平翻转,0表示垂直翻 转,负数表示水平和垂直同时翻转flip函数输出图像参数也可设为和输入图像相同,这样输入图像将被修改20编写程序(续): 在另一个窗口中显示处理结果,这次我们不事先定义窗口: cv:imshow(输出图像, result); 运行下看看,同时测试下flip第3个参数取不同值的情况21编写程序(续): 把处理过的图像存储到磁盘里: cv:imwrite(output.bmp, result); 图像
10、的格式由文件名后缀决定 测试下看看图像是否成功保存 注意:当在程序中使用相对路径名时, 如果通过VS运行,当前目录为项目文件所在目录 如果双击exe运行,当前目录为exe所在目录文件路径名图像对象22实现原理: 在OpenCV的C+ API中,所有类和函数都在命名空间cv内定义。我们有两种方法可以访问它们: 第一种方法是在定义main函数前使用如下声明: using namespace cv; 第二种方法是使用命名空间规范给所有OpenCV的类和函数加上前缀cv:,使用前缀可让OpenCV的类和函数更容易识别。23实现原理: 在使用imread函数装载图像时,可以通过设置选项把它转为灰度图像,
11、有些计算机视觉算法是必须使用灰度图像的 image = cv:imread(puppy.bmp, cv:IMREAD_GRAYSCALE); 这样生成的图像由无符号字节(C+中为unsigned char)构成,OpenCV中用定义的常量CV_8U表示。 Have a try!24实现原理: 对于灰度图像,有时仍需要在读入时把它转换成三通道彩色图像: image = cv:imread(puppy.bmp, cv:IMREAD_COLOR); 此情况为默认情况 这样创建的图像中每个像素有3字节,OpenCV中用CV_8UC3表示。 如果输入的图像文件是灰度图像,这三个通道值相同25实现原理:
12、如果想读入图像时不改变图像的格式,则: image = cv:imread(puppy.bmp, cv:IMREAD_UNCHANGED); 可用channels方法检查图像的通道数: std:cout 该图像有 image.channels() 通道 std:endl;26实现原理: highgui模块非常适合构建原型程序,在生成最终版本的程序时,你很可能会用到IDE提供的GUI模块,这样会让程序看起来更专业扩展: highgui模块中可设置鼠标回调函数,对鼠标事件进行响应,也可在图像上绘制图形或写入文本27扩展1:鼠标回调函数的使用 鼠标回调函数原型: void onMouse(int e
13、vent, / 事件类型int x, int y, / 鼠标位置int flags, / 事件发生时按下了鼠标哪个键void *param / 设置回调函数时指定的参数 );event取值:cv:EVENT_LBUTTONDOWN, cv:EVENT_LBUTTONUP,cv:EVENT_MOUSEMOVE等等28扩展1:鼠标回调函数的使用(续)实现鼠标左键单击时在命令控制台输出点击位置的像素值(暂时针对灰度图) 定义鼠标回调函数:void onMouse(int event, int x, int y, int flags, void* param) cv:Mat *im = reinter
14、pret_cast(param);switch (event) / 什么鼠标事件?case cv:EVENT_LBUTTONDOWN:/ 鼠标左键按下事件 / 输出(x, y)处像素值 std:cout 坐标 ( x , y ) 处像素值为: static_cast(im-at (cv:Point(x, y) std:endl; break;29扩展1:鼠标回调函数的使用(续)实现鼠标左键单击时在命令控制台输出点击位置的像素值(暂时针对灰度图) 在main函数中设置鼠标回调函数: cv:setMouseCallback( “原始图像”, / 捕获鼠标事件的窗口 onMouse, / 鼠标回调函
15、数名 reinterpret_cast(&image) / 传图像对象指针 ); 测试下吧,注意使用灰度图像(对于彩色图像只输出了一个通道的像素值),同时验证下图像的坐标系是怎样的30扩展2:在图像上绘图和写入文本 基本的形状绘制函数有circle、ellipse、line、rectangle等。使用circle函数的例子: cv: circle(image, / 目标图像cv:Point(155, 110), /中心点坐标65, /半径0,/颜色(这里用黑色,针对灰度图像)3/ 线的粗细 );31扩展2:在图像上绘图和写入文本 写入文本的例子: cv:putText( image, / 目标
16、图像“I am not a dog!”, / 文本(中文兼容不好)cv:Point(40, 200), / 文本位置cv:FONT_HERSHEY_PLAIN, / 字体类型(字体很有限)2.0, / 字体大小 255,/ 字体颜色(这里用白色,对于彩色图像则是蓝色)2 / 文本粗细);32扩展2:在图像上绘图和写入文本 在main函数中加入上述绘图和写入文本的代码测试下,注意得重新调用imshow才能显示更新后的图像33用来操作图像和矩阵的关键类(从计算机和数学的角度看,图像其实就是矩阵)cv:Mat有两个组成部分: 头部:包含了矩阵大小、通道数量、数据类型等属性信息 数据块:包含了图像中所
17、有像素的值 头部有一个指向数据块的指针,即data属性 只有在明确要求时,数据块才会被复制。大多数操作仅仅复制了cv:Mat的头部,因此多个对象会共享同一个数据块,这种方式可提高程序运行效率,避免内存泄漏,但也必须了解其带来的后果(后面通过例子体会)34cv:Mat对象的创建: 创建240行320列的灰度图 cv:Mat image1(240, / 行数,图像高度320, / 列数,图像宽度CV_8U, / 每个像素对应1无符号字节100/ 默认像素值(灰度值) );35cv:Mat对象的创建: 创建240行320列的彩色图 cv:Mat image2(240, / 行数,图像高度320, /
18、 列数,图像宽度CV_8UC3, / 每个像素有3通道,各对应1无符号字节cv:Scalar(0, 0, 255) / 默认像素值(红色) ); CV_8UC3中C3意思是Channel 3 OpenCV中RGB三通道的存储顺序是BGR! cv:Scalar也可用于表示灰度值,例如cv:Scalar(100)36cv:Mat对象的创建: 创建240行320列的未初始化的彩色图 cv:Mat image2(cv:Size(320, 240) / 图像尺寸,参数为宽度和高度CV_8UC3/ 每个像素有3通道,各对应1无符号字节 );分配或重新分配一个大小或类型不同的图像数据块 image1.cre
19、ate(200, 200, CV_8U); 第1个200为行数,第2个200为列数 如果之前已分配数据块,则之前的数据块内容会被释放37图像的复制 浅复制(所有图像都使用同一个数据块): cv:Mat image4(image3); image1 = image3; 注意:函数的参数传递和值返回也是使用的浅复制 OpenCV对cv:Mat对象进行引用计数,如果引用计数为0, 则该对象内存自动被释放 深复制(各图像使用不同的数据块): image3.copyTo(image2); cv:Mat image5 = image3.clone(); image1.convertTo(image2, C
20、V_32F, 1/255.0, 0.0);转换数据格式结果图像32位浮点数数据缩放比例像素值偏移量38对于含有cv:Mat属性的类,尽量不要提供返回该cv:Mat属性的public方法,以避免该属性在类外面被悄悄修改 class Testcv:Mat ima; / 图像属性 public: / 在构造函数中创建一幅灰度图像Test() : ima(240, 320, CV_8U, cv:Scalar(100) / 此方法返回一个图像属性,是一种不好的做法 cv:Mat method() return ima; ;39测试P12中1.4.1程序说明: cv:namedWindow可省略 imag
21、e1 = 200; 是将图像image1所有像素值均设为200问题: 程序中一共产生过几个不同的图像数据块? 将函数function内的局部对象ima赋值给了图像对象gray,ima生命周期结束后为何gray还能够显示出来?40扩展1:输入和输出数组 在OpenCV中,有很多方法和函数使用cv:InputArray类型作为输入参数 cv:InputArray类型是一个简单的代理类,用来概括OpenCV中数组的概念,避免同一个方法因为使用了不同类型的输入参数而出现多个不同的版本 对于cv:InputArray类型参数可传入cv:Mat、cv:Scalar、cv:Vec、std:vector等兼容
22、类型对象 cv:InputArray只是一个接口,不能显示定义该类型对象 类似的还有一个代理类cv:OutputArray,用来指定某些方法的返回数组41扩展2:处理小矩阵 开发应用程序时,可能会遇到需要处理小矩阵的情况,可使用模板类cv:Matx和它的子类 例:/ 33双精度型矩阵cv:Matx33d matrix(3.0, 2.0, 1.0, 2.0, 1.0, 3.0, 1.0, 2.0, 3.0);/ 31矩阵(即向量)cv:Matx31d vector(5.0, 1.0, 3.0);/ 相乘cv:Matx31d result = matrix * vector;42有时我们需要让一个
23、处理函数只对图像的某个部分起作用。OpenCV中可以定义图像的子区域(感兴趣区域),并把这个子区域当作普通图像进行操作。任务:把下面的小标志插入到测试图像中为了实现此功能,我们定义一个感兴趣区域(Region Of Interest, ROI),把标志图像复制过去43ROI实际上就是一个cv:Mat对象,它与它的父图像使用同一个数据块,并且有一个头部信息表示ROI的坐标在图像的右下角定义一个ROI: cv:Mat imageROI(image, cv:Rect(image.cols - logo.cols, / ROI左上角x坐标image.rows - logo.rows, / ROI左上角
24、y坐标logo.cols, / ROI的宽度logo.rows / ROI的高度);插入标志: logo.copyTo(imageROI); / logo为标志图像44ROI还可以用行和列的范围来描述: imageROI = image(cv:Range(image.rows logo.rows, image.rows), / 行范围cv:Range(image.cols logo.cols, image.cols); / 列范围 定义由图像中的一些行组成的ROI: cv:Mat imageROI = image.rowRange(start, end); 定义由图像中的一些列组成的ROI:
25、cv:Mat imageROI = image.colRange(start, end);由于图像和ROI共享了同一块图像数据,因此ROI的任何转变都会影响原始图像的对应区域在定义ROI时数据并没有被复制,因此它的执行时间是固定的,不受ROI尺寸的影响45编写程序,实现把小标志插入到图像右下角46扩展:使用图像掩码OpenCV中的函数或方法通常对图像中所有像素进行操作,通过定义和使用掩码可以限制这些函数或方法的作用范围掩码是一幅8位图像,如果掩码中某个位置像素值不为0,则对该位置的像素进行操作;否则不进行操作在调用copyTo方法时就可以使用掩码47任务:利用掩码只复制标志中白色的部分在图像的
26、右下角定义一个ROI: imageROI = image(cv:Rect(image.cols - logo.cols, / ROI左上角x坐标image.rows - logo.rows, / ROI左上角y坐标logo.cols, logo.rows / ROI的宽度和高度);把标志图作为掩码 cv:Mat mask(logo);插入标志,只复制掩码图像中不为黑色的像素位置 logo.copyTo(imageROI, mask);48编写程序实现该任务程序步骤提示: 读入原图 读入标志图(在本例中掩码图等同于标志图) 定义ROI 用带掩码的copyTo插入标志 显示处理后图像 等待按键49结果图像:50
侵权处理QQ:3464097650--上传资料QQ:3464097650
【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。