七.opencv(七):Mat_类

Mat_类是对 Mat 类的一个包装,其定义如下:

template<typename _Tp> 

class Mat_ : public Mat 

{

public:     

//只定义了几个方法     

//没有定义新的属性 

};

 

这是一个非常轻量级的包装,既然已经有 Mat 类,为何还要定义一个 Mat_? 下面我们看这段代码:

Mat M(600, 800, CV_8UC1); 
for( int i = 0; i < M.rows; ++i) 
{     
    uchar * p = M.ptr<uchar>(i);
    for( int j = 0; j < M.cols; ++j )     
    {
        double d1 = (double) ((i+j)%255);         
        M.at<uchar>(i,j) = d1;         
        double d2 = M.at<double>(i,j);//此行有错     
    } 
}

        在读取矩阵元素时,以及获取矩阵某行的地址时,需要指定数据类型。这样 首先需要不停地写“<uchar>”,让人感觉很繁琐,在繁琐和烦躁中容易犯错,如上面代码中的错误,用 at()获取矩阵元素时错误的使用了 double 类型。这种错误 不是语法错误,因此在编译时编译器不会提醒。在程序运行时,at()函数获取到 的不是期望的(i,j)位置处的元素,数据已经越界但是运行时也未必会报错。这样 的错误使得你的程序忽而看上去正常,忽而弹出“段错误”,特别是在代码规模很大时,难以查错。

        如果使用 Mat_类,那么就可以在变量声明时确定元素的类型,访问元素时 不再需要指定元素类型,即使得代码简洁,又减少了出错的可能性。

#include<iostream>
#include<math.h>
#include<opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc, char* argv[]){
       Mat M(600, 800, CV_8UC1);
       // 用指针输入pixel
       for (int i = 0; i < M.rows; i++){
              uchar* p = M.ptr<uchar>(i); //M.ptr是指向第i行开头DIY的指针
              for (int j = 0; j < M.cols; j++){
                     p[j] = (i + j) % 255;
              }
       }
       // 通过at()函数输入pixels
       for (int i = 0; i < M.rows; i++){
              uchar* p = M.ptr<uchar>(i); //M.ptr是指向第i行开头DIY的指针
              for (int j = 0; j < M.cols; j++){
                     // 用at()读写像素,需要指定类型,很麻烦,很容易出错
                     M.at<uchar>(i, j) = ((i + j) % 255);                    
              }
       }
       // Mat_ 类管理
       // create 一个Mat_类对象,将原来Mat类对象的 M 强制类型转换成Mat_<uchar>&,
       // 这样就可以完成关联、赋值
       Mat_<uchar> M1 = (Mat_<uchar>&) M;
       for (int i = 0; i < M1.rows; i++){
              // 不需指定元素类型,语句简洁
              uchar* p = M1.ptr(i);
              for (int j = 0; j < M1.cols; j++){
                     double d1 = (double)((i + j) % 255);
                     // MATLAB风格
                     M1(i, j) = d1;
                     double d2 = M1(i, j);
              }
       }
       imshow("name", M1);
       waitKey();
       return 0;
}

八.opencv(八):Mat类 内存管理

        使用 Mat 类,内存管理变得简单,不再像使用 IplImage 那样需要自己申请 和释放内存。虽然不了解 Mat 的内存管理机制,也无碍于 Mat 类的使用,但是 如果清楚了解 Mat 的内存管理,会更清楚一些函数到底操作了哪些数据。

        Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法, 存储地址等信息)和一个指向存储所有像素值的矩阵的指针,如图 3.9 所示。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头 的尺寸大数个数量级。复制矩阵数据往往花费较多时间,因此除非有必要,不要 复制大的矩阵。

        为了解决矩阵数据的传递,OpenCV 使用了引用计数机制。其思路是让每个 Mat 对象有自己的矩阵头信息,但 多个 Mat 对象可以共享同一个矩阵数据。让矩 阵指针指向同一地址而实现这一目的。很多函数以及很多操作(如函数参数传值) 只复制矩阵头信息,而不复制矩阵数据。

        前面提到过,有很多中方法创建 Mat 类。如果 Mat 类自己申请数据空间, 那么该类会多申请 4 个字节,多出的 4 个字节存储数据被引用的次数。引用次数 存储于数据空间的后面,refcount 指向这个位置,如图 3.9 所示。当计数等于 0 时,则释放该空间。

c++,opencv3 基础核心整理3

 

关于多个矩阵对象共享同一矩阵数据,我们可以看这个例子:

Mat A(100,100, CV_8UC1);
Mat B = A;
Mat C = A(Rect(50,50,30,30));

上面代码中有三个 Mat 对象,分别是 A, B 和 C。这三者共有同一矩阵数据, 其示意图如图 3.10 所示

 

 

c++,opencv3 基础核心整理3

 

九.opencv(九):关于Mat的更多细节

 马上学完了,加油了。

        从前面的例程中,可以看到 Mat 类重载了<<操作符,可以方便得使用流操作来输出矩阵的内容。默认情况下输出的格式是类似 Matlab 中矩阵的输出格式。除了默认格式,Mat 也支持其他的输出格式。

code:

Mat R = Mat(3, 2, CV_8UC3); 
randu(R, Scalar::all(0), Scalar::all(255));
// 默认格式输出的代码如下:
cout << "R (default) = " << endl << R << endl << endl;

之前一直想,如果有RGB三个通道,Mat会如何进行显示。现在发现了问题的答案。(全部显示的代码放在文末)

 

1.默认格式

Default print:

[ 91,   2,  79, 179,  52, 205;

236,   8, 181, 239,  26, 248;

207, 218,  45, 183, 158, 101]

 

2.Python 格式

Python print:

[[[ 91,   2,  79], [179,  52, 205]],

[[236,   8, 181], [239,  26, 248]],

[[207, 218,  45], [183, 158, 101]]]

3.Numpy 格式

Numpy print:

array([[[ 91,   2,  79], [179,  52, 205]],

       [[236,   8, 181], [239,  26, 248]],

       [[207, 218,  45], [183, 158, 101]]], dtype='uint8')

 

#include<iostream>
#include<opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main(int argc, char* argv[]){
       Mat R(3, 2, CV_8UC3);
       // 这个Scalar::all最多支持4-d标量赋值,pixels经常拿他赋值
       /*randu
       Generates a single uniformly-distributed random number or an array of  random numbers.
       Non-template variant of the function fills the matrix dst with  uniformly-distributed
       random numbers from the specified range:
       @ param dst output array of random numbers; the array must be  pre-allocated.
       @ param low,  inclusive lower boundary of the generated random numbers.
       @ param high, exclusive upper boundary of the generated random numbers.
       @sa RNG, randn, theRNG*/
       randu(R, Scalar::all(0), Scalar::all(255));
       cout << "Default print:" << endl << R << endl;
       
             //版本问题,可能是你的是format(R,"python")
       cout << "Python print:" << endl << format(R, Formatter::FMT_PYTHON) <<  endl;
       cout << "Numpy print:" << endl << format(R, Formatter::FMT_NUMPY) << endl;
       return 0;
}

 

相关文章: