(OpenCV — 3)基础图像容器Mat

Mat类

Mat 是一个类 , 由两个数据部分组成 :

  • 矩阵头(包含矩阵尺寸 、存储方法 、 存储地址等信息)
  • 一个指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不 同的维数)的指针 。

矩阵头的尺寸是常数值 , 但矩阵本身的尺寸会依图像的不同而不同 , 通常比矩阵头的尺寸大数个数量级 。 因此,
当在程序中传递图像并创建副本时,大的开销是由矩阵造成的,而不是信息头 。

当进行大图像的复制时 , 会降低程序的运行速度。
为了解决此问题 , OpenCV使用了引用计数机制 。 其思路是让每个 Mat 对象有自己的信息头,但共享同一个矩阵 。 这通过让矩阵指针指向同一地址而实现 。而拷贝构造函数则只复制信息头和矩阵指针 , 而不复制矩阵 。

如下面的代码:

Mat A , C ; // 仅创建信息头部分
A = imread("l.jpg" , CV_ LOAD_IMAGE_COLOR); // 这里为矩阵开辟内存
Mat B(A) ; //使用拷贝构造函数
C = A; //赋值运算符

以上代码中的所有 Mat 对象最终都指向同一个也是唯一一个数据矩阵 。 虽然它们的信息头不同,但通过任何一个对象所做的改变也会影响其他对象 。 实际上 ,不同的对象只是访问相同数据的不同途径而已 。

这里还要提及一个比较棒的功能 :
我们可以创建只引用部分数据的信息头。 比如想要创建一个感兴趣区域 ( ROL ) ,只需要创建包含边界信息的信息头:

Mat D(A, Rect(lO , 10 , 100 , 100) ) ; //使用矩形界定
Mat E = A(Range:all() , Range(l,3)) ; //用行和列来界定

如果矩阵属于多个 Mat 对象,那么 当不再需要它时,谁来负责清理呢?

最后一个使用它的对象 。 通过引用计数机制来实现。我们无论什么时候复制一个 Mat 对象的信息头,都会增加矩阵的引用次数 。 反之,当一个头被释放之后 , 这个计数被减一 ; 当计数值为零 , 矩阵会被消灭。但某些时候你仍会想复制矩阵本身(不只是信息头和矩阵指针 ) , 这时可以使用函数 clone() 或者 copyTo() 。

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

现在改变 F 或者 G 就不会影响 Mat 信息头所指向的矩阵。

小结:
• OpenCV 函数中输出图像的内存分配是自动完成的 (如果不特别指定的话) 。
• 使用 OpenCV 的 C++接口时不需要考虑内存释放问题 。
• 赋值运算符和拷贝构造函数 ( 构造函数)只复制信息头。
• 使用函数 clone()或者 copyTo()来复制一幅图像的矩阵 。

像素值的存储方法

颜色系统有很多,它们各有优势,具体如下 。

  • RGB 是最常见的 ,这是因为人哏采用相似的工作机制 , 它也被显示设备所采用
  • HSY 和 HLS 把颜色分解成色调、饱和度和亮度/明度 。 这是描述颜色更自然的方式 ,比如可以通过抛弃最后一个元素,使算法对输入图像的光照条件不敏感
  • YCrCb 在 JPEG 图像格式中 广泛使用
  • ClE L*a*b*是种在感知上均匀的颜色空间 , 它适合用来度量两个颜色之间的距离

显式创建 Mat 对象的五种方法

1 使用 Mat()构造函数

    Mat M( 3 , 3 , CV_8UC3, Scalar(0 , 0 , 255 ));
    cout << "M = " << endl << " " << M << endl << endl;

运行结果:

M = 
 [  0,   0, 255,   0,   0, 255,   0,   0, 255;
    0,   0, 255,   0,   0, 255,   0,   0, 255;
    0,   0, 255,   0,   0, 255,   0,   0, 255]

对于二维多通道图像,首先要定义其尺寸,即行数和列数 。 然后 , 需要指定存储元素的数据类型以及每个矩阵点的通道数 。 为此,依据下面的规则有多种定义:

CV_[The number of bits per item] [Signed or Unsigned ) [Type Prefix ]C[The channel number)
即 :
CV_[位数][带符号与否][类型前缀]  C  [通道数]如:CV_8UC3

 

2 利用 Create()函数

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

运行结果:

M = 
 [  0,   0,   0,   0,   0,   0,   0,   0;
    0,   0,   0,   0,   0,   0,   0,   0;
    0,   0,   0,   0,   0,   0,   0,   0;
    0,   0,   0,   0,   0,   0,   0,   0]

需要注意的是 ,此创建方法不能为矩阵设初值,只 是在改变尺寸时重新为矩阵数据开辟内存而已。

 

3 采用 Matlab 式的初始化方式

采用 Matlab 形式的初始化方式 : zeros(), ones(), eyes() 。 使用以下方式指定尺寸和数据类型 :

    Mat E = Mat::eye(4 , 4, CV_64F);
    cout << " E = " << endl << " " << E << endl << endl ;

    Mat O = Mat::ones(2 , 2 , CV_32F);
    cout << "O = " << endl << " " << O << endl << endl ;

    Mat Z = Mat::zeros (3 , 3 , CV_8UC1);
    cout << " Z = " << endl << " " << Z << endl << endl;

运行结果:

 E = 
 [1, 0, 0, 0;
 0, 1, 0, 0;
 0, 0, 1, 0;
 0, 0, 0, 1]

O = 
 [1, 1;
 1, 1]

 Z = 
 [  0,   0,   0;
    0,   0,   0;
    0,   0,   0]

4 对小矩阵使用逗号分隔式初始化函数

Mat C = (Mat_<double> (3 , 3) << 0 , -1 , 0 , -1, 5 , -1, 0 , -1 , 0) ;
cout << " C = " << endl << " " << C << endl << endl ;

运行结果:

 C = 
 [0, -1, 0;
 -1, 5, -1;
 0, -1, 0]

 

5 为已存在的对象创建新信息头

使用成员函数 clone()或者 copyTo()为一个已存在的 Mat 对象创建一个新的信息头,示范代码如下 。

Mat C = (Mat_<double> (3 , 3) << 0 , -1 , 0 , -1, 5 , -1, 0 , -1 , 0) ;
cout << " C = " << endl << " " << C << endl << endl ;

Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl ;

运行结果:

 C = 
 [0, -1, 0;
 -1, 5, -1;
  0, -1, 0]

RowClone = 
 [-1, 5, -1]

OpenCV 中的格式化输出方法

1. 【风格一 】 OpenCV 默认风格
风格一为 OpenCV 默认风格的输出 方法 , 如下。

    Mat M = Mat (10, 3, CV_8UC3) ;
    randu(M , Scalar::all (0) , Scalar::all(255));//生成随机数

    cout << "M = " << endl << " " << M << endl << endl;

运行结果:

M = 
 [ 91,   2,  79, 179,  52, 205, 236,   8, 181;
 239,  26, 248, 207, 218,  45, 183, 158, 101;
 102,  18, 118,  68, 210, 139, 198, 207, 211;
 181, 162, 197, 191, 196,  40,   7, 243, 230;
  45,   6,  48, 173, 242, 125, 175,  90,  63;
  90,  22, 112, 221, 167, 224, 113, 208, 123;
 214,  35, 229,   6, 143, 138,  98,  81, 118;
 187, 167, 140, 218, 178,  23,  43, 133, 154;
 150,  76, 101,   8,  38, 238,  84,  47,   7;
 117, 246, 163, 237,  69, 129,  60, 101,  41]

2 . 【风格二 】 Python 风格

    Mat M = Mat (10, 3, CV_8UC3) ;
    randu(M , Scalar::all (0) , Scalar::all(255));//生成随机数

    cout << "M = " << format(M , Formatter::FMT_PYTHON) << ";" << endl << endl ;

运行结果:

M = [[[ 91,   2,  79], [179,  52, 205], [236,   8, 181]],
 [[239,  26, 248], [207, 218,  45], [183, 158, 101]],
 [[102,  18, 118], [ 68, 210, 139], [198, 207, 211]],
 [[181, 162, 197], [191, 196,  40], [  7, 243, 230]],
 [[ 45,   6,  48], [173, 242, 125], [175,  90,  63]],
 [[ 90,  22, 112], [221, 167, 224], [113, 208, 123]],
 [[214,  35, 229], [  6, 143, 138], [ 98,  81, 118]],
 [[187, 167, 140], [218, 178,  23], [ 43, 133, 154]],
 [[150,  76, 101], [  8,  38, 238], [ 84,  47,   7]],
 [[117, 246, 163], [237,  69, 129], [ 60, 101,  41]]];

3. 【 风格三 】 C 语言风格

    Mat M = Mat (10, 3, CV_8UC3) ;
    randu(M , Scalar::all (0) , Scalar::all(255));

    cout << "M = " << format(M , Formatter::FMT_C) << ";" << endl << endl ;

运行结果:

M = { 91,   2,  79, 179,  52, 205, 236,   8, 181,
 239,  26, 248, 207, 218,  45, 183, 158, 101,
 102,  18, 118,  68, 210, 139, 198, 207, 211,
 181, 162, 197, 191, 196,  40,   7, 243, 230,
  45,   6,  48, 173, 242, 125, 175,  90,  63,
  90,  22, 112, 221, 167, 224, 113, 208, 123,
 214,  35, 229,   6, 143, 138,  98,  81, 118,
 187, 167, 140, 218, 178,  23,  43, 133, 154,
 150,  76, 101,   8,  38, 238,  84,  47,   7,
 117, 246, 163, 237,  69, 129,  60, 101,  41};

输出其他常用数据结构

之前我们讲解了 如何输出 Mat 类型,其实, OpenCV 同样支持使用运算符" << "来打印其他常用的 OpenCV 数据结构。

1. 定义和输出二维点
首先看看二维点的定义和输出方法 :

Point2f p(6 , 2 );
cout << "[二维点] p = " << p << ";" << endl ;

运行结果:

[二维点] p = [6, 2];

2. 定义和输出三维点

Point3f p3f (8 , 2 , 0);
cout << " [ 三维点 ] p = " << p3f << ";" << endl ;

运行结果:

[三维点] p = [8, 2, 0] ;

3 . 定义和输出基千 Mat 的 std : :vector

    vector<float> v ;
    v . push_back (3 );
    v . push_back (5 );
    v . push_back (7 );
    cout << "[基于Mat的vector] shortvec = " << Mat(v ) << ";" <<endl ;

运行结果:

[基于Mat的ector] shortvec = [3;
 5;
 7];

4. 定义和输出 std : :vector 点
最后看看如何定义和输出存放着 点的 vector 容器,以存放二维点 Point2f为例 :

    vector<Point2f> points (20 );
    for (size_t i = 0; i < points . size (); ++i )
        points[i] = Point2f((float) (i * 5) , (float) (i % 7)) ;
    cout << " [ 二维点向量 ] points = " <<points<< ";";

运行结果:

 [ 二维点向量 ] points = [0, 0;
 5, 1;
 10, 2;
 15, 3;
 20, 4;
 25, 5;
 30, 6;
 35, 0;
 40, 1;
 45, 2;
 50, 3;
 55, 4;
 60, 5;
 65, 6;
 70, 0;
 75, 1;
 80, 2;
 85, 3;
 90, 4;
 95, 5];

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值