等待其他文章使用1

Opencv C++ 基本数据结构 Mat
Mat
构造单通道Mat对象
获取单通道Mat的基本信息
以三行两列的矩阵为例
1、获取行数和列数
2、使用成员函数size()获取矩阵的尺寸
3、使用成员函数channels()获取矩阵的通道数
4、使用成员函数total获得面积(行数乘列数)
5、成员变量dims(维数)
访问单通道对象中的值
1、使用成员函数at
2、利用成员函数ptr
3、使用成员函数isContinuous 和 ptr
4、使用成员变量step和data
向量类Vec(构建多通道Mat的基础)
构造多通道Mat对象
访问多通道对象中的值
1、使用成员函数at
2、利用成员函数ptr
3、使用成员函数isContinuous 和 ptr
4、使用成员变量step和data
5、分离通道
5、合并通道
获得Mat中某一个区域的值
1、使用row(i)或col(j)得到矩阵的第i行j列
2、使用rowRange或colRange得到矩阵的连续行或连续列
3、使用rowRange或colRange得到矩阵的连续行或连续列
4、使用Rect类
Mat
Mat代表矩阵,该类声明在头文件opencv2/core/core.hpp中
其构造函数为:

Mat(int rows, int cols, int type)
1
rows代表行数,cols代表列数
type可以设置为 CV_8UC(n)、CV_8SC(n)、CV_16SC(n)、、CV_16UC(n)、CV_32FC(n)、*、CV_32SC(n)、CV_64FC(n)
其中8U、8S、16S、16U、32S、32F、64F前的数字代表Mat中每一个数值的位数
U代表uchar类型、S代表int类型、F代表浮点型(32F为float、64F为double)其他类似。

构造单通道Mat对象
如代码所示:

# include <opencv2\core\core.hpp>
using namespace cv;
int main() {
    //构造两行三列的float型矩阵
    Mat m = Mat(2, 3, CV_32FC(1));
    //利用Size对象构造两行3列矩阵,Size(列,行)
    Mat m1 = Mat(Size(3, 2), CV_32FC(1));
    //使用Mat类中的成员函数create完成Mat对象构造
    Mat m2;
    m2.create(Size(2, 3), CV_32FC1);
    //构造零矩阵和1矩阵
    Mat o = Mat::ones(2, 3, CV_32FC1);
    Mat z = Mat::zeros(Size(3,2), CV_32FC1);
    //初始化小型矩阵
    Mat m = (Mat_<int>(2, 3) << 1, 2, 3, 4, 5, 6;
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
获取单通道Mat的基本信息
注:单通道与ndarray中的二维对应,多通道与ndarray的三维对应

以三行两列的矩阵为例
m = [ 11 12 33 43 51 16 ] m=\left[
113351124316
11
12
33
43
51
16
\right]
m= 



  
11
33
51

  
12
43
16

  



 

1、获取行数和列数
# include <opencv2\core\core.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
    //快速构造矩阵
    Mat m = (Mat_<int>(3, 2) << 11, 12, 33, 43, 51, 16);
    //矩阵的行数
    cout << "行数:" << m.rows << endl;
    //矩阵的列数
    cout << "行数:" << m.cols << endl;
    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
输出:


2、使用成员函数size()获取矩阵的尺寸
成员函数**size()**返回的是Size对象

//获取尺寸
    Size size = m.size();
    cout << "尺寸:" << size << endl;
1
2
3


3、使用成员函数channels()获取矩阵的通道数
cout << "通道数:" << m.channels() << endl;
1
输出:


4、使用成员函数total获得面积(行数乘列数)
    cout << "面积:" << m.total() << endl;
1
输出


5、成员变量dims(维数)
cout << "维数:" << m.dims << endl;
1
输出:


访问单通道对象中的值
1、使用成员函数at
例如访问单通道数据类型为CV_32F的对象m,访问其第r行c列的值:
格式为:m.at<类型>(行,列)

m.at<float>(r,c)
1
遍历上文中矩阵m中的值并输出:

# include <opencv2\core\core.hpp>
#include <iostream>
using namespace std;
using namespace cv;
int main() {
    //快速构造矩阵
    Mat m = (Mat_<int>(3, 2) << 11, 12, 33, 43, 51, 16);
    //for循环打印m中的每一个值
    for (int r = 0; r < m.rows; r++)
    {
        for (int c = 0; c < m.cols; c++)
        {
            cout << m.at<int>(r, c) << ",";
        }
        cout << endl;
    }
    //return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
还可以结合opencv中的Point类和at成员函数来实现:
m.at(r, c)可用m.at(Point(r,c))代替

2、利用成员函数ptr
矩阵每一行的值值是连续存储在内存中的,行与行在内存中也可能存在间隔
通过ptr成员函数可以获得执行每一行地址的指针
格式为:m.ptr<类型>(第几行)
遍历上文中矩阵m中的值并输出:

for (int r = 0; r < m.rows; r++)
    {
        const int* ptr = m.ptr<int>(r);
        //打印第r行所有值
        for (int c = 0; c < m.cols; c++)
        {
            cout << ptr[c] << ",";
            //cout << *(ptr+c) << ",";
        }
        cout << endl;
    }
1
2
3
4
5
6
7
8
9
10
11
3、使用成员函数isContinuous 和 ptr
矩阵在存储时如果每一行在内存中没有间隔则isContinuous返回true
遍历上文中矩阵m中的值并输出:

if (m.isContinuous())
    {
        //获取指向第一行的指针
        int* ptr = m.ptr<int>(0);
        for (int i = 0; i < m.total(); i++)
        {
            cout << ptr[i] << ",";
            if (i % m.cols)
            {
                cout << endl;
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
4、使用成员变量step和data
对于单通道矩阵来说,step[0]代表每一行所占的字节数,如果有间隔则间隔也作为字节数被计算在内;step[1]代表每一个数值所占的字节数,data是一个指针类型为uchar,它指向第一个数值。
因此无论矩阵行与行之间在内存中是否有间隔,都可以使用以下代码来访问第r行c列:

*((int *)(m.data+m.step[0]*r+c*m.step[1]))
1
向量类Vec(构建多通道Mat的基础)
可以把这里的向量理解为列向量,构造一个 _cn行x1列的数据类型为_Tp的列向量格式:Vec<Typename _Tp, int _cn>
例如构造一个长度为3类型为int,且初始化为11,87,37的列向量:

Vec<int, 3> vi(11, 87, 37);
1
这样构建的向量默认为列向量,可以通过输出行数和列数查看

    cout << vi.rows << endl;
    cout << vi.cols << endl;
1
2

可以使用 “[]” 或"()"操作符f访问向量中的值

//访问向量中的值
    cout << "访问第0个元素" << vi(0) << endl;
    cout << "访问第1个元素" << vi[1] << endl;
1
2
3

opencv 为向量类取了别名:

typedef Vec\<uchar ,3> Vec3b
typedef Vec\<int ,2> Vec2i
typedef Vec\<float,4> Vec4f
typedef Vec\<double ,3> Vec3d
1
2
3
4
单通道矩阵每个元素都是一个数值,多通道矩阵每个元素可以看作一个向量。

构造多通道Mat对象
构造一个由n个rows*cols二维浮点型矩阵组成的三维矩阵的格式如下:

Mat(int rows,int cols,CV_32FC(n))
1
例如构造一个2行2列的float类型的三通道矩阵

Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 32),
        Vec3f(3, 13, 23), Vec3f(4, 24, 34));
1
2
访问多通道对象中的值
1、使用成员函数at
可以将多通道Mat看作一个特殊的二维数组,数组的每个元素不是数值而是一个向量
以上文 创建的mm多通道对象为例遍历输出多通道矩阵mm的每个元素

    for (int r = 0; r < mm.rows; r++)
    {
        for (int c = 0; c < mm.cols; c++)
        {
            cout << mm.at<Vec3f>(r, c);
        }
        cout << endl;
    }
1
2
3
4
5
6
7
8
输出:


2、利用成员函数ptr
多通道Mat的元素在内存中也是按行存储,每一个行存储在连续内存区域中,如果行与行之间由间隔,间隔相等。成员函数ptr返回指向指定行的第一个元素的指针。
使用ptr访问mm对象的每个元素。

for (int r = 0; r < mm.rows; r++)
    {
        Vec3f* ptr = mm.ptr<Vec3f>(r);
        for (int c = 0; c < mm.cols; c++)
        {
            cout << ptr[c] << ",";
            if (c % mm.cols)
            {
                cout << endl;
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
输出与使用at相同

3、使用成员函数isContinuous 和 ptr
与单通道Mat对象一样,可通过isContinuous判断整个Mat对象中的元素是否存储在连续内存中,返回true即表示连续存储;如果是连续存储,就可以少一个循环用ptr遍历

    if (mm.isContinuous())
    {
        //获取指向第一行的指针
        Vec3f* ptr = mm.ptr<Vec3f>(0);
        for (int i = 0; i < mm.total(); i++)
        {
            cout << ptr[i] << ",";
            if (i % mm.cols)
            {
                cout << endl;
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
4、使用成员变量step和data
与单通道Mat类似,step[0]代表每一行所占的字节数,如果有间隔则间隔计算在内,step[1]代表每一个个元素所占的字节数,step[1] = elemSize1()*channels()
elemSize1()代表一个元素中一个数值所占的字节数;data代表首个数值的地址
下面使用step和data遍历输出mm中的元素:

    for (int r = 0; r < mm.rows; r++)
    {
        for (int c = 0; c < mm.cols; c++)
        {
            cout <<*(Vec3f*) (mm.data + r*mm.step[0]+c*mm.step[1]);
        }
        cout << endl;
    }
1
2
3
4
5
6
7
8
输出与1,2,3相同

5、分离通道
以上述讨论的mm多通道对象为例,将所有向量第一个数值组成的二维矩阵作为第一通道,将所有向量第二个数值组成的单通道矩阵作为第二通道以此类推;
所以mm分离后的三个通道为
第一通道:2x2的矩阵,元素为 1、2、3、4
第二通道:2x2的矩阵,元素为 11、12、13、24
第三通道:2x2的矩阵,元素为 21、32、23、34
使用split可以将mm分离为多个单通道:

vector<Mat>planes;
split(mm, planes);
1
2
5、合并通道
使用merge函数可以将多个单通道矩阵合并为一个三维矩阵
该函数声明为:

void merge (const Mat *mv, size_t count, OutputArray dst)
//其重载函数
void merge (InputArrayofArrays mv, OutputArray dst)
1
2
3
例如将三个2*2int型单通道合并:

    Mat plane0 = (Mat_<int>(2, 2) << 1, 2, 3, 4);
    Mat plane1 = (Mat_<int>(2, 2) << 5, 6, 7, 8);
    Mat plane2 = (Mat_<int>(2, 2) << 9, 10, 11, 12);
    //初始化Mat数组也
    Mat plane[]{ plane0, plane1, plane2 };
    //声明一个mat对象用来保存合并后的多通道对象
    Mat mat;
    merge(plane, 3, mat);
    //将三个单通道矩阵放在vector容器中
    vector<Mat>pl;
    pl.push_back(plane0);
    pl.push_back(plane1);
    pl.push_back(plane2);
    Mat mat1;
    merge(pl, mat);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
获得Mat中某一个区域的值
1、使用row(i)或col(j)得到矩阵的第i行j列
Mat mr = m.row(1);
Mat mc = m.col(1);
1
2
2、使用rowRange或colRange得到矩阵的连续行或连续列
首先知道Opencv中的Range类,该类用于构造连续整数序列,左闭右开。
Range(2,5)产生2、3、4序列。以5*5矩阵为例
m a t r i x = [ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ] matrix = \left[
16111621271217223813182349141924510152025
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
\right]
matrix= 







  
1
6
11
16
21

  
2
7
12
17
22

  
3
8
13
18
23

  
4
9
14
19
24

  
5
10
15
20
25

  







 

//构造一个5*5int型矩阵
    Mat matrix = (Mat_<int>(5, 5) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25);
    //访问第2、3两行
    Mat r_range = matrix.rowRange(Range(2, 4));//可以写为matrix.rowRange(2,4)
    for (int r = 0; r < r_range.rows; r++)
    {
        for (int c = 0; c < r_range.cols; c++)
        {
            cout << r_range.at<int>(r, c) << ",";
        }
        cout << endl;
    }
1
2
3
4
5
6
7
8
9
10
11
12
输出:

colRange与之类似

Mat c_range = matrix.colRange(Range(2, 4));
    for (int r = 0; r < c_range.rows; r++)
    {
        for (int c = 0; c < c_range.cols; c++)
        {
            cout << c_range.at<int>(r, c) << ",";
        }
        cout << endl;
    }
1
2
3
4
5
6
7
8
9
输出:

注意:成员函数row、 col 、 rowRange 、colRange 返回的矩阵是指向原矩阵的,例如改变c_range第1行1列的值,原矩阵也会改变:

c_range.at<int>(1, 1) = 1000;//改为1000
    cout <<  "输出c_range" <<endl;
    for (int r = 0; r < c_range.rows; r++)
    {
        for (int c = 0; c < c_range.cols; c++)
        {
            cout << c_range.at<int>(r, c) << ",";
        }
        cout << endl;
    }
    cout << "输出原矩阵" << endl;
    for (int r = 0; r < matrix.rows; r++)
    {
        for (int c = 0; c < matrix.cols; c++)
        {
            cout << matrix.at<int>(r, c) << ",";
        }
        cout << endl;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
输出:

使用成员函数clone和copyTo可以解决这个问题

3、使用rowRange或colRange得到矩阵的连续行或连续列
使用这两个成员函数会将矩阵克隆一份,以上述r_range为例

    Mat r_range = matrix.rowRange(Range(2, 4)).clone();
    r_range.at<int>(0, 0) = 1000;//修改值为1000
    cout << "输出r_range" << endl;
    for (int r = 0; r < r_range.rows; r++)
    {
        for (int c = 0; c < r_range.cols; c++)
        {
            cout << r_range.at<int>(r, c) << ",";
        }
        cout << endl;
    }
    cout << "输出原矩阵" << endl;
    for (int r = 0; r < matrix.rows; r++)
    {
        for (int c = 0; c < matrix.cols; c++)
        {
            cout << matrix.at<int>(r, c) << ",";
        }
        cout << endl;
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
输出:

copyTo的用法为:

matrix.rowRange(2,4).copyTo(r_range);
1
4、使用Rect类
如果我们需要矩阵中一块矩形区域,我们可以使用rowRange和colRange来定位,但是Opencv提供了Rect类来简化操作。知道矩形的左上角坐标,和矩形的宽高就可以确定一个矩形,所以其构造函数为:

Rect(int _x,int _y, int _width, int _height);
1
也可以将 _width 和 _height保存在一个Size中

Rect(int _x,int _y, Size size);
1
如果知道左上角和右下角的坐标也可以确定一个矩形,所以构造函数为:

Rect(Point2i &pt1,Point2i &pt2);
1
以之前叙述的5*5矩阵matrix为例,要获得使矩形框中的值为8,9,13,14
可以按如下方式构建矩形框

    Mat roi1 = matrix(Rect(Point(2, 1), Point(4, 3)));//左上角和右下角坐标,"左开右闭"
    Mat roi2 = matrix(Rect(2, 1, 2, 2)) ;//x,y,宽,高
    Mat roi3 = matrix(Rect(Point(2, 1), Size(2,2)));//左上角坐标,尺寸
1
2
3
使用for循环输出:

这样得到的矩形区域仍然是指向原矩阵的所以,仍然可以使用clone和copyTo

Mat roi1 = matrix(Rect(Point(2, 1), Point(4, 3))).clone;
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/renweiyi1487/article/details/101616758

前言:

😊😊😊欢迎来到本博客😊😊😊

🌟🌟🌟 本专栏主要结合OpenCV和C++来实现一些基本的图像处理算法并详细解释各参数含义,适用于平时学习、工作快速查询等,随时更新。

😊😊😊 具体食用方式:可以点击本专栏【OpenCV快速查找(更新中)】–>搜索你要查询的算子名称或相关知识点,或者通过这篇博客👉通俗易懂OpenCV(C++版)详细教程——OpenCV函数快速查找(不断更新中)]查阅你想知道的知识,即可食用。

🎁🎁🎁支持:如果觉得博主的文章还不错或者您用得到的话,可以悄悄关注一下博主哈,如果三连收藏支持就更好啦!这就是给予我最大的支持!😙😙😙

文章目录
学习目标
一、 什么是Mat类
二、 了解向量Vec类
三、 构造单、多通道Mat对象
3.1 构造单通道Mat对象
3.2 构造多通道Mat对象
四、 获取单、多通道Mat的信息
4.1 获取单通道Mat的信息
(1) 使用成员变量rows和cols获取矩阵的行数和列数
(2) 使用成员函数`size()`获取矩阵的尺寸
(3) 使用成员函数`channels()`得到矩阵的通道数
(4) 成员函数`total()`
(5) 成员变量`dims`
4.2 获取多通道Mat中某区域的值
(1) 使用成员函数row(i)或col(j)得到矩阵的第i行或者第j列
(2) 使用成员函数`rowRange`或`colRange`得到矩阵的连续行或者连续列
(3) 成员函数`clone()`和`copyTo()`
(4) 使用`Rect`类
五、 访问单、多通道Mat对象中的值
5.1 访问单通道Mat对象中的值
(1) 使用成员函数`at`
(2) 使用成员函数`ptr`
(3) 使用成员函数`isContinuous()`和`ptr`
(4) 使用成员变量`step`和`data`
5.2 访问多通道Mat对象中的值
(1) 使用成员函数`at`
(2) 使用成员函数`ptr`
(3) 使用成员函数`isContinuous()`和`ptr`
(4) 使用成员变量`step`和`data`
(5) 分离通道
(6) 合并通道
六、 总结
学习目标
本章内容较多,如有需要,耐心阅读。

 初识OpenCV中Mat类
 构造单、多通道Mat对象
 获取单、多通道Mat的信息
 访问单、多通道Mat对象中的值
一、 什么是Mat类
  Mat类(Matrix)是OpenCV中最核心的类,代表矩阵或者数组的意思,该类的声明在头文件<opencv2\core\core.hpp>中,当然直接声明 <opencv2/opencv.hpp>也可以,所以使用Mat类时要引入相关头文件。
  构造Mat对象相当于构造了一个矩阵(数组),需要四个基本要素:行数(高)、列数(宽)、通道数及其数据类型,Mat类的构造函数如下:

Mat(int rows,int cols,int type)
1
  其中,rows代表矩阵的行数,cols代表矩阵的列数,type代表类型,包括通道数及其数据类型,可以设置为:

类型    注释
<CV_8UC(n)    占1字节的uchar类型
CV_8SC(n)    占1字节的int类型
CV_16SC(n)    占2字节的int类型
CV_16UC(n)    占2字节的uchar类型
CV_32SC(n)    占4字节的int类型
CV_32FC(n)    占4字节的float类型
CV_64FC(n)    占8字节的doule类型
注:8U、8S、16S、16U、32S、32F、64F前面的数字代表Mat中每一个数值所占的bit数,1byte=8bit,C(n)代表通道数,当n=1时,即构造单通道矩阵或称二维矩阵,当n≠1时,即构造多通道矩阵即三维矩阵,直观上就是n个二维矩阵组成的三维矩阵。

  对于Mat构造函数也可以采用以下形式:

Mat(Size(int cols,int rows),int type);
1
  这里使用了OpenCV的Size类,这个类一般用来存储矩阵的列数和行数。需要注意的是:Size的第一个元素是矩阵的列数(宽),第二个元素是矩阵的行数(高),即先存宽,再存高。

  

二、 了解向量Vec类
  后面在介绍多通道Mat对象相关知识中,会涉及到OpenCV的向量类,这里先了解关于OpenCV中的向量类相关内容。
  这里的向量可以理解为我们数学学过的列向量,构造一个_cn×1的列向量,数据类型为_Tp,格式如下:

Vec<TypeName _Tp,int _cn>    
1
  构造一个长度为5,数据类型为int且初始化12,13,14,15,16的列向量:

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

using namespace std;
using namespace cv;

int  main(int argc, char**argv) {

    Vec<int, 5> vv(12,13,14,15,16);
    cout << "vec列向量的行数为:" << vv.rows << endl;
    cout << "vec列向量的列数为:" << vv.cols << endl;

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

  如果我们想访问向量里的元素,则可以利用“[]”或者“()”操作符访问向量中的值:

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

using namespace std;
using namespace cv;

int  main(int argc, char**argv) {

    Vec<int, 5> vv(12,13,14,15,16);
    cout << "vec列向量的行数为:" << vv.rows << endl;
    cout << "vec列向量的列数为:" << vv.cols << endl;
    cout << "访问第一个元素:" << vv[0] << endl;
    cout << "访问第二个元素:" << vv(1) << endl;

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

  OpenCV为向量类的声明取了一个别名,例如:

typedef Vec<unchar,3> Vec3b;
typedef Vec<int,2> Vec2i;
typedef Vec<float,4> Vec4f;
typedef Vec<double,3> Vec3d;
1
2
3
4
  注:通道矩阵的每一个元素都是一个数值,多通道矩阵的每一个元素都可以看作一个向量。这是多通道Mat的基础,后面会在彩色图像的数字化详细介绍。

  

三、 构造单、多通道Mat对象
3.1 构造单通道Mat对象
  构造单通道Mat对象的方式有很多,不同的方式有各自特点及注意点,下面介绍各种构造方法。
  构造3行4列float类型的单通道矩阵,代码如下:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造3行4列的矩阵
    Mat m = Mat(3, 4, CV_32FC(1));

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
  也可以采用Size对象构造:

//借助Size对象
Mat m = Mat(Size(4, 3), CV_32FC(1));
1
2
注:这里的Size(4,3)是指4列3行。

  还可以使用Mat中的成员函数create完成Mat对象的构造,代码如下:

Mat m;
m.create(3, 4, CV_32FC(1));
//m.create(Size(4, 3), CV_32FC(1));
1
2
3
注:CV_32FC(1)和CV_32FC1的写法都可以。

  
  在平时操作过程中,常见的0矩阵、1矩阵能够更直接去构造,比如构造一个3行4列全1的float类型的单通道矩阵:

//构造全1的3行4列单通道矩阵
Mat one = Mat::ones(3, 4, CV_32FC(1));
Mat one = Mat::ones(Size(4, 3), CV_32FC(1));
1
2
3
  构造一个3行4列全0的float类型的单通道矩阵:

//构造全0的3行4列单通道矩阵
Mat zero = Mat::zeros(3, 4, CV_32FC(1));
Mat zero = Mat::zeros(Size(4, 3), CV_32FC(1));
1
2
3
  平时在测试一些小项目时,可以快速创建矩阵,那么就可以直接定义:

Mat m = (Mat_<int>(3, 4) << 1, 2, 3, 4, 5, 6);
1
  

3.2 构造多通道Mat对象
  构造一个由n个rows*cols二维浮点型矩阵组成的三维矩阵,具体如下:

Mat(int rows, int cols, CV_32FC(n))
1
  前面讲了当n=1时,就是单通道矩阵了。如果构造一个2行2列的float类型的三通道矩阵,代码如下:

//构造一个2行2列的float类型的三通道矩阵
Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 22, 21), Vec3f(2, 12, 31), Vec3f(3, 13, 23), Vec3f(4, 24, 34));
1
2
注:这里的Vec3f(1, 22, 21)写法详见上述二、 了解向量Vec类。

  

四、 获取单、多通道Mat的信息
4.1 获取单通道Mat的信息
  下面详细介绍Mat的成员变量和成员函数,以便获得矩阵m的基本信息,以3行2列的二维矩阵为例:


(1) 使用成员变量rows和cols获取矩阵的行数和列数
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);

    //矩阵行数
    cout << "矩阵m的行数为:" << m.rows << endl;
    //矩阵列数
    cout << "矩阵m的列数为:" << m.cols << endl;

    return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

  

(2) 使用成员函数size()获取矩阵的尺寸
  上述是通过成员函数获取矩阵行和列,我们还可以通过成员函数size()直接得到矩阵尺寸的Size对象,即矩阵大小:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);

    //矩阵行数
    cout << "矩阵m的行数为:" << m.rows << endl;
    //矩阵列数
    cout << "矩阵m的列数为:" << m.cols << endl;
    
    //矩阵大小
    cout << "矩阵m的大小为:" << m.size() << endl;

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

  

(3) 使用成员函数channels()得到矩阵的通道数
  可以通过成员函数channels()得到Mat的通道数:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);

    //矩阵行数
    cout << "矩阵m的行数为:" << m.rows << endl;
    //矩阵列数
    cout << "矩阵m的列数为:" << m.cols << endl;
    
    //矩阵大小
    cout << "矩阵m的大小为:" << m.size() << endl;
    
    //矩阵通道数
    cout << "矩阵m的通道数为:" << m.channels() << endl;

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

  

(4) 成员函数total()
  total()的返回值是矩阵的行数乘以列数,即面积。注意和通道数无关,返回的不是矩阵中数据的个数。

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);

    //矩阵行数
    cout << "矩阵m的行数为:" << m.rows << endl;
    //矩阵列数
    cout << "矩阵m的列数为:" << m.cols << endl;
    
    //矩阵大小
    cout << "矩阵m的大小为:" << m.size() << endl;
    
    //矩阵通道数
    cout << "矩阵m的通道数为:" << m.channels() << endl;

    //面积
    cout << "矩阵m的面积为:" << m.total() << endl;
    
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

  

(5) 成员变量dims
   dims代表矩阵的维数,对于单通道矩阵来说就是一个二维矩阵,对于多通道矩阵来说就是一个三维矩阵。

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);

    //矩阵行数
    cout << "矩阵m的行数为:" << m.rows << endl;
    //矩阵列数
    cout << "矩阵m的列数为:" << m.cols << endl;
    
    //矩阵大小
    cout << "矩阵m的大小为:" << m.size() << endl;
    
    //矩阵通道数
    cout << "矩阵m的通道数为:" << m.channels() << endl;

    //面积
    cout << "矩阵m的面积为:" << m.total() << endl;

    //矩阵维度
    cout << "矩阵m的维度为:" << m.dims << endl;
    
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

  

4.2 获取多通道Mat中某区域的值
(1) 使用成员函数row(i)或col(j)得到矩阵的第i行或者第j列
  对于单通道矩阵而言,获取某行或者某列的所有值很好理解,即:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);
    int r = 1;
    int c = 0;

    Mat mr = m.row(r);
    Mat ml = m.col(c);


    cout <<"矩阵m第["<<r<<"]行为:" <<mr << endl;
    cout << "矩阵m第[" << c << "]列为:" << ml << endl;


    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

注意:返回值仍然是一个单通道的Mat类型。

  

(2) 使用成员函数rowRange或colRange得到矩阵的连续行或者连续列
  在学习该成员函数之前,先了解OpenCV中的Range类,该类用于构造连续的整数序列,构造函数如下:

Range(int _start, int _end);
1
注意:这是一个左闭右开的序列[_start,_end),比如Range(2,6)其实产生的是2、3、4、5的序列,不包括6,常用作rowRange和colRange的输入参数,从而访问矩阵中的连续行或者连续列。

  下面我们构造一个4x4的矩阵:


  访问mm的第2、3行:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat mm = (Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    Mat mm_range = mm.rowRange(Range(2, 4)); //另一种写法:Mat mm_range = mm.rowRange(2, 4);

    for (int  r = 0; r <mm_range.rows; r++)
    {
        for (int c = 0; c < mm_range.cols; c++)
        {
            cout << mm_range.at<int>(r,c)<< ",";
        }
        cout << endl;
    }

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

  成员函数rowRange是一个重载函数,也可以直接将 Range 的_start 和_end 直接作为rowRange的输入参数,上述获取矩阵连续行的操作可以直接写为:

Mat mm_range = mm.rowRange(2, 4);
1
  同理,想获取矩阵第几列可以用colRange,例如:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat mm = (Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    //Mat mm_range = mm.rowRange(Range(2, 4));  //另一种写法:Mat mm_range = mm.rowRange(2, 4);

    Mat mm_range = mm.colRange(Range(2, 4));  //另一种写法:Mat mm_range = mm.colRange(2, 4);

    for (int  r = 0; r <mm_range.rows; r++)
    {
        for (int c = 0; c < mm_range.cols; c++)
        {
            cout << mm_range.at<int>(r,c)<< ",";
        }
        cout << endl;
    }

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

  需要特别注意的是:成员函数rows、cols、rowRange、colRange返回的矩阵是指向原矩阵的,比如改变r_range的第1行第1列的值:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat mm = (Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    //Mat mm_range = mm.rowRange(Range(2, 4));  //另一种写法:Mat mm_range = mm.rowRange(2, 4);

    Mat mm_range = mm.colRange(Range(2, 4));  //另一种写法:Mat mm_range = mm.colRange(2, 4);

    for (int  r = 0; r <mm_range.rows; r++)
    {
        for (int c = 0; c < mm_range.cols; c++)
        {
            cout << mm_range.at<int>(r,c)<< ",";
        }
        cout << endl;
    }
    cout <<"===================================="<< endl;

    //改变第1行第1列值
    mm_range.at<int>(1, 1) = 50;
    cout << "改变后的矩阵为:" << endl;
    cout << mm_range << endl;

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

   但是,我们只访问原矩阵的某些行或列,不想改变原矩阵的值,该如何做呢?

  

(3) 成员函数clone()和copyTo()
  对于刚刚遗留的问题,从函数名就可以看出,clone()和copyTo()用于将矩阵克隆或者复制一份。

  首先,关于clone()函数:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat mm = (Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    //克隆函数
    Mat mm_range_clone = mm.rowRange(Range(2, 4)).clone();

    //改变mm_range_clone第1行第1列值
    mm_range_clone.at<int>(1, 1) = 50;
    cout << "改变后的矩阵为:" << endl;
    cout << mm_range_clone << endl;
    cout << "====================================" << endl;
    cout << "原矩阵为:" << endl;
    cout << mm << endl;
    
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

  将矩阵mm的第2、3行克隆一份,这时候改变mm_range_clone的值,mm中的值是不变的。
  
  接下来,类似操作,也可以使用copyTo()函数:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int  main(int argc, char**argv) {
    //构造矩阵
    Mat mm = (Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    //克隆函数
    Mat mm_range_clone = mm.rowRange(Range(2, 4)).clone();

    //改变mm_range_clone第1行第1列值
    mm_range_clone.at<int>(1, 1) = 50;
    cout << "改变后的矩阵为:" << endl;
    cout << mm_range_clone << endl;
    cout << "====================================" << endl;
    cout << "原矩阵为:" << endl;
    cout << mm << endl;
    
    cout << "*******************************************" << endl;
    
    //复制函数
    Mat mm_range_copy;
    mm.rowRange(Range(2, 4)).copyTo(mm_range_copy);

    //改变mm_range_copy第1行第1列值
    mm_range_copy.at<int>(1, 1) = 100;
    cout << "改变后的矩阵为:" << endl;
    cout << mm_range_copy << endl;
    cout << "====================================" << endl;
    cout << "原矩阵为:" << endl;
    cout << mm << endl;
    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

  将mm中的第2、3行复制到mm_range_copy中。
  

(4) 使用Rect类
  如果我们要获取矩阵中某一特定的矩形区域,根据上述方法可以先使用rowRange,再使用colRange来定位。
   OpenCV提供了一种更简单的方式,就是使用Rect类(Rect是Rectangle的缩写,矩形的意思)。构造一个矩形有多种方式:

条件    构造函数
知道左上角的坐标(x,y),还有矩形的宽度和高度    Rect(int_x,int_y,int_width,int_hight)
将_width和_height保存在一个Size中    Rect(int_x,int_y,Size size)
知道左上角和右下角的坐标也可以构造一个矩形    Rect(Point1,Point2) 注意范围为前闭后开,不含点Point2
   当然,还有其他的构造函数,这里不再一一列举,都差不多,掌握这三种也就够了。我们还以4x4的矩阵为例:


#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int  main(int argc, char**argv) {
    //构造矩阵
    Mat mm = (Mat_<int>(4, 4) << 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);
    Mat rec1 = mm(Rect(Point(1, 1), Point(3, 3)));//左上角坐标、右下角坐标
    Mat rec2 = mm(Rect(1,1,2,2));//左上角坐标、宽、高
    Mat rec3 = mm(Rect(Point(1, 1),Size(2,2)));//左上角坐标、尺寸
    cout << "rec1区域为:" << endl;
    cout << rec1 << endl;
    cout << "====================================" << endl;
    cout << "rec2区域为:" << endl;
    cout << rec2 << endl;
    cout << "====================================" << endl;
    cout << "rec3区域为:" << endl;
    cout << rec3 << endl;


    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

  这样得到的矩形区域是指向原矩阵的,要改变rec1中的值,mm也会发生变化,如果不想这样,则仍然可以使用clone()或者copyTo()。

五、 访问单、多通道Mat对象中的值
5.1 访问单通道Mat对象中的值
(1) 使用成员函数at
  访问Mat对象中的值,最直接的方式是使用Mat的成员函数at,如对于单通道且数据类型为CV_32F的3行2列的二维矩阵矩阵m:


  访问它的第r行第c列的值,格式为:m.at<Typename>(r,c)。我们还是以矩阵m为例,使用at访问它的值。对于矩阵m中的值依次存入下表中,按照行和列的索引取值即可:


  利用成员函数at依次访问m中的所有值并打印:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
    //构造矩阵
int main(int argc,char**argv){
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);

    for (int r = 0; r <m.rows; r++)
    {
        for (int c = 0; c < m.cols; c++)
        {
            cout << m.at<int>(r, c) << ",";
        }
        cout << endl;
    }

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

  
  除了使用成员函数at访问,还可以使用Point类和成员函数at来实现:即将代码m.at<Typename>(r,c)改为m.at<Typename>(Point(c,r)),把行和列的索引变为坐标的形式。

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
    //构造矩阵
int main(int argc,char**argv){
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);

    for (int r = 0; r <m.rows; r++)
    {
        for (int c = 0; c < m.cols; c++)
        {
            //cout << m.at<int>(r, c) << ",";
            cout << m.at<int>(Point(c,r)) << ",";
        }
        cout << endl;
    }

    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

  注:我们习惯表示矩阵的第r行第c列,也可以利用Point指明矩阵中某一个固定位置,那么该位置就用Point(c,r)来定义,注意第一个元素是列坐标(列号),第二个元素是行坐标(行号),符合我们将水平方向作为x轴,将垂直方向作为y轴的习惯,这里的y轴的方向是朝下的。

  

(2) 使用成员函数ptr
  Mat中的数值在内存中的存储,每一行的值是存储在连续的内存区域中的,我们可以通过成员函数ptr获得指向每一行首地址的指针。
   以矩阵m为例,m中所有的值在内存中的存储方式如下图所示,其中如果行与行之间的存储是有内存间隔的,那么间隔也是相等的。


#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
    //构造矩阵
int main(int argc,char**argv){
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);
    
    /*成员函数ptr*/
    for (int r = 0; r <m.rows; r++)
    {    
        //获取矩阵m第r行行首地址
        const int  *ptr = m.ptr<int>(r);
        //打印第r行所有值
        for (int c = 0; c < m.cols; c++)
        {
            cout << ptr[c] << ",";
        }
        cout << endl;
    }
    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

  

(3) 使用成员函数isContinuous()和ptr
   从刚刚的图中可以一目了然,即每一行的所有值存储在连续的内存区域中,行与行之间可能会有间隔,
  如果isContinuous返回值为true,则代表行与行之间也是连续存储的,即所有的值都是连续存储的,如图下图所示。

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
    //构造矩阵
    Mat m = (Mat_<int>(3, 2) << 12, 15, 13, 16, 14, 17);
    /*成员函数isContinuous*/
    if (m.isContinuous())
    {    
        cout << "isContinuous is: " << m.isContinuous()<< endl;
        //获取矩阵m第1个值的地址
        const int  *ptr = m.ptr<int>(0);
        for (int n = 0; n < m.rows * m.cols; n++)
        {
            cout << ptr[n] << ",";
        }
    }
    
    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

  

(4) 使用成员变量step和data
  从前面的讨论我们已经知道,Mat中的值在内存中存储分为两种情况,以矩阵m为例,如上述(2)和(3)所述。下面通过这两种情况,介绍两个重要的成员变量——step和data。

   如下图所示是行与行之间有相等的内存间隔的情况,即不连续:


   如下图所示是是连续的情况:


  如上图所示:对于单通道矩阵而言,step[0]代表每一行所占的字节数,而如果有间隔的话,这个间隔也作为字节数的一部分被计算在内; step[1]代表每一个数值所占的字节数;data是指向第一个数值的指针,类型为uchar。

   所以,无论哪一种情况,如访问一个int类型的单通到矩阵的第r行第c列的值,都可以通过以下代码来实现:

*((int*)(m.data+m.step[0]*r+c*m.step[1]))
1
  总结:如果是CV_32F类型,则将int替换成float即可。从取值效率上说,直接使用指针的形式取值是最快的,使用at是最慢的,但是可读性很高。

  

5.2 访问多通道Mat对象中的值
  访问多通道Mat对象中的值使用的成员函数与单通道几乎一致,不同的是多通道所表示的矩阵维度有差异,具体如下。

(1) 使用成员函数at
  成员函数at访问多通道Mat的元素值,可以将多通道Mat看作一个特殊的二维数组,即在每一个位置上不是一个数值而是一个向量(元素)。


  通过上图按行号、列号取出每一个元素Vec3f即可:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
   //构造矩阵
   Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 22), Vec3f(3, 13, 23), Vec3f(4, 14, 24));


   /* 成员函数at*/
   for (int r = 0; r <mm.rows; r++)
   {
       for (int c = 0; c < mm.cols; c++)
       {
           cout << mm.at<Vec3f>(r, c) << ",";
           //cout << m.at<Vec3f>(Point(c, r)) << ",";
       }
       cout << endl;
   }
   return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

  也可以使用cout << m.at<Vec3f>(Point(c, r)) << ",";替换cout << mm.at<Vec3f>(r, c) << ",";,具体注意点与单通道成员函数at一致。

  

(2) 使用成员函数ptr
  多通道Mat的数值在内存中也是按行存储的,且每一行存储在连续的内存区域中,如果行与行之间有内存间隔,这个间隔也是相等的,成员函数ptr可以返回指向指定行的第一个元素(注意不是第一个数值)的指针,如下图所示:


  使用ptr访问多通道矩阵的每一个元素:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
    //构造矩阵
    Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 22), Vec3f(3, 13, 23), Vec3f(4, 14, 24));
    /*成员函数ptr*/
    for (int r = 0; r <mm.rows; r++)
    {
    //获取矩阵m第r行行首地址
    const Vec3f  *ptr = mm.ptr<Vec3f>(r);
    //打印第r行所有值
    for (int c = 0; c < mm.cols; c++)
    {
    cout << ptr[c] << ",";
    }
    cout << endl;
    }
    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

  

(3) 使用成员函数isContinuous()和ptr
  与单通道Mat对象类似,通过isContinuous判断整个Mat对象中的元素值是否存储在连续内存区域中,如果返回值是true,即表示是连续存储的,如下图所示:


#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
    //构造矩阵
    Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 22), Vec3f(3, 13, 23), Vec3f(4, 14, 24));
    /*成员函数isContinuous*/
    if (m.isContinuous())
    {
        cout << "isContinuous is: " << mm.isContinuous() << endl;
        //指向多通道矩阵mm第1个元素的地址
        const Vec3f  *ptr = mm.ptr<Vec3f>(0);
        for (int n = 0; n < mm.rows * m.cols; n++)
        {
            cout << ptr[n] << ",";
        }
    }
    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

  如果只获取第r行第c列的元素值,那么就可以通过ptr[r*rows+c*cols]命令得到。

  

(4) 使用成员变量step和data
  单通道Mat类似,通过data和step获取多通道Mat的每一个元素。也是分两种情况:行与行之间有内存间隔和连续存储。

   如下图所示是行与行之间有相等的内存间隔的情况,即不连续,step[0]代表包含这个间隔的字节数:


  
   如下图所示是是连续的情况:


  使用data、step、ptr获得多通道矩阵的每一个元素:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
    //构造矩阵
    Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 22), Vec3f(3, 13, 23), Vec3f(4, 14, 24));
    // 成员函数data step ptr
    for (int r = 0; r <mm.rows; r++)
    {
        for (int c = 0; c < mm.cols; c++)
        {
            Vec3f  *ptr = (Vec3f*)(mm.data + r*mm.step[0] + c*mm.step[1]);
            cout << *ptr << " ";
    }
        cout << endl;
    }
    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

  

(5) 分离通道
  对于多通道矩阵,可以将其分离为多个单通道矩阵,然后按照单通道矩阵的规则访问其中的值。
  如下图可以形象看出多通道矩阵分离成单通道矩阵的,即把所有向量的第一个值组成的单通道矩阵作为第一通道,将所有向量的第二元素组成的单通道矩阵作为第二通道,依次类推:


  OpenCV中提供了的split()函数来分离多通道,将多通道矩阵mm分离为多个单通道,这些单通道矩阵被存放在vector容器中:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
    //构造矩阵
    Mat mm = (Mat_<Vec3f>(2, 2) << Vec3f(1, 11, 21), Vec3f(2, 12, 22), Vec3f(3, 13, 23), Vec3f(4, 14, 24));
    
    /* 成员函数split*/
    vector<Mat> SingleChannel;
    split(mm, SingleChannel);


    //获取每个通道的值
    for (int i = 0; i < SingleChannel.size(); i++)

    {
        cout << "第["<< i+1 <<"]通道:" << endl;
        cout << SingleChannel[i] << endl;
        cout << "==========" << endl;

    }

    return 0;

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

  

(6) 合并通道
  有分有合,merge()函数可以将多个单通道矩阵合并为一个三维矩阵。merge()函数声明如下:

void merge(const Mat * mMV, size_ t count, OutputArray dst)
1
  例如将三个2行2列的int类型的单通道矩阵合并为一个多通道矩阵:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
    Mat channel1 = (Mat_<int>(2, 2) << 11, 12, 13, 14);
    Mat channel2 = (Mat_<int>(2, 2) << 21, 22, 23, 24);
    Mat channel3 = (Mat_<int>(2, 2) << 31, 32, 33, 34);

    Mat multiChannel[] = {channel1, channel2, channel3};
    Mat MM;
    merge(multiChannel, 3, MM);
    cout << MM;

    return 0;


}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

  上面代码将三个单通道矩阵存储在一个Mat数组中,当然也可以将其存储在vector容器中,使用merge的重载函数:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

int main(int argc,char**argv){
    Mat channel1 = (Mat_<int>(2, 2) << 11, 12, 13, 14);
    Mat channel2 = (Mat_<int>(2, 2) << 21, 22, 23, 24);
    Mat channel3 = (Mat_<int>(2, 2) << 31, 32, 33, 34);

    //Mat multiChannel[] = {channel1, channel2, channel3};
    vector<Mat> multiChannel;
    

    multiChannel.push_back(channel1);
    multiChannel.push_back(channel2);
    multiChannel.push_back(channel3);

    Mat MM;
    //merge(multiChannel, 3, MM);

    merge(multiChannel, MM);

    cout << MM;
    
    return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

六、 总结
  最后,长话短说,大家看完就好好动手实践一下,切记不能三分钟热度、三天打鱼,两天晒网。OpenCV是学习图像处理理论知识比较好的一个途径,大家也可以自己尝试写写博客,来记录大家平时学习的进度,可以和网上众多学者一起交流、探讨,有什么问题希望大家可以积极评论交流,我也会及时更新,来督促自己学习进度。希望大家觉得不错的可以点赞、关注、收藏。
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/qq_41225961/article/details/128196585

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值