Opencv系列1.4--图像和大型数据类型

更多更详细的文章请关注微信公众号:SLAM之路

大型数组类型中最为重要的是,cv::Mat类,是整个Opencv C++库的核心;可用于表示任意维度的稠密数组,大多数图像都是采用稠密数组形式,即数组每一项都存储有数值;此外,还有一种cv::SparseMat类存储稀疏矩阵,即仅存储非零项,可节约存储空间,例如直方图;

cv::Mat中数据以一种类似n维光栅扫描的方式存储,即一维数组按照顺序排列;二维数组中,数据组织为行结构,一行紧跟一行;三维数组中,每个平面均按照行接行填充,然后一个平面接一个平面;

每个矩阵都包含:

flags:表明数组内容;

dims:表明维度;

rows:行数;

cols:列数;

data:   指向数组存储位置的指针;

refcount:参考数量;使cv::Mat表现项智能指针;

创建数组

下表列出cv::Mat基本构造器,除了默认构造器外,基本可分为三种:一种是用行数和列数创建二维数组,一种是用cv::Size对象创造二维数组,一种是创建n维数组,要求指定维数和每个维度中的维数

//用cv::Mat直接实例化,此时数组无大小无数据类型    cv::Mat m;
    //可用create()分配数据,行数3,列数10,数据类型三通道32位单精度,表示二维对象    m.create(3,10,CV_8UC1);    //数组类型决定其元素类型,有效的数组类型需确定元素种类和通道数    //所有类型已通过头文件预定义CV_{8U,16S,16U,32F,32S,64F}C{1,2,3}    //例如CV_32FC3,表示32为单精度三通道数组;
    //设定第一通道的值1.0,第二通道0.0,第三通道1.0    m.setTo(cv::Scalar(1.0f,0.0f,1.0f));
    //等同于cv::Mat m(3,10,CV_32FC3,cv::Scalar(1.0f,0.0f,1.0f));

通过从另一个cv::Mat复制数据构造,通过行列感兴趣范围构建,或通过cv::Rect指定感兴趣范围构建;

   //cv::Mat(const Mat& mat)   //复制构造数组   const cv::Mat img=cv::imread("a.jpg");   cv::imshow("Original Img",img);   cv::waitKey(0);   cv::Mat img_copy(img);  //复制构造   cv::imshow("copy img",img_copy);   cv::waitKey(0);

 

  //cv::Mat(const Mat& mat, const cv::Range rows, cv::Range  cols);  //获取数组的子数组
   const cv::Mat img=cv::imread("a.jpg");   cv::imshow("Original Img",img);   cv::waitKey(0);
   //利用cv::Range构造数组   int height=img.cols;   int width = img.rows;   std::cout<<"cols"<<height<<"rows"<<width;
   const cv::Range rows=cv::Range(100,800);   const cv::Range cols=cv::Range(100,800);   cv::Mat img_sub(img, cols, rows);   cv::imshow("Sub Img",img_sub);   cv::waitKey(0);      //利用Rect构造数组   cv::Rect rec1=cv::Rect(400,400,600,600);   cv::Mat img_rect(img,rec1);;   cv::imshow("Rect Img",img_rect);   cv::waitKey(0);

静态成员构造器,全是零,全是1,对角阵;

    cv::Mat m(cv::Mat::zeros(5,5,CV_8UC1));    std::cout<<"Matrix zeros:"<<std::endl<<m<<std::endl;
    cv::Mat m1(cv::Mat::ones(4,6,CV_8UC1));    std::cout<<"Matrix ones:"<<std::endl<<m1<<std::endl;
    cv::Mat m2(cv::Mat::eye(4,6,CV_8UC1));    std::cout<<"Matrix eye:"<<std::endl<<m2<<std::endl;

 

访问数组

访问单个元素的两个主要方法是通过位置或遍历;

直接访问的方法是根据成员函数,at<数据类型>(位置)

    cv::Mat m2(cv::Mat::eye(4,6,CV_32FC1));    std::cout<<"Matrix eye:"<<std::endl<<m2<<std::endl;
    std::cout<<m2.at<float>(3,3);    //at<数据类型>(位置)

对于多通道数组,方法是类似的:多通道时建议使用cv::Vec<>

    cv::Mat img=cv::imread("a.jpg",1);    std::cout<<"img channels: "<<img.channels()<<std::endl;    std::cout<<"value is: "<<img.at<cv::Vec3b>(10,10)[0];    std::cout<<"value is: "<<img.at<cv::Vec3b>(10,10)[1];    std::cout<<"value is: "<<img.at<cv::Vec3b>(10,10)[2];

 

通过指针函数ptr<>()进行访问,速度最快:

为访问二维数组,我们可以提取一个指向数组具体行的指针,ptr<>()就是cv::Mat中的模板函数。ptr<>()用一个类型名进行实例化,整数参数表示你想访问并且指针指向的行数,函数返回一个指针,指向所建数组中元素类型,也就是说,如果数组类型是CV_32FC3,那么返回值是类型float*。因此,若给定一个三通道矩阵mtx,元素类型float,构造mtx.ptr<Vec3f>(3)返回一个指针,指向矩阵mtx第三行中第一个元素的第一个通道,这是访问数组最快的方式。

如果可以判断数组是以连续行打包,则可以入一维数组那样逐行连续访问,isContinuous()函数判断。

通过迭代进行顺序访问:

Opencv提供迭代模板,分为针对const和非const,cv::MatIterator<>和cv::MatConstIterator<>。cv::Mat的方法begin()和end()可返回这种类型的对象。这种迭代方法非常方便,因为迭代器非常智能,足以处理连续打包和非连续打包情况和任意维度的数组。

每个迭代器必须被宣告和指定属于哪个数组的迭代器对象,下面是简单例子,用于计算三维数组三通道元素中最长的元素:

  int sz[3]={4,4,4};  cv::Mat m(3,sz,CV_32FC3);  cv::randu(m,-1.0f,1.0f);  float max=0.0f;  cv::MatConstIterator<cv::Vec3f> it=m.begin();
  while (it!=m.end()){      len2=(*it)[0]*(*it)[0]+(*it)[1]*(*it)[1]+(*it)[2]*(*it)[2];      if(len2>max)max=len2;      it++;  }

按块访问数组元素

需要注意的是,当我调用这些函数时,数组中的数据并未复制到新数组。

 

 

矩阵表达:代数和cv::Mat

 

 

溢出转换

Opencv中,进行操作时可能出现溢出等情况,尤其是无符号类型数据进行减法时,因此构建cv::saturate_cast<>()。例如:

uchar& Vxy = m0.at<uchar>(y,x);

Vxy = cv::saturate_cast<uchar>((Vxy-128)*2+128);

在该例子中,我们首先分配一个引用Vxy直向一个8位数组m0中的元素。当我们从这个数组减去128,然后乘以2,在增加128时,C算法规则会对Vxy-128分配32位有符号整数,然后进行乘法和加法。如果Vxy的原始值是10,那么最后结果是-108,将不满足Vxy 8位无符号数据类型,因此必须使用cv::saturate_casts<uchar>(),当低于无符号数据下限时,变为0。

其他数组可做的事情

 

 

稀疏数组:cv::SparseMat

通常用于0项远多于非零项的数组,一般出现在线性代数的稀疏矩阵或者直方图。稀疏数组仅存储那些存在的数据,所以节省空间,实际中稀疏对象太大很难以稠密数组表示;稀疏表示方法的缺点是,计算太慢。

cv::SparseMat类似于cv::Mat,它采用哈希表方式存储数据。

访问稀疏数组元素

稀疏数组和稠密数组最大的区别在于,如何访问元素。

稀疏数组提供四种访问方法:cv::SparseMat::ptr(),cv::SparseMat::ref(),cv::SparseMat::value(),cv::SparseMat::find();

 

稀疏数组独有函数

 

大型数组类型的模板结构

cv::SparseMat_<>和cv::Mat_<>,通过模板类则不需要使用他们成员的模板;

例如,定义矩阵cv::Mat m(10,10,CV_32FC2);则访问每个元素都需指定矩阵的类型,m.at<Vec2f>(i0,i1)=cv::Vec2f(x,y);

如果用模板类定义m,则不需要具体指明类型使用at(),

cv::Mat_<Vec2f>  m(10,10);

m.at(i0,i1)=cv::Vec2f(x,y);

这种模板结构简化了代码;

以上两种方法,效率上相同,但是更建议第二种方法;

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值