Mat 为OpenCV中的核心数据结构,主要负责图像数据的保存,Mat创建方法有很多种

Mat构造函数

可以使用Mat构造函数,创建Mat,Mat构造函数有多种形式的参数,来满足要求

Mat 主要构造函数

Description

Mat()

默认构造函数

Mat(int rows, int cols, int type)

矩阵的行和列或者是图片的高和宽

type为存储的数据格式为单通道还是多通道,以及是char 还是unshort等,其type参数为CV_8UC1, ..., CV_64FC4 等最多为4个通道,其中UC为数据类型为unsigned char

SC为 char, 16UC为unsigned short , 16SC为short, 32SC为 int, 32FC为32位浮点型, 64FC为64位浮点型

Mat(Size size, int type)

Size位创建一个二维矩阵,其参数为Size(cols, rows),type同上

 Mat(int rows, int cols, int type, const Scalar& s)

带初始化的Mat创建,其中Scalar为初始化Mat中的值,为OpenCV的比较重要的数据结构,最多具有4个元素值,常用来传递像素值,例如RGB,其表达式可以为Scalar(a, b, c).

如果是单通道可以为Scalar(a)

如果数双通道为Scalar(a, b)

 Mat(Size size, int type, const Scalar& s)

带初始化的Mat创建

Mat(int ndims, const int* sizes, int type)

ndims为Mat维度

sizes为个维度大小的数组

Mat(const std::vector<int>& sizes, int type)

verctor形式参数初始化,其中sizes为各维度大小,

Mat(int ndims, const int* sizes, int type, const Scalar& s)

带初始化的Mat创建

Mat(const std::vector<int>& sizes, int type, const Scalar& s)

带初始化的Mat创建

Mat(const Mat& m)

直接从已有的一个Mat中进行创建一个新的Mat

注意只创建Mat的头,其数据矩阵还是指向的同一块

Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)

带数据初始化Mat创建,通常用于将一块数据转换成Mat场景中.

step:Number of bytes each matrix row occupies

每个矩阵一行占用的字节数,实际值为cols*elemSize()

其中elemSize()为单个值占用的字节数*通道数,默认为AUTO_STEP

Mat(Size size, int type, void* data, size_t step=AUTO_STEP)

带初始化的Mat创建,同上

Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)

带初始化的Mat创建,step为本像素点与下一个像素点间隔几个数据,即该数据通道是几个通道,

Mat(const std::vector<int>& sizes, int type, void* data, const size_t* steps=0);

带初始化的Mat创建,同上

Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all())

Mat初始化,  Range(int _start, int _end),表示的是其行或列的开始以及结束位置,可以选择性的截取m中的一部分数据,其数据部分还是同一块,不会产生新的数据copy,只是相当于取指针

Mat(const Mat& m, const Rect& roi)

Mat初始化,和 Range类似只是取其中一部分

Mat(const Mat& m, const Range* ranges)

Mat初始化,只是规定行的起始位置,没有规定列的起始位置

Mat(const Mat& m, const std::vector<Range>& ranges)

Mat初始化,只是规定行的起始位置,没有规定列的起始位置

    用例大概如下:

 

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	
	int sz[3] = { 2,2,2 };
	Mat L(3, sz, CV_8UC1, Scalar::all(12));

	Mat mtx();

	Mat test1(640, 480, CV_8UC1);
	Mat test2(Size(640, 480), CV_8UC1);
	
	Mat M(2, 2, CV_8UC3, Scalar(110, 0, 255));
	cout << "M= " << endl << " " << M << endl << endl;
	Mat test3(Size(2, 2), CV_8UC3, Scalar(120, 0, 255));
	cout << "test3= " << endl << " " << test3 << endl << endl;

	int sz2[2] = { 5,5};
	Mat test4(2, sz2, CV_8UC3, Scalar(12));
	cout << "test4= " << endl << " " << test4 << endl << endl;
	Mat test5(test4);
	cout << "test5= " << endl << " " << test5 << endl << endl;
	Mat test6(3,3, CV_8UC3, test5.data);
	cout << "test6= " << endl << " " << test6 << endl << endl;
	Mat test11(4, 4, CV_8UC3, test5.data, 12);
	cout << "test11= " << endl << " " << test11 << endl << endl;

	Mat test7(2, sz2, CV_8UC3, test5.data);
	cout << "test7= " << endl << " " << test7 << endl << endl;
	const size_t  steps = 3;
	Mat test12(2, sz2, CV_8UC3, test5.data, &steps);
	cout << "test12= " << endl << " " << test12<< endl << endl;

	Mat test8(test7, Range(0,2), Range(0, 2));
	cout << "test8= " << endl << " " << test8 << endl << endl;
	Mat test9(test7, Rect(0,0, 3, 3));
	cout << "test8= " << endl << " " << test9 << endl << endl;
	Mat test10(test7, Range(0, 2));
	cout << "test10= " << endl << " " << test10 << endl << endl;

}

根据已有Mat创建Mat

利用已有mat创建一个新的Mat(注意有些是深度copy,有些只是创建了新的矩阵头,没有创建数据区域,后面再介绍),涉及到的Mat类中的方法有:

Method

Description

Mat row(int y)

取第y行作为新矩阵

Mat col(int x) 

取第x列作为新矩阵

Mat rowRange(int startrow, int endrow)

以starrow行起始点(0开始),到endrow行结束,产生新的Mat(不产生新的数据区域)

 Mat rowRange(const Range& r) 

同上,只是参数不同

Mat colRange(int startcol, int endcol)

以startcol 列为起点(0开始),到endcol列结束,产生新的Mat(不产生新的数据区域)

Mat colRange(const Range& r)

同上,只是参数不同

Mat diag(int d=0)

当d = 0时,取对角线

当d>0时,取对角线上部分

当d<0时,取对角线下部分

例如:

Mat m = (Mat_<int>(3,3) <<
                    1,2,3,
                    4,5,6,
                    7,8,9);
        Mat d0 = m.diag(0);
        Mat d1 = m.diag(1);
        Mat d_1 = m.diag(-1);

d0 =
       [1;
        5;
        9]
 d1 =
       [2;
        6]
  d_1 =
       [4;
        8]

static Mat diag(const Mat& d)

d代表的就是对角线部分 即产生新的对角矩阵

Mat clone() 

克隆一个新的Mat

 void copyTo( OutputArray m ) 

copy产生一个新的copy, 深度copy,会产生新的数据区域

void copyTo( OutputArray m, InputArray mask ) 

opy产生一个新的copy, 深度copy,其中mask中为0的不copy, 不为零的进行copy

上述这些操作只是产生新的mat,有些方法为深度copy,但是都没有修改Mat的type 以及shape

使用用例如下:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	int sz[3] = { 2,2,2 };
	Mat L(3, sz, CV_8UC1, Scalar::all(12));

	Mat mtx();

	Mat test1(640, 480, CV_8UC1);
	Mat test2(Size(640, 480), CV_8UC1);
	
	Mat M(2, 2, CV_8UC3, Scalar(110, 0, 255));
	cout << "M= " << endl << " " << M << endl << endl;
	Mat test3(Size(2, 2), CV_8UC3, Scalar(120, 0, 255));
	cout << "test3= " << endl << " " << test3 << endl << endl;

	int sz2[2] = { 5,5};
	Mat test4(2, sz2, CV_8UC3, Scalar(12));
	cout << "test4= " << endl << " " << test4 << endl << endl;
	Mat test5= test4.rowRange(1, 3);
	cout << "test5= " << endl << " " << test5 << endl << endl;
	Mat test6 = test4.rowRange(Range(0, 4));
	cout << "test6= " << endl << " " << test6 << endl << endl;
	Mat test7 = test4.colRange(0, 2);
	cout << "test7= " << endl << " " << test7 << endl << endl;
	Mat test8 = test4.colRange(Range(0, 3));
	cout << "test8= " << endl << " " << test8 << endl << endl;
	Mat test9 = test4.diag(0);
	cout << "test9= " << endl << " " << test9 << endl << endl;
	Mat test10 = test4.diag(1);
	cout << "test10= " << endl << " " << test10 << endl << endl;
	Mat test11 = test4.diag(-1);
	cout << "test11= " << endl << " " << test11 << endl << endl;
	Mat test12;
	//Mat test13(2, sz2, CV_8UC1, Scalar(12));
	test12 = test4.clone();
	cout << "test12= " << endl << " " << test12 << endl << endl;
	Mat test13;
	test4.copyTo(test13);
	cout << "test13= " << endl << " " << test13 << endl << endl;
	Mat test14;
	test4.copyTo(test14, test4);
	cout << "test14= " << endl << " " << test14 << endl << endl;

}

 产生新的Mat并修改旧Mat的type和shape

以下这些操作会根据旧的Mat生成新Mat,并会修改Mat的数据type以及shape

Method

Description

void convertTo( OutputArray m, int rtype, double alpha=1, double beta=0 ) 

转换Mat的数据类型,

OutputArray m:为转换后的矩阵

int rtype:为转换后的矩阵数据类型,注意通道数通最好一样,如果转换类型为负面的,转换后的类型和转换前类型一样

double alpha和double beta为防止转换后数据越界,而对数据进行缩放和偏移,其中alpha为缩放或者扩大因子,beta为偏移值,转换后结果起公式为:

\f[m(x,y) = saturate \_ cast<rType>( \alpha (*this)(x,y) +  \beta )\f]

Mat reshape(int cn, int rows=0)

重新设置Mat shape,设置前后的col * row * channel相等,否则转换失败或者和旧的一样

 Mat reshape(int cn, int newndims, const int* newsz)

同上,只是参数不一样

 Mat reshape(int cn, const std::vector<int>& newshape)

同上,只是参数不一样

 MatExpr t()

矩阵的转置

MatExpr inv(int method=DECOMP_LU)

矩阵的逆

 void resize(size_t sz)

修改Row大小

 void resize(size_t sz, const Scalar& s)

同上

使用用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	int sz[3] = { 2,2,2 };
	Mat L(3, sz, CV_8UC1, Scalar::all(12));

	Mat mtx();

	Mat test1(640, 480, CV_8UC1);
	Mat test2(Size(640, 480), CV_8UC1);
	
	Mat M(2, 2, CV_8UC3, Scalar(110, 0, 255));
	cout << "M= " << endl << " " << M << endl << endl;
	Mat test3(Size(2, 2), CV_8UC3, Scalar(120, 0, 255));
	cout << "test3= " << endl << " " << test3 << endl << endl;

	int sz2[2] = { 5,5};
	Mat test4(2, sz2, CV_8UC3, Scalar(12));
	cout << "test4= " << endl << " " << test4 << endl << endl;
	Mat test5;
	test4.convertTo(test5, CV_64FC3, 0.1, 1);
	cout << "test5= " << endl << " " << test5 << endl << endl;
	printf("test5 col: %d\n", test5.cols);
	Mat test6;
	test6 = test4.reshape(1);
	cout << "test6= " << endl << " " << test6 << endl << endl;
	printf("test6 col: %d\n", test6.cols);
	Mat test7;
	int sz3[2] = { 25,3 };
	
	test7 = test4.reshape(1, 2, sz3);
	cout << "test7= " << endl << " " << test7 << endl << endl;
	Mat test8 = test7.col(0);
	cout << "test8= " << endl << " " << test8 << endl << endl;
	Mat test9 = test7.col(1);
	cout << "test9= " << endl << " " << test9 << endl << endl;
	Mat test10 = test7.col(2);
	cout << "test10= " << endl << " " << test10 << endl << endl;
	Mat test11 = test7.t();
	cout << "test11= " << endl << " " << test11 << endl << endl;
	//test7.convertTo(test7, CV_8UC1, 10);
	Mat M2(5, 5, CV_32FC1, Scalar(110));
	cout << "M2= " << endl << " " << M2 << endl << endl;
	Mat test12 = M2.inv();
	cout << "test12= " << endl << " " << test12 << endl << endl;

        Mat test22;
	test22.create(16, 16, CV_8UC1);
	printf("Test22  col:%d, row:%d\n", test22.cols, test22.rows);
	//Mat test2;
	test22.resize(8);
	printf("Test22  col:%d, row:%d\n", test22.cols, test22.rows);
	test22.resize(4,Scalar::all(11));
	printf("Test22  col:%d, row:%d\n", test22.cols, test22.rows);
	cout << "test22= " << endl << " " << test22 << endl << endl;

}

create、zeros、ones、eye方法

Mat类还支持zeros,eye等类似python的创建矩阵方法

Method

Description

 void create(int rows, int cols, int type)

创建Mat.

1:如果当前mat shape和channel和要创建的shape和channel一样,则立即返回,否则就释放旧的Mat继续下一步.

2:初始化新创建mat 头

3:申请新的data数据,大小为total()\*elemSize()

4:申请新的counter,并设为1

void create(Size size, int type)

入参不一样,其他一样

 void create(int ndims, const int* sizes, int type)

入参不一样,其他一样

void create(const std::vector<int>& sizes, int type)

入参不一样,其他一样

 static MatExpr zeros(int rows, int cols, int type)

将Mat 初始化为零

static MatExpr zeros(Size size, int type)

同上,入参不同

static MatExpr zeros(int ndims, const int* sz, int type)

同上,入参不同

static MatExpr ones(int rows, int cols, int type)

初始化全部为1

static MatExpr ones(Size size, int type)

初始化全部为1

static MatExpr ones(int ndims, const int* sz, int type)

初始化全部为1

static MatExpr eye(int rows, int cols, int type)

生成眼图矩阵

static MatExpr eye(Size size, int type)

生成眼图矩阵

使用用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat test1;
	test1.create(320, 240, CV_8UC1);
	printf("Test1  col:%d, row:%d\n", test1.cols, test1.rows);
	Mat test2;
	test2.create(Size(320,240), CV_8UC1);
	printf("Test2 col:%d, row:%d\n", test2.cols, test2.rows);
	int sz[2] = {15, 15 };
	Mat test3;
	test3.create(2, sz, CV_8UC1);
	printf("Test3 col:%d, row:%d\n", test3.cols, test3.rows);
	vector<int> mat_size;
	mat_size.push_back(320);
	mat_size.push_back(240); 
	Mat test4;
	test4.create(mat_size, CV_8UC1);
	printf("Test4 col:%d, row:%d\n", test4.cols, test4.rows);
	Mat test5(2, sz, CV_8UC1, Scalar::all(12));
	cout << "test5= " << endl << " " << test5 << endl << endl;
	test5= Mat::zeros(2, sz, CV_8UC1);
	cout << "zero test5= " << endl << " " << test5 << endl << endl;
	Mat test6;
	test6 = Mat::zeros(3,4, CV_8UC1);
	cout << "zero test6= " << endl << " " << test6 << endl << endl;
	Mat test7 = Mat::zeros(5,6, CV_8UC1);
	cout << "zero test7= " << endl << " " << test7 << endl << endl;
	
	Mat test8 = Mat::ones(5, 6, CV_8UC1);
	cout << "one test8= " << endl << " " << test8 << endl << endl;
	Mat test9 = Mat::ones(Size(4, 7), CV_8UC1);
	cout << "one test9= " << endl << " " << test9 << endl << endl;
	Mat test10 = Mat::ones(2, sz, CV_8UC1);
	cout << "one test10= " << endl << " " << test10 << endl << endl;

	Mat test11 = Mat::eye(5, 6, CV_8UC1);
	cout << "eye test11= " << endl << " " << test11 << endl << endl;
	Mat test12 = Mat::eye(Size(7, 7), CV_8UC1);
	cout << "eye test12= " << endl << " " << test12 << endl << endl;
}

Mat类中获取系列操作

 Mat类中获取一系列参数操作,用于获取Mat的row,col等其他参数

Method

Description

size_t elemSize()

获取矩阵每个点的元素大小,起大小与数据类型和通道有关

比如 Mat 类型为CV_16SC3, 则元素大小为 3 *sizeof(short) = 6

size_t elemSize1() 

获取矩阵每个点的元素大小(注意忽略通道,只和数据类型有关)

比如Mat 类型为CV_16SC3,elemSize1为2

int type()

获取Mat type 

int depth()

获取Mat element depth(我的个人理解获取到的是Mat中的数据类型)

比如 18位 signed element array,返回值为CV_16S

其返回值共有:

  -   CV_8U = 0 - 8-bit unsigned integers ( 0..255 )
  -   CV_8S = 1 - 8-bit signed integers ( -128..127 )
    -   CV_16U = 2 - 16-bit unsigned integers ( 0..65535 )
    -   CV_16S  = 3 - 16-bit signed integers ( -32768..32767 )
    -   CV_32S  = 4 - 32-bit signed integers ( -2147483648..2147483647 )
    -   CV_32F  = 5 - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )
    -   CV_64F = 6- 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

   -   CV_USRTYPE1=7

int channels()

获取mat的 通道数

size_t step1(int i=0) 

每一维的通道数,具体详细讲解可以查看

bool empty()

mat是否为空,如果 Mat::total()为0,或者 Mat::data == null 则返回true

size_t total() 

Mat的像素总数,等于row * col,不包括通道数

size_t total(int startDim, int endDim=INT_MAX)

返回特定的维度的total number, startDim <= dim < endDim

int dims

Mat中的变量,可以用于获取到Mat维度

int rows, cols

Mat中的变量,Mat的行和列

uchar* data

指向data的指针

MatSize size

MatSize结构,可以获取到Mat的row,col,dim等

Mat test;

test.size()返回的结构体为Size

 

MatStep step

详解可以看:


int flags

包括几个标志位:

 - the magic signature
 - continuity flag,第14位
  - depth:第0,1,2位为depth
  - number of channels:第3到11位

  - submatrix:第15位:

使用用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{

	Mat test2;
	test2.create(Size(320,240), CV_8UC3);
	printf("Test2 col:%d, row:%d,dims:%d\n", test2.cols, test2.rows, test2.dims);
	printf("Test2 elemsize:%d, elemsize1:%d\n", test2.elemSize(), test2.elemSize1());
	printf("Test2 type:%d\n", test2.type());
	printf("Test2 depth:%d\n", test2.depth());
	printf("Test2 channels: %d\n", test2.channels());
	if (test2.empty())
	{
		printf("Test2 is empty\n");
	}
	else {
		printf("Test2 is not empty\n");
	}

	printf("Test2 total:%d\n", test2.total());
	printf("Test2 size:%d\n", *test2.size.p);
	printf("Test2 width:%d\n", test2.size().width);
	printf("Test2 height:%d\n", test2.size().height);
	printf("Test2 dim:%d\n", test2.size.dims());
	printf("Test2 height:%d\n", test2.size[0]);
	printf("Test2 width:%d\n", test2.size[1]);
   
	printf("Test2 step[0]:%d\n", test2.step[0]);
	printf("Test2 step[1]:%d\n", test2.step[1]);
}

Step

step概念是从Mat数组占用的空间角度来讲,在一个N维的矩阵运算中如果要取其某个元素的地址,其计算公式为:

opencv Mat 是什么操作 opencv mat函数_mat

 其中(i0, ii, … , iN d -1)为N维矩阵中,每个维度下的索引,如果是二维场景则:

opencv Mat 是什么操作 opencv mat函数_opencv_02

 mtx.step[0]为行相当于矩阵的col * channels* sizeof(data type),相当于在行中偏移的位置, 而mtx.step[1]为列 则为 channels* sizeof(data type) 相当于在列中的位置,Step概念就相当于在该维度下索引加1所需要移动的内存偏移量。

Step就脱离Mat中的shape概念,直接为内存角度考虑。

isContinuous函数

该函数表明Mat中的数据是否连续存储,连续存储是返回true,否则返回false。

Mat的数据部分存储是按照行进行存储的,相同行内的数据是连续存储,行与行之间的存储是否有间隙,可以通过isContinuous函数来判断。对于1*1或1*N矩阵总是连续的,一般使用Mat::create创建的矩阵总是连续的,而如果使用Mat::col,Mat::diag等提取的矩阵一部分,或者外部分配的数据构造矩阵头,则此类矩阵可能不再连续存储。

按照opencv中的解释说明,连续行标志存储是存储在Mat :: flags字段,理论上可以判断是否连续可以使用如下:

bool myCheckMatContinuity(const Mat& m)
{
    //return (m.flags & Mat::CONTINUOUS_FLAG) != 0;
    return m.rows == 1 || m.step == m.cols*m.elemSize();
}

在大部分对Mat数据进行遍历时都需要使用isContinuous,以及在对元素操作(例如算术和逻辑操作,数学函数,alpha混合,颜色空间变好等)不依赖于图像集合。因此,如果所有的输入和输出数组都是连续的,那么函数可以将它们处理非常长的单行向量。下面官方例子说明了如何实现一个alpha混合函数:

template<typename T>
        void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
        {
            const float alpha_scale = (float)std::numeric_limits<T>::max(),
                        inv_scale = 1.f/alpha_scale;

            CV_Assert( src1.type() == src2.type() &&
                       src1.type() == CV_MAKETYPE(traits::Depth<T>::value, 4) &&
                       src1.size() == src2.size());
            Size size = src1.size();
            dst.create(size, src1.type());

            // here is the idiom: check the arrays for continuity and,
            // if this is the case,
            // treat the arrays as 1D vectors
            if( src1.isContinuous() && src2.isContinuous() && dst.isContinuous() )
            {
                size.width *= size.height;
                size.height = 1;
            }
            size.width *= 4;

            for( int i = 0; i < size.height; i++ )
            {
                // when the arrays are continuous,
                // the outer loop is executed only once
                const T* ptr1 = src1.ptr<T>(i);
                const T* ptr2 = src2.ptr<T>(i);
                T* dptr = dst.ptr<T>(i);

                for( int j = 0; j < size.width; j += 4 )
                {
                    float alpha = ptr1[j+3]*inv_scale, beta = ptr2[j+3]*inv_scale;
                    dptr[j] = saturate_cast<T>(ptr1[j]*alpha + ptr2[j]*beta);
                    dptr[j+1] = saturate_cast<T>(ptr1[j+1]*alpha + ptr2[j+1]*beta);
                    dptr[j+2] = saturate_cast<T>(ptr1[j+2]*alpha + ptr2[j+2]*beta);
                    dptr[j+3] = saturate_cast<T>((1 - (1-alpha)*(1-beta))*alpha_scale);
                }
            }
        }

使用用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");
	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	Rect rect(1, 1,100,100);
	Mat crop_img = src(rect);
	imshow("crop_img", crop_img);
	waitKey(0);
	printf("Row:%d, Col:%d\n", crop_img.rows, crop_img.cols);
	printf("---crop_img.isContinuous=%d\n", crop_img.isContinuous());

	Mat crop_img2 = crop_img.clone();
	printf("---crop_img2.isContinuous=%d\n", crop_img2.isContinuous());
}

运行结果:

opencv Mat 是什么操作 opencv mat函数_mat_03

setTo方法

setTo函数为Mat类设置数据值的方法,主要是将所有或者部分的mat数据设置为所需要的值,函数原型为:

Mat& setTo(InputArray value, InputArray mask=noArray());

主要功能就是按照mask 非零的部分mat的数据值设置为所需要的值

如果不传递mask,将mask是一个与原图尺寸大小一致的且元素值全为非0的矩阵,因此不加mask的时候会将原矩阵的像素值全部赋值为value.

使用用例如下:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src(4, 4, CV_8UC1, Scalar(23));
	cout << "src= " << endl << " " << src << endl << endl;
	Mat mask(4, 4, CV_8UC1, Scalar(0));
	src.setTo(100, mask);
	cout << "src= " << endl << " " << src << endl << endl;
	Mat mask2(4, 4, CV_8UC1, Scalar(1));
	src.setTo(100, mask2);
	cout << "src= " << endl << " " << src << endl << endl;
	src.setTo(20, src<200);
	cout << "src= " << endl << " " << src << endl << endl;
}

运行结果:

opencv Mat 是什么操作 opencv mat函数_opencv Mat 是什么操作_04

ptr指针

opencv 提供了丰富的获取Mat数据接口,主要是通过ptr指针获取到每行的首地址来进行对数据的访问,因为每行之间如果不是连续存储模式中间会存在空洞,故对Mat数据访问时一般都是按照行进行访问,ptr相关接口如下

Method

Description

uchar* ptr(int i0=0)

获取到Mat中数据矩阵的第i0行首地址,此时数据类型为uchar

const uchar* ptr(int i0=0)

返回的数据类型为const uchar,即只能读取不能修改值

uchar* ptr(int row, int col)

针对Mat二维空间,返回第row行和第cok列的元素地址

const uchar* ptr(int row, int col) 

同上,返回地址不能修改其值

uchar* ptr(int i0, int i1, int i2)

Mat为三维空间,返回其三维空间某个元素地址

 const uchar* ptr(int i0, int i1, int i2) 

同上,返回的地址 其值不能修改

uchar* ptr(const int* idx)

各个维度中的位置用数组来表示

const uchar* ptr(const int* idx)

各个维度中的位置用数组来表示

 template<int n> uchar* ptr(const Vec<int, n>& idx)

各个维度中的位置用vector来表示

template<int n> const uchar* ptr(const Vec<int, n>& idx)

各个维度中的位置用vector来表示

template<typename _Tp> _Tp* ptr(int i0=0)

返回第id0行 首地址吗,其数据类型和Tp相关

template<typename _Tp> const _Tp* ptr(int i0=0)

和uchar* ptr(int i0=0)类似,差别在与Tp数据类型

 template<typename _Tp> _Tp* ptr(int row, int col)

和uchar* ptrint row, int col),差别在与Tp数据类型

 template<typename _Tp> const _Tp* ptr(int row, int col)

和const  uchar*ptr(int row, int col),差别在与Tp数据类型

template<typename _Tp> _Tp* ptr(int i0, int i1, int i2)

和 uchar* ptr(int i0, int i1, int i2),差别在与Tp数据类型

 template<typename _Tp> const _Tp* ptr(int i0, int i1, int i2)

和 uchar* ptr(int i0, int i1, int i2),差别在与Tp数据类型

 template<typename _Tp> _Tp* ptr(const int* idx)

和uchar* ptr(const int* idx),差别在与Tp数据类型

template<typename _Tp> const _Tp* ptr(const int* idx)

和const uchar* ptr(const int* idx),差别在与Tp数据类型

template<typename _Tp, int n> _Tp* ptr(const Vec<int, n>& idx)

和uchar* ptr(const Vec<int, n>& idx),差别在与Tp数据类型

 template<typename _Tp, int n> const _Tp* ptr(const Vec<int, n>& idx)

和const uchar* ptr(const Vec<int, n>& idx),差别在与Tp数据类型

ptr机制单通道机制访问

单通道 uchar类型的Mat数据访问例子如下:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);
	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
	{
		uchar *RowPtr = src.ptr(RowIndex);
		for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
		{
			printf("Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, *(RowPtr + ColIndex));
		}
	}
}

ptr多通道机制访问

ptr多通道机制访问也有两种:

第一种:采用像c指针一样,开发人员根据需要自己来算偏移,需要考虑多通道问题

第二种:使用vec类型指针,ptr模板支持vec数据类型

三通道 BGR Mat数据进行遍历用例1

三通道BGR ptr访问方法,开发人员开做多通道偏移:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");
	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
	{
		uchar *RowPtr = src.ptr(RowIndex);
		for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
		{
			printf("Row:%d, Col %d, B: value:%d\n", RowIndex, ColIndex, *(RowPtr + 3* ColIndex));
			printf("Row:%d, Col %d, R: value:%d\n", RowIndex, ColIndex, *(RowPtr + 3 * ColIndex + 1));
			printf("Row:%d, Col %d, G: value:%d\n", RowIndex, ColIndex, *(RowPtr + 3 * ColIndex + 2));
		}
	}
}

三通道 BGR Mat数据进行遍历用例1

三通道BGR ptr使用模板访问方法,其模板数据类型为vec:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");
	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
	{
		cv::Vec3b * RowPtr = src.ptr<cv::Vec3b>(RowIndex);
		for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
		{
			printf("Row:%d, Col %d, B: value:%d\n", RowIndex, ColIndex, RowPtr[ColIndex][0]);
			printf("Row:%d, Col %d, R: value:%d\n", RowIndex, ColIndex, RowPtr[ColIndex][1]);
			printf("Row:%d, Col %d, G: value:%d\n", RowIndex, ColIndex, RowPtr[ColIndex][2]);
		}
	}
}

Mat类中的at机制

访问Mat数据时,为了防止使用ptr机制进行指针偏移错误,Mat提供了at机制,用于对Mat数据中的某个元素进行访问,at机制消除了对指针操作的偏移出现的问题,也为不习惯使用指针的开发人员提供了极大遍历,其主要接口如下:

Method

Description

template<typename _Tp> _Tp& at(int i0=0)

Mat数组为一维场景下,对某个元素进行访问,可修改其值

template<typename _Tp> const _Tp& at(int i0=0)

同上,不可修改其值

template<typename _Tp> _Tp& at(int row, int col)

Mat数组为二维场景下,对某个元素进行访问,可修改其值

template<typename _Tp> const _Tp& at(int row, int col)

同上,不可修改其值

template<typename _Tp> _Tp& at(int i0, int i1, int i2)

Mat数组为三维场景下,对某个元素进行方案,可修改其值

template<typename _Tp> const _Tp& at(int i0, int i1, int i2)

同上,不可修改其值

template<typename _Tp> _Tp& at(const int* idx)

各个维度下的位置用数组来表示,可修改元素值

template<typename _Tp> const _Tp& at(const int* idx)

同上,不可修改元素值

template<typename _Tp, int n> _Tp& at(const Vec<int, n>& idx)

各个维度的位置用vector来表示,可修改元素值

template<typename _Tp, int n> const _Tp& at(const Vec<int, n>& idx)

同上,不可修改元素值

 template<typename _Tp> _Tp& at(Point pt)

Mat二维场景下,使用Point参数传递

template<typename _Tp> const _Tp& at(Point pt)

同上,不可修改元素值

根据Opencv源代码中的说明,Mat中的数据类型对应的其Tp类型

  •      - If matrix is of type `CV_8U` then use `Mat.at<uchar>(y,x)`.
  •      - If matrix is of type `CV_8S` then use `Mat.at<schar>(y,x)`.
  •      - If matrix is of type `CV_16U` then use `Mat.at<ushort>(y,x)`.
  •      - If matrix is of type `CV_16S` then use `Mat.at<short>(y,x)`.
  •      - If matrix is of type `CV_32S`  then use `Mat.at<int>(y,x)`.
  •      - If matrix is of type `CV_32F`  then use `Mat.at<float>(y,x)`.
  •      - If matrix is of type `CV_64F` then use `Mat.at<double>(y,x)`.

   at机制单通道访问

单通道uchar 类型Mat变量访问用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);
	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
	{
		for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
		{
			printf("Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, ColIndex));
		}
	}
}

利用at机制修改其Mat中的数据值:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{;
    Mat Src(10, 10, CV_8UC1);
	for (int RowIndex = 0; RowIndex < Src.rows; RowIndex++)
	{
		for (int ColIndex = 0; ColIndex < Src.cols; ColIndex++)
		{
			Src.at<unsigned char>(RowIndex, ColIndex)= ColIndex;
		}
	}

	cout << "Src = " << endl << Src << endl;
}

at机制多通道访问

如果Mat矩阵为多通道数据,使用数据方式有两种:

第一种是在遍历是,填写列时由开发人员自己根据一个元素占用的内存大小进行偏移,其计算方法为col*channel通道数,该方法的缺点在与需要开发人员自己算通道内偏移,比较麻烦

第二种at中的模板数据格式使用相应的vec格式,有点访问遍历使用方便

三通道 BGR Mat数据进行遍历用例1

第一个三通道 BGR Mat数据进行遍历用例,显示其通道数有开发人员自己做偏移:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");
	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
	{
		uchar *RowPtr = src.ptr(RowIndex);
		for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
		{
			printf("B Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, 3*ColIndex));
			printf("G Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, 3 * ColIndex + 1));
			printf("R Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<unsigned char>(RowIndex, 3 * ColIndex + 2));
		}
	}
}

三通道 BGR Mat数据进行遍历用例2

at模板中使用vec数据结构用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");
	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	for (int RowIndex = 0; RowIndex < src.rows; RowIndex++)
	{
		uchar *RowPtr = src.ptr(RowIndex);
		for (int ColIndex = 0; ColIndex < src.cols; ColIndex++)
		{
			printf("B Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<cv::Vec3b>(RowIndex, ColIndex)[0]);
			printf("G Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<cv::Vec3b>(RowIndex, ColIndex)[1]);
			printf("R Row:%d, Col %d, value:%d\n", RowIndex, ColIndex, src.at<cv::Vec3b>(RowIndex, ColIndex)[2]);
		}
	}
}

 

 MatIterator迭代器

Mat提供了迭代器遍历接口,对迭代器熟悉的开发人员可以方便使用,主要接口如下:

Method

Description

template<typename _Tp> MatIterator_<_Tp> begin()

获取Mat初始迭代器

 template<typename _Tp> MatConstIterator_<_Tp> begin()

同上,不可修改其内容

 template<typename _Tp> MatIterator_<_Tp> end()

获取Mat最后迭代器

template<typename _Tp> MatConstIterator_<_Tp> end()

同上,不可修改其内容

单通道元素迭代

单通道uchar Mat遍历使用用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);

	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	Mat_<uchar>::iterator SrcStart = src.begin<uchar>();
	Mat_<uchar>::iterator SrcEnd   = src.end<uchar>();
	int number = 0;
	for (;SrcStart != SrcEnd; ++SrcStart)
	{
		uchar data = *SrcStart;

		printf("Image index:%d, value:%d\n", number, data);
		number++;
	}

	printf("Image count:%d\n", number);
}

多通道元素迭代

如果是其他多通道数据 ,其迭代数据结构可以使用如下opencv 定义好的数据:

typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;

typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;

typedef Vec<ushort, 2> Vec2w;
typedef Vec<ushort, 3> Vec3w;
typedef Vec<ushort, 4> Vec4w;

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

三通道BGR迭代数据访问用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");

	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	Mat_<cv::Vec3b>::iterator SrcStart = src.begin<cv::Vec3b>();
	Mat_<cv::Vec3b>::iterator SrcEnd   = src.end<cv::Vec3b>();
	int number = 0;
	for (;SrcStart != SrcEnd; ++SrcStart)
	{
		printf("Image index:%d, B value:%d\n", number, (*SrcStart)[0]);
		printf("Image index:%d, G value:%d\n", number, (*SrcStart)[1]);
		printf("Image index:%d, R value:%d\n", number, (*SrcStart)[2]);
		number++;
	}

	printf("Image count:%d\n", number);
}

MatIterator迭代器为Mat遍历访问的比较重要方式之一。

官方给出了对RGBA的 alpha blending处理办法:

template<typename T>
        void alphaBlendRGBA(const Mat& src1, const Mat& src2, Mat& dst)
        {
            typedef Vec<T, 4> VT;

            const float alpha_scale = (float)std::numeric_limits<T>::max(),
                        inv_scale = 1.f/alpha_scale;

            CV_Assert( src1.type() == src2.type() &&
                       src1.type() == traits::Type<VT>::value &&
                       src1.size() == src2.size());
            Size size = src1.size();
            dst.create(size, src1.type());

            MatConstIterator_<VT> it1 = src1.begin<VT>(), it1_end = src1.end<VT>();
            MatConstIterator_<VT> it2 = src2.begin<VT>();
            MatIterator_<VT> dst_it = dst.begin<VT>();

            for( ; it1 != it1_end; ++it1, ++it2, ++dst_it )
            {
                VT pix1 = *it1, pix2 = *it2;
                float alpha = pix1[3]*inv_scale, beta = pix2[3]*inv_scale;
                *dst_it = VT(saturate_cast<T>(pix1[0]*alpha + pix2[0]*beta),
                             saturate_cast<T>(pix1[1]*alpha + pix2[1]*beta),
                             saturate_cast<T>(pix1[2]*alpha + pix2[2]*beta),
                             saturate_cast<T>((1 - (1-alpha)*(1-beta))*alpha_scale));
            }
        }

上述迭代器为Mat内的元素迭代,还有另外一种迭代器NAryMatIterator,该迭代器迭代的是Mat.

利用data进行Mat数据遍历

如果Mat中的数据是连续存储的,还有另外一种不是很常见的方法对Mat中 的数据进行遍历,利用Mat中的data变量,指向的数数据区域的的指针进行遍历访问。

data数据偏移

单通道用例如下:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);

	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	uchar * Data = src.data;
	
	for (int RowIndex=0; RowIndex <  src.rows; ++RowIndex)
		for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
	{
	    printf("Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols + ColIndex));
	}


}

三通道BGR用例如下:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");

	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	uchar * Data = src.data;
	
	for (int RowIndex=0; RowIndex <  src.rows; ++RowIndex)
		for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
	{
	    printf("B : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols*3 + ColIndex));
		printf("G : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex + 1));
		printf("R : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex + 2));
	}
}

data偏移使用step

data变量实际上还有另外一种用法,实际上可以使用step,前面有讲到step的实际意义,使用起来更加方便

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

void main()
{
	Mat src = imread("len_top.jpg");

	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);
	uchar * Data = src.data;

	for (int RowIndex = 0; RowIndex < src.rows; ++RowIndex)
		for (int ColIndex = 0; ColIndex< src.cols; ++ColIndex)
		{
			printf("B : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.step[0] + ColIndex * src.step[1]));
			printf("G : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.step[0] + ColIndex * src.step[1] + 1));
			printf("R : Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.step[0] + ColIndex * src.step[1] + 2));
		}
}

 

Mat 数据遍历总结

对Mat中的数据遍历进行操作主要有几个方法:

1:利用ptr进行操作

2:利用at进行遍历操作

3:利用Mat迭代器

4:如果是连续存储可以直接利用data进行遍历访问 

5:利用forEach方法,该方法是利用并行方法,所以是最快的,它是是通过forEach的函数Operator来实现

forEach

forEach方法充分应用了整个计算机的硬件特性,利用并行原理最大发挥了CPU和核数,是指能够发挥最大性能,其API如下:

Method

Description

template<typename _Tp, typename Functor> void forEach(const Functor& operation)

operation函数由开发者来实现具体操作

template<typename _Tp, typename Functor> void forEach(const Functor& operation) const

同上

 forEach的实现主要是通过 operation来实现具体每个像素的计算

单通道将每个元素值修改为1的简单例子:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

typedef uint8_t Pixel;

void complicated(Pixel &pixel)
{
	pixel = 1;
}
// Parallel execution with function object.
struct Operator
{
	void operator ()(Pixel &pixel, const int * position) const
	{
		// Perform a simple threshold operation
		complicated(pixel);
	}
};

void main()
{
	Mat src = imread("len_top.jpg", IMREAD_GRAYSCALE);

	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);

	src.forEach<Pixel>(Operator());

	uchar * Data = src.data;
	
	for (int RowIndex=0; RowIndex <  src.rows; ++RowIndex)
		for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
	{
	    printf("Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols + ColIndex));
	}
}

三通道BRG将B通道全部修改为1用例:

 

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;

typedef Point3_<uint8_t> Pixel;

void complicated(Pixel &pixel)
{
	pixel.x = 1;
}
// Parallel execution with function object.
struct Operator
{
	void operator ()(Pixel &pixel, const int * position) const
	{
		// Perform a simple threshold operation
		complicated(pixel);
	}
};

void main()
{
	Mat src = imread("len_top.jpg");

	imshow("Src", src);
	waitKey(0);
	printf("---src.isContinuous=%d\n", src.isContinuous());
	printf("Row:%d, Col:%d\n", src.rows, src.cols);
	printf("Type:%d\n", src.type());
	printf("CV_8UC3:%d\n", CV_8UC3);
	printf("CV_8UC1:%d\n", CV_8UC1);

	src.forEach<Pixel>(Operator());

	uchar * Data = src.data;
	
	for (int RowIndex=0; RowIndex <  src.rows; ++RowIndex)
		for(int ColIndex=0;ColIndex< src.cols; ++ColIndex)
	{
	    printf("B Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex * 3));
	    printf("G Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex * 3 + 1));
	    printf("R Row: %d, Col: %d, Value: %d\n", RowIndex, ColIndex, *(Data + RowIndex * src.cols * 3 + ColIndex * 3 + 2));
	}
}

operator =重载

opencv Mat类利用C+++ operator特性来完成对两个Mat之间的赋值操作,可以看到起等号赋值重载如下:

Method

Description

 Mat& operator = (const Mat& m)

m为被赋值的右侧矩阵,矩阵的赋值操作复杂度为O(1),意味着其赋值过程中没有数据段赋值,并且两个矩阵使用的是同一个计数器。在给矩阵赋新数据之前由Mat:release()释放引用

 Mat& operator = (const MatExpr& expr)

expr为矩阵表达式.具体可以查看OpenCV Mat主要用法(2)_MatExpr详细了解

使用一个简单的例子验证赋值操作并没有产生新的数据区:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;


void main()
{
	Mat src(3, 3, CV_8UC1, Scalar(0));
	
	cout << "Src= " << endl << src << endl;
	Mat  dst = src;

	dst.at<unsigned char>(0, 0) = 1;
	dst.at<unsigned char>(0, 1) = 2;
	dst.at<unsigned char>(0, 2) = 3;
	cout << "dst = " << endl << dst << endl;
	cout << "Src= " << endl << src << endl;

}

修改过赋值之后的Mat矩阵中的值,其源矩阵的值也同样进行了修改,因为并没有产生新的数据区域。

运行结果:

opencv Mat 是什么操作 opencv mat函数_opencv_05

 

向量内积(点乘)dot

向量的点乘,也叫向量的内积、数量积,对两个向量执行点乘运算,就是对这两个向量对应位一一相乘之后求和的操作,点乘的结果是一个标量。

对于向量a和向量b:

opencv Mat 是什么操作 opencv mat函数_opencv Mat 是什么操作_06

    

opencv Mat 是什么操作 opencv mat函数_opencv_07

      a和b的点积公式为:

opencv Mat 是什么操作 opencv mat函数_#include_08

要求一维向量a和向量b的行列数相同。

对于此概念不清楚的,可以查看

对于Mat而言,如果是两个多维矩阵计算点乘,Mat会将这个矩阵扩展成一列,转换成一维之后,然后在执行向量点乘,向量点乘的结果为一个标量。例如一个M,N的二维矩阵会将其转换成1, M*N列的矩阵然后做点乘运算。

如果Mat是多通道的矩阵,先计算各个通道先独立计算dot之后,计算各个通道dot结果和

Mat的点乘API 为dot:

Method

Description

double dot(InputArray m)

m为做点乘的另外一个矩阵

一个Mat 点乘的简单例子:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;


void main()
{
	Mat A(3, 3, CV_8UC1, Scalar(0));
	Mat B(3, 3, CV_8UC1, Scalar(0));
	
	A.at<unsigned char>(0, 0) = 1;
	A.at<unsigned char>(0, 1) = 2;
	A.at<unsigned char>(0, 2) = 3;
	A.at<unsigned char>(1, 0) = 4;
	A.at<unsigned char>(1, 1) = 5;
	A.at<unsigned char>(1, 2) = 6;
	A.at<unsigned char>(2, 0) = 7;
	A.at<unsigned char>(2, 1) = 8;
	A.at<unsigned char>(2, 2) = 9;


	B.at<unsigned char>(0, 0) = 3;
	B.at<unsigned char>(0, 1) = 2;
	B.at<unsigned char>(0, 2) = 1;
	B.at<unsigned char>(1, 0) = 6;
	B.at<unsigned char>(1, 1) = 5;
	B.at<unsigned char>(1, 2) = 4;
	B.at<unsigned char>(2, 0) = 9;
	B.at<unsigned char>(2, 1) = 8;
	B.at<unsigned char>(2, 2) = 7;
	cout << "A = " << endl << A<< endl;
	cout << "B= " << endl << B << endl;
	cout << "A.dot(B)= " << endl << A.dot(B) << endl;

}

运行结果:

opencv Mat 是什么操作 opencv mat函数_初始化_09

 Mat矩阵mul

OpenCV mat同样支持两个矩阵相对应位的乘积,以简单的情况为例,对于2*2大小的Mat矩阵A和B:

opencv Mat 是什么操作 opencv mat函数_opencv Mat 是什么操作_10

 对A和B执行mul运算:

opencv Mat 是什么操作 opencv mat函数_opencv Mat 是什么操作_11

需要说明的A和B两个矩阵维度,通道数等必须相同 。

Method

Description

 MatExpr mul(InputArray m, double scale=1)

m为另外一个矩阵,scale为结果比例缩放因子,防止结果越界

下面为一个简单的mul使用用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;


void main()
{
	Mat A(3, 3, CV_8UC1, Scalar(0));
	Mat B(3, 3, CV_8UC1, Scalar(0));
	
	A.at<unsigned char>(0, 0) = 1;
	A.at<unsigned char>(0, 1) = 2;
	A.at<unsigned char>(0, 2) = 3;
	A.at<unsigned char>(1, 0) = 4;
	A.at<unsigned char>(1, 1) = 5;
	A.at<unsigned char>(1, 2) = 6;
	A.at<unsigned char>(2, 0) = 7;
	A.at<unsigned char>(2, 1) = 8;
	A.at<unsigned char>(2, 2) = 9;


	B.at<unsigned char>(0, 0) = 3;
	B.at<unsigned char>(0, 1) = 2;
	B.at<unsigned char>(0, 2) = 1;
	B.at<unsigned char>(1, 0) = 6;
	B.at<unsigned char>(1, 1) = 5;
	B.at<unsigned char>(1, 2) = 4;
	B.at<unsigned char>(2, 0) = 9;
	B.at<unsigned char>(2, 1) = 8;
	B.at<unsigned char>(2, 2) = 7;
	cout << "A = "<<endl<<  A<< endl;
	cout << "B= "  <<endl<< B << endl;
	cout << "A.mul(B)= "  <<endl<< A.mul(B) << endl;

}

运行结果:

opencv Mat 是什么操作 opencv mat函数_mat_12

 矩阵相乘

OpenCV mat同样支持两个矩阵相乘,矩阵的相乘主要是通过MatExpr中重载*来实现的,后面再详细描述

两个2*2两维度矩阵相乘如下:

opencv Mat 是什么操作 opencv mat函数_opencv_13

如果具体对矩阵相乘的公式不懂,可以复习下大学的线性代数 

下面来看一个矩阵相乘例子:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;


void main()
{
	Mat A(3, 3, CV_32FC1);
	Mat B(3, 3, CV_32FC1);
	
	A.at<float>(0, 0) = 1;
	A.at<float>(0, 1) = 2;
	A.at<float>(0, 2) = 3;
	A.at<float>(1, 0) = 4;
	A.at<float>(1, 1) = 5;
	A.at<float>(1, 2) = 6;
	A.at<float>(2, 0) = 1;
	A.at<float>(2, 1) = 2;
	A.at<float>(2, 2) = 3;


	B.at<float>(0, 0) = 3;
	B.at<float>(0, 1) = 2;
	B.at<float>(0, 2) = 1;
	B.at<float>(1, 0) = 6;
	B.at<float>(1, 1) = 5;
	B.at<float>(1, 2) = 4;
	B.at<float>(2, 0) = 1;
	B.at<float>(2, 1) = 2;
	B.at<float>(2, 2) = 3;
	cout << "A = "<<endl<<  A<< endl;
	cout << "B= "  <<endl<< B << endl;
	cout << "A*B = "  <<endl<< A*B << endl;

}

运行结果

 

opencv Mat 是什么操作 opencv mat函数_opencv Mat 是什么操作_14

矩阵的叉乘Cross

矩阵的叉乘,其实是说的两个向量的叉乘,矩阵是不能叉乘的。做叉乘的两个矩阵必须是三元素的向量。

公式:|c|=|a×b|=|a||b|sin<a,b>

几何意义:叉乘的结果是一个向量。方向垂直于原先两个向量构成的平面。

举例:a=[1,2,3],b=[4,5,6],则cross(a,b)=[-3 6 -3]。它表示的意思是三维空间中的两个点A(1,2,3)和B(4,5,6),再加上原点O,则构成的两个向量OA,OB,则cross(a,b)就是垂直平面OAB的向量。

计算方法:

         给定直角坐标系的单位向量i,j,k满足下列等式:

i×j=k;

j×k=i;

k×i=j;

通过这些规则,两个向量的叉积的坐标可以方便地计算出来,不需要考虑任何角度:设

a=[a1,a2,a3]=a1i+a2j+a3k;

b=[b1,b2,b3]=b1i+b2j+b3k;

则a×b=[a2b3-a3b2,a3b1-a1b3,a1b2-a2b1]。

计算公式为:

opencv Mat 是什么操作 opencv mat函数_opencv Mat 是什么操作_15

 Mat 做叉乘API为:

opencv Mat 是什么操作 opencv mat函数_mat_16

opencv Mat做叉乘必须是浮点型 

使用用例:

#include <stdio.h>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;
using namespace std;


void main()
{
	Mat A(1, 3, CV_32FC1);
	Mat B(1, 3, CV_32FC1);
	
	A.at<float>(0, 0) = 1;
	A.at<float>(0, 1) = 2;
	A.at<float>(0, 2) = 3;
	


	B.at<float>(0, 0) = 3;
	B.at<float>(0, 1) = 2;
	B.at<float>(0, 2) = 1;
	
	cout << "A = "<<endl<<  A<< endl;
	cout << "B= "  <<endl<< B << endl;
	cout << "A.cross(B)= "  <<endl<< A.cross(B)<< endl;
	

}

运行结果:

opencv Mat 是什么操作 opencv mat函数_初始化_17