[转]opencv2-新特性及Mat

opencv2-新特性及Mat

转:http://www.cnblogs.com/shouhuxianjian/p/4529192.html

本文参照《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);来访问,记得多通道的图像是列数上增加的,行数不变。

时间: 2024-10-13 15:38:14

[转]opencv2-新特性及Mat的相关文章

web新特性 之 WebSocket

详情参见:你真的了解WebSocket吗?     WebSocket系列教程   HTML5新特性之WebSocket WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全双工(full-duplex)通信.其本质是保持TCP连接,在浏览器和服务端通过Socket进行通信. 服务端与客户端的连接不断开,实现全双工的操作.及服务端或是客户端都会给对方发送消息. WebSocke

Java精品高级课,架构课,java8新特性,P2P金融项目,程序设计,功能设计,数据库设计,第三方支付,web安全,视频教程

36套精品Java架构师,高并发,高性能,高可用,分布式,集群,电商,缓存,性能调优,设计模式,项目实战,P2P金融项目,大型分布式电商实战视频教程 视频课程包含: 高级Java架构师包含:Spring boot.Spring  cloud.Dubbo.Elasticsearch,Redis.ActiveMQ.Nginx.Mycat.Spring.MongoDB.ZeroMQ.Git.Nosql.Jvm.Mecached.Netty.Nio.Mina.java8新特性,P2P金融项目,程序设计,

Atitit js es5 es6新特性 attilax总结

1.1. JavaScript发展时间轴:1 1.2. 以下是ES6排名前十的最佳特性列表(排名不分先后):1 1.3. Es6 支持情况 基本chrome ff 360se8全面支持了2 2. ECMAScript 2015(ES6)的十大特征 – WEB前端开发 - 专注前端开发,关注用户体验.html2 1.1. JavaScript发展时间轴: 1.1995:JavaScript诞生,它的初始名叫LiveScript. 2.1997:ECMAScript标准确立. 3.1999:ES3出

CSS3的新特性 行内盒子before和after

CSS3的新特性 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>before after</title> 6 <style type="text/css"> 7 /*在DIV1盒子内部前面*/ 8 #div1:before{ 9 width: 100p

Xcode And iOS9新特性

Xcode And iOS9 1. Xcode7 新特性 > 可直接在真机上运行自己的应用,只需要有苹果账号,无需购买苹果开发者账号. > 可设置在出现 EXC_BAD_ACCESS 错误时,显示更详细的错误信息. 设置方式:XCode->Product->Scheme->Edit Scheme 左侧选择“Run” 右侧选择“Diagnostics(诊断)” 在“Runtime Sanitization(运行时净化处理)” 勾选“Enable Address Sanitize

Atitit.业务系统的新特性&#160;开发平台&#160;新特性的来源总结

Atitit.业务系统的新特性 开发平台 新特性的来源总结 1.1. 语言新特性(java c# php js python lisp c++ oc swift ruby  go dart1 1.2. 流行lib.frmawork  新特性 jdk clr framework 新特性 (jq   Servlet2 1.3. Ide 新特性( eclipse vs netbea jetbrain2 1.4. Vm  新特性 clr  jvm(jvm好像没有独立的版本号,继承在jdk里面2 1.5.

Atitit.mysql&#160;5.0&#160;5.5&#160;&#160;5.6&#160;5.7&#160;&#160;新特性&#160;新功能

Atitit.mysql 5.0 5.5  5.6 5.7  新特性 新功能 1. MySQL  5.6    5 大新特性1 1.1. 优化器的改进1 1.2. InnoDB 改进1 1.3. 使用 memcached API 直接访问 NoSQL2 1.4. 更好的复制2 1.5. Performance Schema2 2. MySQL 5.7.62 2.1. 内建中文全文索引2 2.2. 多主复制2 2.3. other2 3. 参考2 1. MySQL  5.6    5 大新特性 M

TypeScript系列1-1.5版本新特性

1. 简介 随着PC端快速向移动端迁移,移动(体验)优先的概念也越来越响.由于ReactJS目前移动端仅仅支持iOS,因此移动端Web开发框架只能选择: AngularJS/Angula2 + Ionic框架 + Cordova.想要学习好Angula2以及阅读其代码, 就必须了解和学习TypeScript,也因此需要学习好ES6以及Web Component.近期将开始学习TypeScript语言. 下面先看看TypeScript语言的发展: 鉴于JavaScript这种脚本语言很难应用于大规

jdk8新特性

jAVA8 十大新特性 投稿:junjie 字体:[增加 减小] 类型:转载 本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API上的改进,比如流,函数式接口,Map以及全新的日期API "Java is still not dead-and people are starting to figure that out." 本教程将用带注释的简单代码来描述新特性,你将