Opencv Mat 类 (一)

我有一个梦想,我写的代码,可以像诗一样优美。我有一个梦想,我做的设计,能恰到好处,既不过度,也无不足。

opencv,一直零零散散的看着,总没有沉下心来总结。
正好手里的项目涉及到了opencv的相关内容,趁着这个机会,总结回顾下,也算是巩固知识点了(总结的不对的地方,欢迎各位指正)。

opencv中一个很重要的点就是Mat类了,当然回顾也应该从这里开始,话不多说,上干货!
先上代码,再一一过。

void useMat()
{
	Mat img = imread("1.jpg", IMREAD_COLOR);//加载图片
	if (img.empty())
	{
		cout << "read picture failed";
		return;
	}

	cout << "flags = " << img.flags << endl;
	cout << "dims = " << img.dims << endl;//矩阵维度
	cout << "rows = " << img.rows << endl;//行数
	cout << "cols = " << img.cols << endl;//列数
	cout << "depths = " << img.depth() << endl;//深度
	
	cout << "channels = " << img.channels() << endl;//通道数
	cout << "iscontinuous = " << img.isContinuous() << endl;
	cout << "isSubMatrix() = " << img.isSubmatrix() << endl;
	cout << "type() = " << img.type() << endl;//类型


	Mat mat(2, 3, CV_64FC2, Scalar(1.0, 3.0));
	cout << "mat = " << endl << mat << endl;
	cout << "rows = " << mat.rows << endl;
	cout << "cols = " << mat.cols << endl;
	cout << "channel = " << mat.channels() << endl;
	cout << "depths = " << mat.depth() << endl;

	cout << "total = " << mat.total() << endl;//元素总个数
	cout << "total0 = " << mat.total(0, 1) << endl;//维度的行数
	cout << "total1 = " << mat.total(1, 2) << endl;//维度的列数
	cout << "elemSize = " << mat.elemSize() << endl;//一个元素包含多少字节(所有通道);
	cout << "elemSize1 = " << mat.elemSize1() << endl;//一个元素的一个通道包含多少字节
	cout << "step0 = " << mat.step1(0) << endl;//一行所有通道的元素数
	cout << "step1 = " << mat.step1(1) << endl;//一个点的所有通道的元素数
	}

好,现在来梳理:
img.flags ,来看文档:

/*! includes several bit-fields:
         - the magic signature
         - continuity flag
         - depth
         - number of channels
     */
    int flags;

32位整型,按位存放了以上相关信息,跟踪opencv的代码,不难发现opencv获取相关信息是依照位运算来获取,举个例子:

int Mat::depth() const
{
    return CV_MAT_DEPTH(flags);
}

//CV_MAT_DEPTH(flags);
#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)

此变量,不多赘述,不用硬记,知道即可,使用时再按照需求详细了解,网上随便就可以搜到相关的32位 结构图示。

img.dims 矩阵维度,>= 2;特别的当维度大于2的时候,rows和cols就都为-1了;
rows 、cols、channels()、depth()、type()分别对应的行、列、通道数、深度、类型,这些基础的点接触opencv都会首先了解(就像背英语单词,第一个总是那么熟悉,abandon(放弃-_-)),所以不再此过多停顿。
下面进入xue微有点绕的环节:

/** @brief Returns the total number of array elements.

    The method returns the number of array elements (a number of pixels if the array represents an
    image).*/
mat.total() ;

size_t Mat::total() const
{
    if( dims <= 2 )
        return (size_t)rows * cols;
    size_t p = 1;
    for( int i = 0; i < dims; i++ )
        p *= size[i];
    return p;
}

此函数返回的是数组的元素总个数,在数组表示图片的时候即维度小于等于2的时候,直接返回rowscols,注意:并没有乘以通道数(强调这点是因为下面会涉及通道数的问题,防止混淆);当维度大于2的时候,则遍历每一维度,将尺寸全部相乘。
ps:MatSize size;存放每一维度的尺寸,包含一个成员变量int
p;
size[i] = p[i];
举个例子,Mat mat(2, 3, CV_8UC2);
size[0] = p[0] = 2;
size[1] = p[1] = 3;

for( int i = _dims-1; i >= 0; i-- )
    {
        int s = _sz[i];//_sz--> (2, 3)
        CV_Assert( s >= 0 );
        m.size.p[i] = s;
     }

mat.total(0, 1);
/*Returns the total number of array elements.

     The method returns the number of elements within a certain sub-array slice with startDim <= dim < endDim*/
size_t Mat::total(int startDim, int endDim) const
{
    CV_Assert( 0 <= startDim && startDim <= endDim);
    size_t p = 1;
    int endDim_ = endDim <= dims ? endDim : dims;
    for( int i = startDim; i < endDim_; i++ )
        p *= size[i];
    return p;
}

在给定的起始维度范围内返回元素的总数量,当数据为二维矩阵的时候,Mat mat(2, 3, CV_8UC2);mat.total(0, 1),返回第一个维度的元素数量,在此代码中即为2,即行数;同理可得列数,了解上面MatSize,则此处理解起来就容易多了。


/** @brief Returns the matrix element size in bytes.

    The method returns the matrix element size in bytes. For example, if the matrix type is CV_16SC3 ,
    the method returns 3\*sizeof(short) or 6.
     */
mat.elemSize();
size_t Mat::elemSize() const
{
    size_t res = dims > 0 ? step.p[dims - 1] : 0;
    CV_DbgAssert(res != 0);
    return res;
}

首先说明这个函数,按照注释:此函数返回的是以字节为单位,返回矩阵元素的大小,注意单个元素要考虑多通道的因素,注释中举得例子:CV_16SC3,则函数返回的是sizeof(short) * 3(通道数)。
OK,这点又引出来一个成员变量MatStep step;
step存放步长字节数,包含一个成员变量p(是不是感觉似曾相识,想想上面的MatSize);
同样的该类中也重写了[]符,step[i] = step.p[i];

size_t esz = CV_ELEM_SIZE(m.flags), esz1 = CV_ELEM_SIZE1(m.flags), total = esz;
    for( int i = _dims-1; i >= 0; i-- )
    {
   			int s = _sz[i];
       .......
            m.step.p[i] = total;
            int64 total1 = (int64)total*s;
            if( (uint64)total1 != (size_t)total1 )
                CV_Error( CV_StsOutOfRange, "The total matrix size does not fit to \"size_t\" type" );
            total = (size_t)total1;
        ....
    }

为了方便说明该变量的含义,只保留了相关的一部分代码,一句一句来,首先:
size_t esz = CV_ELEM_SIZE(m.flags);
esz是通过对flags进行位运算取得矩阵元素的大小(以字节为单位),即上文的elemSize();
按照维度倒叙遍历,按原文代码,则_dims = 2;则第一个循环,i= _dims - 1 = 1;
64位浮点数的字节数为8;
m.step.p[1] = total = esz = 16 = 8 * 2;
接着往下走,s在上面已经取得为列数,total = total * s;
啥意思?
意思是total此时表示的是一行元素的大小,此时循环进入下一层,i = 0;
m.step.p[0] = total = 16 * 3 = 48;
看到这里,不要乱,明确了step的含义,对于二维矩阵来说,step[0]存放的是一行元素的大小(以字节为单位),step[1] 存放的是一个元素的大小(以字节为单位);所以elemSize()返回的是step[1],即一个元素的字节大小。


mat.elemSize1() ;

/** @brief Returns the size of each matrix element channel in bytes.

    The method returns the matrix element channel size in bytes, that is, it ignores the number of
    channels. For example, if the matrix type is CV_16SC3 , the method returns sizeof(short) or 2.
     */
size_t Mat::elemSize1() const
{
    return CV_ELEM_SIZE1(flags);
}

以字节为单位,返回每个元素的单个通道的大小。原文中应返回8。


mat.step1(0);
mat.step1(1);
size_t Mat::step1(int i) const
{
    return step.p[i] / elemSize1();
}

注释写的是通过除以Mat::elemSize1()返回一个矩阵的步长,用于快速访问矩阵元素。
这一点要插一句,这点需要了解下Mat的构成,举个例子说明下:
Mat mte = (Mat_(2, 2) << (0, 0), (1, 1), (2, 2), (3, 3));
矩阵应为:
【0, 0, 1, 0,
2, 0, 3, 0】;
(Mat的模板类Mat_类的初始化方式以后再讲下,这里先不做涉及,只是初始化的方式了解下,矩阵并不是
【0, 0,1, 1,
2, 2, 3, 3】;
此时step1(0)返回的应为m.step.p[0] / elemSIze1(),为一行元素的个数(包含所有通道),mte.step1(0) = 4;
同理step1(1)返回的则为一个元素的个数(包含所有通道)【MD,这点理解但是说出来怎感觉就特么是个病句】,总之mte.step(1) = 2;

现说这么多,下一篇从引用次数Mat::u->refcount说起。