收藏 分享(赏)

OpenCV中文翻译教程.doc

上传人:精品资料 文档编号:10475938 上传时间:2019-11-21 格式:DOC 页数:98 大小:1.17MB
下载 相关 举报
OpenCV中文翻译教程.doc_第1页
第1页 / 共98页
OpenCV中文翻译教程.doc_第2页
第2页 / 共98页
OpenCV中文翻译教程.doc_第3页
第3页 / 共98页
OpenCV中文翻译教程.doc_第4页
第4页 / 共98页
OpenCV中文翻译教程.doc_第5页
第5页 / 共98页
点击查看更多>>
资源描述

1、OPENCV2 基础(补充材料 )OpenCV_tutorials 翻译资料整理而来翻译材料出处: http:/ Mat - 基本图像容器 .1二、 OpenCV 如何扫描图像、利用查找表和计时 1三、 矩阵的掩码操作 1四、 使用 OpenCV 对两幅图像求和(求混合 (blending)) .1五、 改变图像的对比度和亮度 1六、 图像平滑处理 1七、 腐蚀与膨胀(Eroding and Dilating) .1八、 实现自己的线性滤波器 1九、 给图像添加边界 1十、 Sobel 导数 1十一、 霍夫线变换 1十二、 直方图均衡化 1十三、 仿射变换 1十四、 Remapping 重映射

2、 .12一、 Mat - 基本图像容器目的从真实世界中获取数字图像有很多方法,比如数码相机、扫描仪、CT 或者磁共振成像。无论哪种方法,我们(人类)看到的是图像,而让数字设备来“看“ 的时候,则是在记录图像中的每一个点的数值。比如上面的图像,在标出的镜子区域中你见到的只是一个矩阵,该矩阵包含了所有像素点的强度值。如何获取并存储这些像素值由我们的需求而定,最终在计算机世界里所有图像都可以简化为数值矩以及矩阵信息。作为一个计算机视觉库, OpenCV 其主要目的就是通过处理和操作这些信息,来获取更高级的信息。因此,OpenCV 如何存储并操作图像是你首先要学习的。Mat在 2001 年刚刚出现的时

3、候,OpenCV 基于 C 语言接口而建。为了在内存(memory)中存放图像,当时采用名为 IplImage 的 C 语言结构体,时至今日这仍出现在大多数的旧版教程和教学材料。但这种方法必须接受 C 语言所有的不足,这其中最大的不足要数手动内存管理,其依据是用户要为开辟和销毁内存负责。虽然对于小型的程序来说手动管理内存不是问题,但一旦代码开始变得越来越庞大,你需要越来越多地纠缠于这个问题,而不是着力解决你的开发目标。幸运的是,C+出现了,并且带来类的概念,这给用户带来另外一个选择:自动的内存管理(不严谨地说)。这是一个好消息,如果 C+完全兼容 C 的话,这个变化不会带来兼容性问题。为此,O

4、penCV 在 2.0 版本中引入了一个新的C+接口,利用自动内存管理给出了解决问题的新方法。使用这个方法,你不需要纠结在管理内存上,而且你的代码会变得简洁(少写多得)。但 C+接口3唯一的不足是当前许多嵌入式开发系统只支持 C 语言。所以,当目标不是这种开发平台时,没有必要使用 旧 方法(除非你是自找麻烦的受虐狂码农)。关于 Mat ,首先要知道的是你不必再手动地(1)为其开辟空间( 2)在不需要时立即将空间释放。但手动地做还是可以的:大多数 OpenCV 函数仍会手动地为输出数据开辟空间。当传递一个已经存在的 Mat 对象时,开辟好的矩阵空间会被重用。也就是说,我们每次都使用大小正好的内存

5、来完成任务。基本上讲 Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同矩阵可以是不同的维数)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。因此,当在程序中传递图像并创建拷贝时,大的开销是由矩阵造成的,而不是信息头。OpenCV 是一个图像处理库,囊括了大量的图像处理函数,为了解决问题通常要使用库中的多个函数,因此在函数中传递图像是家常便饭。同时不要忘了我们正在讨论的是计算量很大的图像处理算法,因此,除非万不得已,我们不应该拷贝 大 的图像,因为这会降

6、低程序速度。为了搞定这个问题,OpenCV 使用引用计数机制。其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵。这通过让矩阵指针指向同一地址而实现。而拷贝构造函数则 只拷贝信息头和矩阵指针 ,而不拷贝矩阵。以上代码中的所有 Mat 对象最终都指向同一个也是唯一一个数据矩阵。虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其它对象。实际上,不同的对象只是访问相同数据的不同途径而已。这里还要提及一个比较棒的功能:你可以创建只引用部分数据的信息头。比如想要创建一个感兴趣区域( ROI ),你只需要创建包含边界信息的信息头:Mat D (A, Rect(10, 10, 100,

7、100) ); / using a rectangleMat E = A(Range:all(), Range(1,3); / using row and column boundaries现在你也许会问,如果矩阵属于多个 Mat 对象,那么当不再需要它时谁来负责清理?简单的回答是:最后一个使用它的对象。通过引用计数机制来实现。无论什么时候有人拷贝了一个 Mat 对象的信息头,都会增加矩阵的引用次数;反之当一个头被释放之后,这个计数被减一;当计数值为零,矩阵会被清理。但某些时候你仍会想拷贝矩阵本身(不只是信息头和矩阵指针),这时可以使用函数 clone() 或者 copyTo() 。Mat A

8、, C; / 只创建信息头部分A = imread(argv1, CV_LOAD_IMAGE_COLOR); / 这里为矩阵开辟内存Mat B(A); / 使用拷贝构造函数C = A; / 赋值运算符4Mat F = A.clone();Mat G;A.copyTo(G);在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。总结一下,你需要记住的是 OpenCV 函数中输出图像的内存分配是自动完成的(如果不特别指定的话)。 使用 OpenCV 的 C+接口时不需要考虑内存释放问题。 赋值运算符和拷贝构造函数( ctor )只拷贝信息头。 使用函数 clone() 或者 copyTo(

9、) 来拷贝一副图像的矩阵。存储 方法这里讲述如何存储像素值。需要指定颜色空间和数据类型。颜色空间是指对一个给定的颜色,如何组合颜色元素以对其编码。最简单的颜色空间要属灰度级空间,只处理黑色和白色,对它们进行组合可以产生不同程度的灰色。对于 彩色 方式则有更多种类的颜色空间,但不论哪种方式都是把颜色分成三个或者四个基元素,通过组合基元素可以产生所有的颜色。RGB 颜色空间是最常用的一种颜色空间,这归功于它也是人眼内部构成颜色的方式。它的基色是红色、绿色和蓝色,有时为了表示透明颜色也会加入第四个元素 alpha (A)。有很多的颜色系统,各有自身优势: RGB 是最常见的,这是因为人眼采用相似的工

10、作机制,它也被显示设备所采用。 HSV 和 HLS 把颜色分解成色调、饱和度和亮度/ 明度。这是描述颜色更自然的方式,比如可以通过抛弃最后一个元素,使算法对输入图像的光照条件不敏感。 YCrCb 在 JPEG 图像格式中广泛使用。 CIE L*a*b*是一种在感知上均匀的颜色空间,它适合用来度量两个颜色之间的 距离 。每个组成元素都有其自己的定义域,取决于其数据类型。如何存储一个元素决定了我们在其定义域上能够控制的精度。最小的数据类型是 char ,占一个字节或者 8 位,可以是有符号型(0 到 255 之间)或无符号型(-127 到+127 之间)。尽管使用三个 char 型元素已经可以表示

11、 1600 万种可能的颜色(使用 RGB颜色空间),但若使用 float(4 字节,32 位)或 double(8 字节,64 位)则能给出更加精细的颜色分辨能力。但同时也要切记增加元素的尺寸也会增加了图像所占的内存空间。显式地创建一个 Mat 对象教程 读取、修改、保存图像 已经讲解了如何使用函数 imwrite() 将一个矩阵写入图像文件中。但是为了 debug,更加方便的方式是看实际值。为此,你可以通过 Mat 的运算符 MatCreate() function: 函数M.create(4,4, CV_8UC(2);cout (3,3) v;v.push_back( (float)CV_

12、PI); v.push_back(2); v.push_back(3.01f);cout vPoints(20);for (size_t E = 0; E divideWith;if (!s)cout (i);for ( j = 0; j it, end; for( it = I.begin(), end = I.end(); it != end; +it)*it = table*it;break;16case 3: MatIterator_ it, end; for( it = I.begin(), end = I.end(); it != end; +it)(*it)0 = table(*

13、it)0;(*it)1 = table(*it)1;(*it)2 = table(*it)2;return I; 对于彩色图像中的一行,每列中有 3 个 uchar 元素,这可以被认为是一个小的包含 uchar 元素的 vector,在 OpenCV 中用 Vec3b 来命名。如果要访问第n 个子列,我们只需要简单的利用来操作就可以。需要指出的是,OpenCV的迭代在扫描过一行中所有列后会自动跳至下一行,所以说如果在彩色图像中如果只使用一个简单的 uchar 而不是 Vec3b 迭代的话就只能获得蓝色通道(B)里的值。3. 通过相关返回值的 On-the-fly 地址计算 事实上这个方法并不推

14、荐被用来进行图像扫描,它本来是被用于获取或更改图像中的随机元素。它的基本用途是要确定你试图访问的元素的所在行数与列数。在前面的扫描方法中,我们观察到知道所查询的图像数据类型是很重要的。这里同样的你得手动指定好你要查找的数据类型。下面的代码中是一个关于灰度图像的示例(运用 + at() 函数):Mat const int channels = I.channels();switch(channels)case 1: for( int i = 0; i (i,j) = tableI.at(i,j);break;case 3: Mat_ _I = I;for( int i = 0; i data t

15、ype. 它同样可以被用于获知矩阵的数据类型,你可以简单利用()操作返回值来快速获取查询结果. 值得注意的是你可以利用 at() 函数来用同样速度完成相同操作. 它仅仅是为了让懒惰的程序员少写点 _ ,一个包含于 core module 的函数. 首先我们建立一个 mat 型用于查表:Mat lookUpTable(1, 256, CV_8U);uchar* p = lookUpTable.data; for( int i = 0; i (j - 1);const uchar* current = myImage.ptr(j );const uchar* next = myImage.ptr(

16、j + 1);uchar* output = Result.ptr(j);for(int i= nChannels;i (5*currenti-currenti-nChannels - currenti+nChannels - previousi - nexti);Result.row(0).setTo(Scalar(0);Result.row(Result.rows-1).setTo(Scalar(0);Result.col(0).setTo(Scalar(0);Result.col(Result.cols-1).setTo(Scalar(0);刚进入函数的时候,我们要确保输入图像是无符号字

17、符类型的。为了做到这点,我们使用了 CV_Assert 函数。若该函数括号内的表达式 为 false,则会抛出一个错误。CV_Assert(myImage.depth() = CV_8U); / 仅接受 uchar 图像然后,我们创建了一个与输入有着相同大小和类型的输出图像。在 图像矩阵是如何存储在内存之中的? 一节可以看到,根据图像的通道数,我们有一个或多22个子列。我们用指针在每一个通道上迭代,因此通道数就决定了需计算的元素总数。Result.create(myImage.size(),myImage.type();const int nChannels = myImage.channel

18、s();利用 C 语言的操作符,我们能简单明了地访问像素。因为要同时访问多行像素,所以我们获取了其中每一行像素的指针(分别是前一行、当前行和下一行)。此外,我们还需要一个指向计算结果存储位置的指针。有了这些指针后,我们使用操作符,就能 轻松访问到目标元素。为了让输出指针向前移动,我们在每一次操作之后对输出指针进行了递增(移动一个字节):for(int j = 1 ; j (j - 1);const uchar* current = myImage.ptr(j );const uchar* next = myImage.ptr(j + 1);uchar* output = Result.ptr(

19、j);for(int i= nChannels;i (5*currenti-currenti-nChannels - currenti+nChannels - previousi - nexti);在图像的边界上,上面给出的公式会访问不存在的像素位置(比如(0,-1))。因此我们的公式对边界点来说是未定义的。一种简单的解决方法,是不对这些边界点使用掩码,而直接把它们设为 0:Result.row(0).setTo(Scalar(0); / 上边界Result.row(Result.rows-1).setTo(Scalar(0); / 下边界 Result.col(0).setTo(Scalar

20、(0); / 左边界 Result.col(Result.cols-1).setTo(Scalar(0); / 右边界23filter2D 函数 滤波器在图像处理中的应用太广泛了,因此 OpenCV 也有个用到了滤波器掩码(某些场合也称作核)的函数。不过想使用这个函数,你必须先定义一个表示掩码的 Mat 对象:Mat kern = (Mat_(3,3) #include #include using namespace cv;int main( int argc, char* argv )double alpha = 0.5; double beta; double input;Mat src

21、1, src2, dst;/ Ask the user enter alphastd:coutinput;/ We use the alpha provided by the user iff it is between 0 and 1if( alpha = 0 / Read image ( same size, same type )src1 = imread(“/images/LinuxLogo.jpg“);src2 = imread(“/images/WindowsLogo.jpg“);if( !src1.data ) printf(“Error loading src1 n“); re

22、turn -1; if( !src2.data ) printf(“Error loading src2 n“); return -1; / Create WindowsnamedWindow(“Linear Blend“, 1);beta = ( 1.0 - alpha );addWeighted( src1, alpha, src2, beta, 0.0, dst);imshow( “Linear Blend“, dst );waitKey(0);return 0;说明 1.既然我们要执行27我们需要两幅输入图像 ( 和 )。相应地,我们使用常用的方法加载图像src1 = imread(“

23、/images/LinuxLogo.jpg“);src2 = imread(“/images/WindowsLogo.jpg“);Warning 因为我们对 src1 和 src2 求 和 ,它们必须要有相同的尺寸(宽度和高度)和类型。现在我们生成图像 .为此目的,使用函数 addWeighted 可以很方便地实现:一、beta = ( 1.0 - alpha );addWeighted( src1, alpha, src2, beta, 0.0, dst);这是因为 addWeighted 进行如下计算这里 对应于上面代码中被设为 的参数。3.创建显示窗口,显示图 像并等待用户结束程序。结果

24、 28五、 改变图像的对比度和亮度目的 本篇教程中,你将学到: 访问像素值 用 0 初始化矩阵 saturate_cast 是做什么用的,以及它为什么有用 一些有关像素变换的精彩内容原理 Note以下解释节选自 Richard Szeliski 所著 Computer Vision: Algorithms and Applications图像处理 一般来说,图像处理算子是带有一幅或多幅输入图像、产生一幅输出图像的函数。29 图像变换可分为以下两种: 点算子(像素变换) 邻域(基于区域的)算子像素变换 在这一类图像处理变换中,仅仅根据输入像素值(有时可加上某些全局信息或参数)计算相应的输出像素值。 这类算子包括 亮度和 对 比度 调 整 ,以及颜色校正和变换。亮度和对比度调整 两种常用的点过程(即点算子),是用常数对点进行 乘法 和 加法 运算: 两个参数 和 一般称作 增益 和 偏置 参数。我们往往用这两个参数来分别控制 对 比度 和 亮度 。 你可以把 看成源图像像素,把 看成输出图像像素。这样一来,上面的式子就能写得更清楚些:其中, 和 表示像素位于 第 i 行 和 第 j 列 。代码

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报