本文参照《opencv_2.4.9tutorial》的core部分完成。因为功力还不足以学习侯捷那种大师一样去深入浅出的解析opencv的源码,也只能先学会怎么用opencv,然后实在觉得不够才会去看源码,了解一个开源项目的源码,其实也有助于提升架构框架的能力,和写出一手大神范的代码。

这里推荐一牛逼opencv的大神:http://blog.csdn.net/poem_qianmo/article/details/19809337  浅墨大神即将要出有关《OpenCV3》的书了。

然后推荐一直战斗在Opencv引入中国 传教士行列的大神于仕琪 http://www.opencv.org.cn/forum.php?mod=viewthread&tid=33549,这里面写了简短的入门教程,对于没有编程经验的来说可以看看,其中一句说的好:“学不好opencv主要是编码能力和模式识别、图像处理上两个方面的欠缺的问题”。

正文

一、opencv2及其特点

相对于opencv的历史来说,可以看《学习opencv》即《learning opencv》,这里面有详细的介绍。之前的opencv版本是基于C代码写的,因为c/cpp的国际标准化,使得能够在后期移植到各个平台上,所以很多开源项目其实都是基于c/cpp系列的(个人:不过 我还不知道为什么不用java来写原始代码,不过 估计肯定是效率,问题吧?)。之前版本的opencv也叫做opencv1,所以对于有cpp接口的opencv2 来说,才有了我们安装opencv中的一步----需要去包含opencv2这个文件夹。而且相对来说,如果是.h
的 那么一般就是 c 的头文件;如果是 .hpp 的那么就是cpp接口的头文件。而且opencv是以c/cpp系列为源码,然后在基于此加上一些不同的编程语言接口,并使用cmake来跨平台吧(个人:这里的个中缘由 因为知识尚浅,而且都没真正了解cmake的内部原理,信口胡说的。)

相对于cpp接口的源码来说,它就有很多好处了,比如不需要像之前需要用 c 语言来模拟 cpp的面向对象那样,能够更加的模块化,而且对于编程人员来说,新增的cpp接口中增加的智能指针类,可以大大减少编程人员所需的自己申请 自己释放内存的问题。(其实 个人更加喜欢cpp,自然各种好话往上堆啊,哈哈)。

相比较来说,opencv2是基于cpp写的,基于cpp的语言特点更能模块化,它每个不同的模块都包含着与其相关的数据结构,所以如果只需要某个模块,那么就直接包含那个模块就好,例如:

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

与cpp的语言特性一样,它使用了命名空间,只要在头上加上using namespace cv;就行,当然也可以使用其中的部分,有关命名空间的原理:参考《c++ 程序设计语言》一书相关章节,这可是cpp发明人写的书额。

第二版与第一版最大的不同在于它不是用IplImage和CvMat这样的数据结构,而是通过使用Mat这样的类来代替,其实在《learning opencv》一书中也说了,在opencv1中的很多地方这两个数据结构是相通的,换句话说也即是冗余了,估计当时只是为了区分图像处理还是矩阵数据处理吧。

关于如何将opencv2 与opencv1 进行混合编程或者兼容 可以参考http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/interoperability_with_OpenCV_1/interoperability_with_OpenCV_1.html#interoperabilitywithopencv1 ,这是《opencv_2.4.9tutorial》的对应章节的中文版。

二、opencv2的模块介绍

参考 浅墨的介绍http://blog.csdn.net/poem_qianmo/article/details/19925819。其实不同模块对应不同功能,首要的当然是学习core和highgui部分,和imgproc模块部分。这部分没啥好写的,也就不重复介绍了。

三、Mat类的使用及数据的访问方法

    3.1Mat类的初始化

1、考虑到OpenCV中会对大图像进行操作,如果一两幅也就算了,可是总有经常拷贝图像和视频帧的时候,这时候如果频繁的拷贝数据,那么就很慢了,而且大部分的情况下程序员的想法也是想对同一幅图像数据进行操作,所以OpenCV将这个想法作为默认想法,如果真想在复制一副图像的同样的数据,那么就显式的调用函数。在OpenCV中一般会有(opencv1中有图像或者矩阵头,和图像或者矩阵数据部分 两个部分),这里也是一样,只不过将默认拷贝矩阵头和指向数据的指针进行复制(有点像cpp语言特性中的引用)。

Mat A, C;                                 // 只创建信息头部分
A = imread(argv[1], CV_LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存 Mat B(A); // 使用拷贝构造函数 C = A; // 赋值运算符
Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range:all(), Range(1,3)); // using row and column boundaries

这里是先声明矩阵A和C,然后进行图像的读取(imread函数的参数这里暂且不说,有兴趣的去看看对应的API介绍,不过照虎画猫 其实都差不多):1、对于初始化式、赋值式、建立感兴趣区域(ROI,不过这里也算是初始化式、赋值式),这三种操作来说,都是类似引用的想法,在Mat类不同对象中只有包含的数据(也就是矩阵的的大小啊,通道啊什么的)是完整复制的,但是它们操作的时候对应的数据却是同一块区域,所以这里可以猜测,Mat类的内部结构其实是一堆信息外带一个指向数据的指针,这样就保证复制的是指针而不是整体的数据(可以这么想
,不过具体怎样得去看源码,只是这样暂时便于理解)。

如果真想复制数据部分,那么:

Mat F = A.clone();
Mat G;
A.copyTo(G);

通过使用类中的clone()和copyTo()函数来完成,这时候操作不同的类对象,那么两个就完全没什么牵连的关系了。

2、上面的是从图像中读取数据,这才有的矩阵,但是如果想平地而起的创建咋办?那就得使用Mat类的其他构造函数了。这里不介绍多,尽量往简单通用的地方靠,等使用opencv多了,那就可以往深的地方看看:

Mat M(2,2, CV_8UC3, Scalar(0,0,255)); 
或者  Mat R = Mat(3, 2, CV_8UC3); randu(R, Scalar::all(0), Scalar::all(255));
int sz[3] = {2,2,2};      Mat L(3,sz, CV_8UC(1), Scalar::all(0));

上面第一行是建立个二维矩阵:参数为行、列、元素类型、每个元素的初始化值。CV_8UC3,8就是8 bit,U是无符号,C是通道,在OpenCV中是通过宏来定义的,具体的可以参见<types_c.h>的584-625行。ps:对于二维矩阵有个特例就是可以直接cout<<M<<endl; 因为内部实现了对2维矩阵的直接输出。在官方文档的具体输出部分也是只介绍了二维矩阵在不同格式下的显示(不过因为暂时只玩过matlab和cpp,那些python啥的没接触,也就不介绍了 http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/core/mat%20-%20the%20basic%20image%20container/mat%20-%20the%20basic%20image%20container.html#matthebasicimagecontainer
这里的格式化打印部分,有兴趣的看看,个人觉得这个是在黑框显示的,只是给程序员看内部数据的,所以觉得有一个用得着就够了,介绍多了,容易晕)。

上面第二行是先建立矩阵大小,然后不指定初始化值,通过使用randu()函数来随机指定,其中需要有随机数的上下界。

上面第三行是多维矩阵的建立,先是一个多维矩阵每个维度上的大小,CV_8UC(1) 就是指定多少通道,因为默认只有1 2 3 4 ,所以其他维度就得自己指定(这里也算是多此一举,因为可以使用CV_8UC1 代替的)。

3、Matlab类型的初始化

Mat M;    M.create(4,4, CV_8UC(2));
    cout << "M = "<< endl << " "  << M << endl << endl;
    Mat E = Mat::eye(4, 4, CV_64F);    

    Mat O = Mat::ones(2, 2, CV_32F);    

    Mat Z = Mat::zeros(3,3, CV_8UC1);

上面第一行是一个创建函数,为了开辟一个指定大小的数据空间,其中的值由205初始化,通道数影响的是每一行的长度,比如这里实际开辟的是4×8的矩阵大小。然后随之直接输出结果,记得这里只有二维矩阵才有这待遇使用cout<<;

接下来的三行就是因为Matlab所属公司和OpenCV小组合作的结果,这样才有这么便捷的初始化(个人:这里的理解在cpp语法上应该是先创建一个临时类对象,然后进行eye等的初始化和数据空间的创建,然后在将临时类对象赋值给矩阵E、 O、 Z,具体的可以看《C++程序设计语言 》或者《C++primer》中的有关章节)。关于CV_64F的定义可以查看<types_c.h>中569-576行。

3.2 Mat类对象的访问方法

这里要说下在OpenCV中的数据表示方法,如下图所示,是每一行中长度也就是矩阵的列数是由 (点×通道数)  决定,而行数是不变的,而且是BGR排序,不是RGB排序,这个排序的顺序,完全是最初的大牛制定的时候卖萌罢了。即:

Mat I = imread("your picture",CV_LOAD_IMAGE_COLOR);
int nCols = I.cols*I.channels();
int  nRows =I .rows;

方法1:c分割的指针访问

按照上面的代码接着写

 if (I.isContinuous())
{
nCols *= nRows;
nRows = 1;
}

这里的isContinuous()是为了识别该系统的编译器是否将这个矩阵的数据行与行之间连接的存储,还是分开的存储,如果是连接的存储,其实也就是个n×1的矩阵,可以直接顺序访问。

    uchar* p;
for(size_t i = 0; i < nRows; ++i)
{
p = I.ptr<uchar>(i);
for (size_t j = 0; j < nCols; ++j)
{
p[j] = /*your operation*/;
}
}

通过两个循环,如果是连续的,那么第一个for只执行一回,如果不是,那么按照正常的访问过程。I.ptr<uchar>(i)是访问矩阵 I 的 第 i 行开始的地址。

template<typename _Tp> inline _Tp* Mat::ptr(int y)

{

    CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) );

    return (_Tp*)(data + step.p[0]*y);

}

上面的是在<mat.hpp>中的代码段,可以看出,这个模板就是返回data(data就是矩阵的第一行第一列的地址)加上每行的步长长度乘以第几行。

 方法2:迭代器的方式访问

 const int channels = I.channels();
switch(channels)
{
case 1:
{
MatIterator_<uchar> it, end;
for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)
*it = /*your operation*/;
break;
}
case 3:
{
MatIterator_<Vec3b> it, end;
for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it)
{
(*it)[0] = /*your operation*/;
(*it)[1] = /*your operation*/;
(*it)[2] = /*your operation*/;
}
}
}

分单通道和三通道两部分:MatIterator_<uchar>it,end。就是Mat的迭代器的声明,其中

uchar:typedef unsigned char uchar;

Vec3b:typedef  Vec<uchar, 3> Vec3b;

Vec:template<typename _Tp, int cn> class Vec;

方法3:at()函数访问

switch(channels)
{
case 1:
{
for( size_t i = 0; i < nRows; ++i)
for( size_t j = 0; j < nCols; ++j )
I.at<uchar>(i,j) = /*your operation*/;
break;
}
case 3:
{
Mat_<Vec3b> _I = I;
         for( size_t i = 0; i < nRows; ++i){
for( size_t j = 0; j < nCols; ++j ) {
                                     _I(i,j)[0] = /*your operation*/;    
                    _I(i,j)[1] = /*your operation*/;
_I(i,j)[2] = /*your operation*/;  
                     }    
                     I = _I;        
                    break;       
       }

单通道的时候,采用at<uchar>()函数,进行访问矩阵 I 的对应位置;

而三通道的时候,是先建立个矩阵_I,当然,这里还是创建新的矩阵头,指向的还是同一片数据区域。只是这个矩阵头中,将每个点的三个通道放在一起,作为一个一维的3元素的数组,所以可以很清楚的访问每个像素点的位置。如果是想还是使用at()函数,那么就I.at<uchar>(i,(j-1)*nChannels +nChannels);来访问,记得多通道的图像是列数上增加的,行数不变。

opencv2-新特性及Mat的更多相关文章

  1. Atitit opencv3.0  3.1 3.2 新特性attilax总结

    Atitit opencv3.0  3.1 3.2 新特性attilax总结 1. 3.0OpenCV 3 的改动在哪?1 1.1. 模块构成该看哪些模块?2 2. 3.1新特性 2015-12-21 ...

  2. Atitit opencv版本新特性attilax总结

    Atitit opencv版本新特性attilax总结 1.1. :OpenCV 3.0 发布,史上功能最全,速度最快的版1 1.2. 应用领域2 1.3. OPENCV2.4.3改进 2.4.2就有 ...

  3. C++11新特性——初始化列表 initializer_list

    破事水: 由于最近数据结构有个实验报告说是要对字符串进行排序,想偷个懒不想一个一个地赋值,虽然可以用strcpy和传入二级指针的形式直接写,但是这样感觉不美观漂亮. 然后就去膜了一下C++11的新特性 ...

  4. Matlab 2018b 新特性

    新特性简要介绍 一.实时编辑器 所创建的脚本不仅可以捕获代码,还可以讲述与人分享的故事.自动化的上下文提示可让您在编程时快速推进,并且将结果与可视化内容和您的代码一起显示. 二.App Designe ...

  5. SQL Server 2014 新特性——内存数据库

    SQL Server 2014 新特性——内存数据库 目录 SQL Server 2014 新特性——内存数据库 简介: 设计目的和原因: 专业名词 In-Memory OLTP不同之处 内存优化表 ...

  6. ElasticSearch 5学习(10)——结构化查询(包括新特性)

    之前我们所有的查询都属于命令行查询,但是不利于复杂的查询,而且一般在项目开发中不使用命令行查询方式,只有在调试测试时使用简单命令行查询,但是,如果想要善用搜索,我们必须使用请求体查询(request ...

  7. [干货来袭]C#6.0新特性

    微软昨天发布了新的VS 2015 ..随之而来的还有很多很多东西... .NET新版本 ASP.NET新版本...等等..太多..实在没消化.. 分享一下也是昨天发布的新的C#6.0的部分新特性吧.. ...

  8. CSS3新特性应用之结构与布局

    一.自适应内部元素 利用width的新特性min-content实现 width新特性值介绍: fill-available,自动填充盒子模型中剩余的宽度,包含margin.padding.borde ...

  9. 【译】Meteor 新手教程:在排行榜上添加新特性

    原文:http://danneu.com/posts/6-meteor-tutorial-for-fellow-noobs-adding-features-to-the-leaderboard-dem ...

  10. 跨平台的 .NET 运行环境 Mono 3.2 新特性

    Mono 3.2 发布了,对 Mono 3.0 和 2.10 版本的支持不再继续,而且这两个分支也不再提供 bug 修复更新. Mono 3.2 主要新特性: LLVM 更新到 3.2 版本,带来更多 ...

随机推荐

  1. SQL优化技巧--远程连接对象引起的CTE性能问题

    背景 最近SSIS的开发过程中遇到几个问题.其中使用CTE时,遇到一个远程连接对象,结果导致严重的性能问题,为了应急我就修改了代码. 之前我写了一篇介绍CTE的随笔包含了CTE的用法等: http:/ ...

  2. 在JavaScript和C#中获得referer

    1. JavaScript /** * 获取HTTP请求的Referer * @ishost 布尔类型 Referer为空时是否返回Host(网站首页地址) */ function get_http_ ...

  3. mysql-2 mysql客户端

    mysql 官方客户端  MySQL-Workbench 下载链接http://dev.mysql.com/downloads/workbench/ 具体安装步骤就不写了,直接一直下一步就可以了. 下 ...

  4. Silverlight 调用自托管的wcf 报跨域异常的处理

    Sileverlight很多时候需要通过wcf和后台,程序进行交互.如果 iis was托管还好,极端的遇到自托管的程序,console,windowsservice,winform,wpf等,就会出 ...

  5. 使用For XML PATH 会影响Cross Apply 返回

    昨天在写语句的时候,遇到了一个现象,其实就是使用 Cross Apply做一个拼接字符串的而已.比如 CREATE TABLE GoodsCatalog (ID INT, Name )) CREATE ...

  6. SQL Server调优系列基础篇

    前言 关于SQL Server调优系列是一个庞大的内容体系,非一言两语能够分析清楚,本篇先就在SQL 调优中所最常用的查询计划进行解析,力图做好基础的掌握,夯实基本功!而后再谈谈整体的语句调优. 通过 ...

  7. centos yum源配置

    5步搞定yum源配置 作者小波/QQ463431476欢迎转载! 第一步: 卸载原来的yum [root@localhost home]#rpm -qa|grep yum|xargs rpm -e - ...

  8. 大话设计模式C++版——装饰模式

    女人常说男人喜新厌旧,只见新人笑,那闻旧人哭,但装饰模式(Decorator)却是一种结交新朋友不忘老朋友的设计模式,非常适合去古代当老公(现代是不行的,因为只能娶一个老婆了).装饰模式的本质是每一个 ...

  9. [转]backbone.js template()函数

    本文转自:http://book.2cto.com/201406/43974.html 本文所属图书 > Backbone.js实战 资深Web开发专家根据Backbone js最新版本撰写,对 ...

  10. 计算2的N次方&&计算e

    2的N次方 注意:这里在处理的时候并没有用循环来处理,而是用移位的做法.    n<<4  就是 n*2^4    ,所以在本例中只需要写 1<<time  (time是要求的 ...