OPENCV之Mat类详解

 译文参考The OpenCV Reference Manual (Release 2.3)August 17 2011

Mat类

OpenCV c + + n 维稠密数组类

class CV_EXPORTS Mat

{

public:

/ / … …很多的方法...

...

/*!包括几位字段:

-神奇的签名

-连续性标志

-深度(Note:应该是位深)

-通道数

*/

int flags;Note :目前还不知道flags做什么用的)

//!数组的维数,> = 2

int dims ;

//!行和列的数量或 (-1,-1) 此时数组已超过 2 维

int rows,cols;

//!指向数据的指针

uchar *data ;

//!指针的引用计数器 ;

/ / 阵列指向用户分配的数据时,当指针为 NULL

int * refcount ;

/ / 其他成员

...

};

Mat类表示一个 n 维的密集数值单通道或多通道数组。它可以用于存储实数或复数值的向量和矩阵、灰度或彩色图像、体素、向量场、点云、张量、直方图 (尽管较高维的直方图存储在SparseMat可能更好)。M 数组的数据布局是由阵列  M.step[]定义的,使元素的地址(i0,。。。。iM.dims-1),其中 0<= ik < M.size [k],可以计算为:

addr( Mi0 ;:::;iM.dims-1) = M.data+ M.step[ 0]*i0 + M.step[ 1] *i1 + .…+ M.step[ M:dims- 1] iM:dims- 1

2维的数组的情况下根据上述公式被减至:

addr( Mi,j)= M.data+ M.step[ 0]*i+ M.step[ 1] *j

请注意,M.step[i] > =M.step[i+1] (事实上,M.step[i] > =M.step[i+1]*M.size[i+1])。这意味着2维矩阵是按行存储的,3 维矩阵是由平面存储,以此类推。M.step[M.dims-1] 是最小的而且总是等于元素大小M.elemSize()。因此,Mat中的数据布局完全兼容OpenCV 1.x 中CvMat、 IplImage、 CvMatND类型。它也和标准工具包和SDK,如Numpy(ndarray),Win32(独立设备位图)等主流的密集数组类型相兼容,也就是说,与任何使用步进(或步长)来计算像素位置的阵列相兼容。由于这种兼容性,使用户分配的数据创建Mat头以及用OpenCV函数实时处理该头成为可能。有很多不同的方法,创建一个Mat的对象。下面列出了最常见的选项:

使用 create(nrows,ncols,type)方法或类似的Mat(nrows,ncols,type [,fillValue])构造函数。一个新的指定了大小和类型的数组被分配。type和cvCreateMat 方法中的type参数具有相同的含义。例如,CV_8UC1 是指一个 8 位单通道阵列,CV_32FC2 指 2 通道(复平面)浮点阵列,以此类推。

//创建一个用1+3j填充的 7 x 7 复矩阵。

Mat  M(7,7,CV_32FC2,Scalar(1,3)) ;

/ /现在将 M转换为100 x 60的CV_8UC(15)的矩阵。

/ / 旧内容将会被释放

M.create(100,60,CV_8UC(15)) ;

这一章导言中指出,当当前的数组与指定的数组的形状或类型create() 分配唯一的新数组时的形状或类型。

创建多维数组:

/ / 创建 100 x 100 x 100 8 位数组

int sz[] = {100, 100, 100};

Mat. bigCube (3,sz,CV_8U,Scalar::all(0)) ;它将维度数(= 1)传递给Mat的构造函数,但列数设置为 1时,创建数组将是 2 维的。因此,Mat::dims 始终是>=2的(该数组为空时,也可以是 0)。

使用的复制构造函数或赋值运算符可以是一个数组或右侧的表达式(请参阅下图)。正像在导言中指出的,数组赋值运算复杂度是O(1)因为当你需要它的时候,它仅复制头和增加引用计数。Mat::clone() 方法可用于获取全(深)的副本数组。

为另一个数组的一部分构建头。它可以是单个行、 单个列,几个行,几个列,矩形区域(代数中称为较小值) 的数组或对角线。这种操作也是复杂度为O(1),因为,新头引用相同的数据。实际上,您可以使用此特性修改该数组的一部分例如:

/ /第 5行,乘以 3,加到第 3 行,

M.row(3) = M.row(3) + M.row (5) * 3 ;

/ / 现在将第7列复制到第1列

/ / M.col(1) = M.col(7) ;/ / 这个不能实现。

Mat  M1= M.col(1) ;

M.col(7).copyTo(M1) ;

/ / 创建一种新的 320 x 240 图像

Mat img(Size(320,240),CV_8UC3) ;

/ / 选择ROI(region of interest)

Mat roi(img,Rect(10,10,100,100)) ;

/ / 填充 (0,255,0) 的ROI (这是RGB 空间中的绿色);

/ / 320 x 240 原始图像将被修改。

roi = Scalar(0,255,0) ;

由于额外的 datastart 和 dataend 的成员,它们使得用locateROI() 计算子数组在主容器数组中的相对的位置成为可能:

Mat A = Mat::eye ( 10, 10, CV_32S);

/ / 提取 A 的1 (含)到 3 (不包含)列。

Mat B = A(Range::all(),Range(1,3)) ;

/ / 提取 B 的5 (含)到 9 (不包含)行。

/ /即 C ~ A(Range(5,9),Range (1,3))

Mat C = B(Range(5,9),Range::all()) ;

Size size;Point ofs;

C.locateROI (size,ofs);

/ / size将变为 (width= 10,height= 10),ofs会变为 (x = 1,y = 5)

考虑到整个矩阵,如果您需要深层副本,使用子矩阵的sclone() 方法的提取。

为用户分配数据创建矩阵头。有利于执行下列操作:

1. 使用 OpenCV处理"外来"的数据(例如,当您执行 DirectShow *filter 或 gstreamer的pro-cessing 模块,等等)。例如:

void process_video_frame (const unsignedchar * pixels,

int width,int height,int step)

{

Mat img (width,height, CV_8UC3,pixels,step);

GaussianBlur (img,img ,Size(7,7),1.5,1.5) ;

}

2.快速初始化小矩阵和/或获取超快的元素的访问。

double m[3] [3] = {{a,b,c},{d,e,f} {g, h, i}}};

Mat M = Mat(3,3,CV_64F,m).inv() ;

本例中用户分配数据的一些很常见情况是从CvMat 和 IplImage 转换到Mat。为达到此目的,有些特殊的构造函数以指向CvMat 或 IplImage 和flag可选参数指示是否数据复制。从Mat到 CvMat 或 IplImage 的后台转换是通过类型转换运算符 Mat::operator CvMat() const 和 Mat::operator IplImage()实现的。operators不要复制数据。

IplImage * img = cvLoadImage("greatwave.jpg",1) ;

Mat mtx(img) ;/ / IplImage *-> Mat

CvMat oldmat = mtx ;/ / Mat-> CvMat

CV_Assert (oldmat.cols = = img-> width&& oldmat.rows = = img-> height & &

oldmat.data.ptr = = (uchar *) img->imageData & & oldmat.step = = img-> widthStep);

使用 MATLAB 样式数组初始值设定项zeros()、 ones()、 eye(),例如:

/ / 创建具双精度标识矩阵并将其添加到M。

M + = Mat::eye (M.rows,M.cols,CV_64F);

使用逗号分隔的初始值设定项:

/ / 创建 3 x 3 双精度恒等矩阵

Mat M = (Mat_ <double> (3,3) <<1,0,0,0,1,0,0,0,1) ;

使用此方法,您首先调用具有适当的参数的 Mat_类构造函数,然后只要把 << 运算符后面的值用逗号分隔,这些值可以是常量、变量、 表达式,等等。此外请注意所需的额外的圆括号((Mat_<double> (3,3)<< 1,0,0,0,1,0,0,0,1))以免出现编译错误。

数组一旦创建起来,它可以自动通过引用计数的机制被管理。如果数组头是在用户分配的数据的基础上构建的,您应该自己处理这些数据。当没有指向它的引用时,数组中的数据将被释放。如果在数组的析构函被调用之前要释放一个由矩阵头指向的数据,请使用Mat::release()。

掌握Array类的另一个重要的环节是元素的访问。本手册已经描述了如何计算每个数组元素的地址。通常情况下,不需要在代码中直接使用的公式。如果你知道数组元素类型(它可以使用 Mat::type() 方法检索得到),您可以用以下方式访问二维数组的元素Mij

M.at <double>(i,j)  + = 1.f ;

假定 M 一个双精度浮点型数组。有几个变体的不同方法来针对不同的维度数进行处理。

如果您要处理整行的二维数组,最有效的方式是获取该行的头指针然后只需使用普通的 C运算符[]:

/ / 正矩阵元素之和计算

/ / (假定M 是一个双精度矩阵)

double sum = 0;

for (int i = 0 ;i < M.rows ; i + +)

{

const double *Mi = M.ptr <double> (i) ;

     for (int j = 0; j < M.cols ; j + +)

sum + = std::max(Mi [j],0.) ;

}

以上的操作中,某些操作实际上不依赖该数组的形状。他们只是一个接一个(或多个具有相同的坐标的多个数组中的元素,例如,数组相加)地处理数组元素。这种操作称为 元素指向(element-wise)。检查是否所有的输入/输出阵列是连续的,即有没有间断在每行的结尾,是有意义的。如果是的话,将它们(这些数组)作为单独的一个长行来处理:

/ / 计算正矩阵元素,优化的变量的总和

double sum = 0;

int cols =M.cols,rows = M.rows ;

if(M.isContinuous())

{

    cols * = rows ;

    rows = 1 ;

}

for (int i = 0 ;i < rows; i + +)

{

const double * Mi = M.ptr <double>(i) ;

for (int j = 0; j < cols ; j ++)

   sum + = std::max (Mi [j],0.) ;

}

对于连续的矩阵来说,外部循环体只需一次执行。所以,开销是规模较小,

小型矩阵的情况下尤其明显。

最后,还有足以成功跳过连续的行之间的间隔智能的STL 样式迭代器:

/ / 计算正矩阵元素和基于迭代器类型的变量之和

double sum = 0;

Mat Const Iterator_ <double> it =M.begin <double> (),it_end = M.end <double> () ;

for(; it! = it_end ; ++it)

sum+ = std::max (*it,0.);

矩阵迭代器是随机存取的迭代器,所以他们可以被传递给任何 STL 算法,包括 std::sort()。

矩阵表达式

这是已经实现的可以组合在任意复杂的表达式中的矩阵运算操作, (此处 A 、B 的表示矩阵 (Mat)、 s表示标量(Scalar),alpha为实数标量 (双精度型):

  加法、减法、求反: A + B + A-B、 A + s、 A-s、 s + A、 s-A、-A;

  缩放: A * alpha

  每个元素乘法和除法: A.mul (B)、 A / B,alpha/A

  矩阵相乘: A * B

  大动脉转位: A.t() (指在)

 矩阵反演和伪反演,求解线性系统和最小二乘问题:

A.inv([method]) (~ A-1) , A.inv([method])*B (~ X: AX=B)

  比较: cmpop B、 cmpop alpha、 alpha cmpop A,其中 cmpop 是以下几种运算符之一: >,> =,= =,! =,< =,<。比较的结果是其元素设置为 255的 8 位单通道掩码(如果特殊元素对满足条件) 或 0。

  按位逻辑运算: logicop B、 logicop s slogicop A、 ~ A,其中 logicop 是以下运算符之一: &,|, ^.

  元素的最小值和最大值:分 (A、 B)、 民 (,alpha),最大值 (A,B),最大 (,alpha)

  元素的绝对价值: abs(A)

  叉乘,点乘: A.cross(B) A.dot(B)

  任何标量与矩阵或矩阵的函数,返回一个矩阵或标量(scalar),如norm、, mean、 sum、countNonZero、trace、determinant、repeat和其他。

  矩阵初始值设定项(Mat::eye(),Mat::zeros(),Mat::ones())、矩阵以逗号分隔的初始值设定项、可提取sub-matrices的m atrix构造函数和运算符,(请参见Mat的说明)。

  Mat_ <destination_type> () 构造函数将结果强制转换为适当的类型。

Note:有些逗号分隔初始值设定项和一些其他的运算符可能需要显示调用Mat();或Mat_<T>();的构造函数来解决可能产生的歧义。

         以下是一些矩阵表达式的例子:

//计算矩阵A的伪反演等价于A.inv(DECOMP_SVD)

SVD svd(A);

Mat pinvA =svd.vt.t()*Mat::diag(1./svd.w)*svd.u.t();

//计算莱文伯格-马夸特算法中的参数的新向量

x -= (A.t()*A +lambda*Mat::eye(A.cols,A.cols,A.type())).inv(DECOMP_CHOLESKY)*(A.t()*err);

//用“Unsharp Mask”算法锐化图像

Mat blurred; double sigma = 1, threshold =5, amount = 1;

GaussianBlur(img, blurred, Size(), sigma,sigma);

Mat lowConstrastMask = abs(img - blurred)< threshold;

Mat sharpened = img*(1+amount) +blurred*(-amount);

img.copyTo(sharpened, lowContrastMask);

下面正式讲解Mat的各种方法。

Mat:: Mat

各种Mat构造函数。

C++: Mat::Mat()

C++: Mat::Mat(int rows, int cols, int type)

C++: Mat::Mat(Size size, int type)

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

C++: Mat::Mat(Size size, int type, constScalar& s)

C++: Mat::Mat(const Mat& m)

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

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

C++: Mat::Mat(const Mat& m, constRange& rowRange, const Range& colRange)

C++: Mat::Mat(const Mat& m, constRect& roi)

C++: Mat::Mat(const CvMat* m, boolcopyData=false)

C++: Mat::Mat(const IplImage* img, boolcopyData=false)

C++: template<typename T, int n>explicit Mat::Mat(const Vec<T, n>& vec, bool copyData=true)

C++: template<typename T, int m, intn> explicit Mat::Mat(const Matx<T, m, n>& vec, bool copyData=true)

C++: template<typename T> explicitMat::Mat(const vector<T>& vec, bool copyData=false)

C++: Mat::Mat(const MatExpr& expr)

C++: Mat::Mat(int ndims, const int* sizes,int type)

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

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

C++: Mat::Mat(const Mat& m, constRange* ranges)

         参数

ndims– 数组的维数.

rows – 2维数组中行行数

cols – Number of columnsin a 2D array.

size – 2维数组的尺寸Size(cols, rows) .在Size()构造函数中行数和列数在次序上刚好反转过来了。

sizes–指定 n 维数组形状的整数数组。

type–数组的类型。使用 CV_8UC1,… …,创建 1-4 通道的矩阵,CV_64FC4 或CV_8UC(n),… …,CV_64FC(n)可以创建多通道 (高达 CV_MAX_CN 通道)矩阵。

s–一个可选的初始化每个矩阵元素的参数。要在矩阵建成后将所有元素设置为特定值可以用Mat的赋值运算符Mat:operator=(constScala& value)。

data–指向用户数据的指针。矩阵构造函数传入data和step参数不分配矩阵数据。相反,它们只是初始化矩阵头指向指定的数据,这意味着没有数据的复制。此操作是很高效的,可以用来处理使用 OpenCV 函数的外部数据。外部数据不会自动释放,所以你应该小心处理它。

step–每个矩阵行占用的字节数。如果任何值应包括每行末尾的填充字节。如果缺少此参数(设置为 AUTO_STEP),假定没有填充和实际的步长用cols*elemSize()计算。请参阅Mat::elemSize()。

steps–多维数组(最后一步始终设置为元素大小) 的情况下的 ndims-1个步长的数组。如果没有指定的话,该矩阵假定为连续。

m–分配给构造出来的矩阵的阵列(作为一个整体或部分)。这些构造函数没有复制数据。相反,指向 m 的数据或它的子数组的头被构造并被关联到m上。引用计数器中无论如何都将递增。所以,当您修改矩阵的时候,自然而然就使用了这种构造函数,您还修改 m 中的对应元素。如果你想要独立的子数组的副本,请使用 Mat::clone()。

img –指向老版本的 IplImage图像结构的指针。默认情况下,原始图像和新矩阵之间共享数据。但当 copyData 被设置时,完整的图像数据副本就创建起来了。

vec–矩阵的元素构成的STL 向量。矩阵可以取出单独一列并且该列上的行数和矢量元素的数目相同。矩阵的类型匹配的向量元素的类型。构造函数可以处理任意的有正确声明的DataType类型。这意味着矢量元素不支持的混合型结构,它们必须是数据(numbers)原始数字或单型数值元组。对应的构造函数是显式的。由于 STL 向量不会自动转换为Mat实例,您应显式编写 Mat(vec)。除非您将数据复制到矩阵 (copyData = true),没有新的元素被添加到向量中,因为这样可能会造成矢量数据重新分配,并且因此使得矩阵的数据指针无效。

copyData –指定STL 向量或旧型 CvMat 或 IplImage是应复制到 (true)新构造的矩阵中 还是 (false) 与之共享基础数据的标志,复制数据时,使用Mat引用计数机制管理所分配的缓冲区。虽然数据共享的引用计数为 NULL,但是分配数据必须在矩阵被析构之后才可以释放。

rowRange – m 的行数的取值范围。正常情况下,范围开始端具有包容性和范围结束端是独占的。使用 Range::all() 来取所有的行。

colRange –m 列数的取值范围。使用 Range::all() 来取所有的列。

ranges –表示M沿每个维度选定的区域的数组。

expr – 矩阵表达式。请参见矩阵表达式。

以上这些都是Mat生成一个矩阵的各类构造函数。如 输出数据的自动分配 一节(该节内容在第一章 Introduction)中所提到的,往往默认构造函数就足够了,不同的矩阵可以由 OpenCV 函数来分配数据空间。构造的矩阵可以进一步分配给另一个矩阵或矩阵表达或通过Mat::create()获配。在前一种情况,旧的内容是间接引用的。


Mat::~Mat

Mat的析构函数。

C++: Mat::~Mat()

析构函数调用Mat::release()。

Mat::operator =

提供矩阵赋值操作。

C++: Mat& Mat::operator=(const Mat& m)

C++: Mat& Mat::operator=(const MatExpr_Base& expr)

C++: Mat& Mat::operator=(const Scalar& s)

参数:

m – 被赋值的右侧的矩阵。 矩阵的赋值是一个复杂度为O(1) 的操作。 这就意味着没有数据段复制并且有数量的递增两矩阵将使用同一引用计数器。在给矩阵赋新数据之前先由Mat::release()释放引用。

expr –被赋值的矩阵表达式对象。 作为第一种赋值方式的逆操作第二种形式可以被重新用到具有适当大小和尺寸的已分配空间的矩阵上以适应表达式的结果。矩阵表达式扩展得到的实函数将自动处理这个分配过程。例如:

C=A+B 扩展成add(A, B, C) , andadd() 要当心C重新分配数据的操作。.

s – 标量赋值给每一个矩阵元,矩阵的大小和类型将不会改变。有现成的赋值运算符。由于他们各不相同请阅读运算符参数说明。

Mat::operator MatExpr

提供一种Mat-to-MatExpr转换运算符

C++: Mat::operator MatExpr_<Mat, Mat>() const

转换运算符不能显示调用而是由矩阵表达式引擎(Matrix Expression engine)内部调用The cast operator should not be called explicitly. It is used internally by the Matrix Expressions engine.

Mat::row

创建一个指定行数的矩阵头。.

C++: Mat Mat::row(int i) const

参数:

i – 一个0基的行索引.

该方法创建一个具有指定了行数的新矩阵头的矩阵并返回它。这是一个复杂度为O(1) 的操作,无须考虑矩阵的尺寸。新矩阵和原矩阵共享一份基础数据。这是一个典型基本矩阵处理操作的例子, axpy是LU和许多其它算法都使用的一个函数

inline void matrix_axpy(Mat& A, int i, int j, double alpha)

{

A.row(i) += A.row(j)*alpha;

}

Note:在当前实现中,下面的代码不会无法按预期的效果工作:

Mat A ;

...

A.row(i) = A.row(j) ;/ /不起作用

发生这种情况是因为 A.row(i) 形成临时矩阵头进一步分配给另一个矩阵头。请记住,每个操作复杂度为O(1),即没有复制任何数据。因此,如果你预期第 j行被复制到第 i行,那么上述赋值不成立。要做到这一点,应该把这种简单的赋值转换到表达式中或使用 Mat::copyTo() 方法:

Mat A ;

...

/ / 可行,但看上去有点目的不明确。

A.row(i) = A.row(j) + 0;

/ / 这是有点儿长,但这是推荐的方法。

A.row(j).copyTo(A.row(i)) ;

Mat::col

创建一个具有指定了矩阵头中列数这个参数的矩阵

C++: Mat Mat::col(int j) const

参数:

j –一个0基(从0开始)的列索引

该方法创建一个具有指定了矩阵头中列数这个参数的新矩阵并作为函数返回值。这是一种复杂度为O(1)的操作,不用考虑矩阵的尺寸大小。新矩阵和原始矩阵共享一份基础数据。参看Mat::row()说明信息。

Mat::rowRange

为指定的行span创建一个新的矩阵头。

C++: Mat Mat::rowRange(int startrow, int endrow) const

C++: Mat Mat::rowRange(const Range& r) const

参数:

startrow – 一个包容性的0基(从0开始)的行span起始索引.。

endrow – 一个0基的独占性的行span.终止索引。

r – Range 结构包含着起始和终止的索引值。该方法给矩阵指定的行span创建了新的头。 与Mat::row() 和 Mat::col()相类似这是一个复杂度为O(1)的操作。

Mat::colRange

为指定的行span创建一个矩阵头。

C++: Mat Mat::colRange(int startcol, int endcol) const

C++: Mat Mat::colRange(const Range& r) const

参数:

startcol – 一个包容性的0基(从0开始)的span列起始索引。

endcol –一个0基的独占性的列span.终止索引。

r –Range 结构包含着起始和终止的索引值。该方法给矩阵指定的列span创建了新的头。 与Mat::row() 和 Mat::col()相类似这是一个复杂度为O(1)的操作。

Mat::diag

提取或创建矩阵对角线。

C++: Mat Mat::diag(int d) const

C++: static Mat Mat::diag(const Mat& matD)

参数:

d – 对角线的索引值,可以是以下的值:

– d=0 是主对角线

– d>0表示下半部的对角线。例如:d=1对角线是紧挨着住对角线并位于矩阵下方。

– d<0表示来自矩阵上半部的对角线。例如:d= 1表示对角线被设置在对角线的上方并紧挨着。

matD – 单列用于形成矩阵对角线的列。

该方法为指定的矩阵创建一个新的头。然后新矩阵被分割为单独的列矩阵。类似于Mat::row() 和Mat::col() ,它是复杂度为O(1)操作。

Mat::clone

创建一个数组及其基础数据的完整副本。

C++: Mat Mat::clone() const

该方法创建了一个完整的数组副本。原始的step[]不会被考虑在内的。因此数组的副本是一占用total()*elemSize()字节的连续阵列。

Mat::copyTo

把矩阵复制到另一个矩阵中。

C++: void Mat::copyTo(OutputArray m) const

C++: void Mat::copyTo(OutputArray m, InputArray mask) const

参数:

m – 目标矩阵。如果它的尺寸和类型不正确,在操作之前会重新分配。

mask – 操作掩码。它的非零元素表示矩阵中某个要被复制。

该方法把矩阵的复制到另一个新的矩阵中在复制之前该方法会调用

m.create(this->size(), this->type);

因此,目标矩阵会在必要的情况下重新分配

尽管m.copyTo(m) works flawlessly,该函数并不处理源矩阵和目标矩阵之间有重叠的部分的情况。当操作掩码指定以及上述的Mat::create重新分配矩阵,新分配的矩阵在数据复制到里面之前全都被初始化为0。

Mat::convertTo

在缩放或不缩放的情况下转换为另一种数据类型。

C++:

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

参数:

m – 目标矩阵。如果它的尺寸和类型不正确,在操作之前会重新分配。

rtype – 要求是目标矩阵的类型,或者在当前通道数与源矩阵通道数相同的情况下的depth。如果rtype 为负,目标矩阵与源矩阵类型相同。

beta – 可选的delta加到缩放值中去。

该方法将源像素值转化为目标类型saturate_cast<> 要放在最后以避免溢出

m( x;y) = saturate_cast < rType > ( α*( *this)( x;y) +β)

Mat::assignTo

提供了一个convertTo的功能形式。

C++: void Mat::assignTo(Mat& m, int type=-1 ) const

Parameters

m – 目标阵列。

type – 要求是目标阵列depth或-1(如果阵列的类型和源矩阵类型相同)

这是一个 internally 使用的由 Matrix Expressions引擎调用的方法。

Mat::setTo

将阵列中所有的或部分的元素设置为指定的值。

C++: Mat& Mat::setTo(const Scalar& s, InputArray mask=noArray())

参数:

s – 把标量赋给阵列并转化到阵列的实际类型。

mask – 与 *this尺寸相同的操作掩码。这是Mat::operator=(const Scalar& s)运算符的一个高级变量。

Mat::reshape

在无需复制数据的前提下改变2D矩阵的形状和通道数或其中之一。

C++: Mat Mat::reshape(int cn, int rows=0) const

参数:

cn – 新的通道数。若cn=0,那么通道数就保持不变。

rows –新的行数。 若rows = 0, 那么行数保持不变。

该方法为*this元素创建新的矩阵头。这新的矩阵头尺寸和通道数或其中之一发生改变,在以下的情况任意组合都是有可能的:

ü  新的矩阵没有新增或减少元素。通常,rows*cols*channels()在转换过程中保持一致。.

ü  无数据的复制。也就是说,这是一个复杂度为 O(1)的操作。通常,如果该操作改变行数或透过其他方式改变元素行索引,那么矩阵必定是连续的。参见Mat::isContinuous()。

例如,有一存储了STL向量的三维点集,你想用3xN的矩阵来完成下面的操作:

std::vector<Point3f> vec;

...

Mat pointMat = Mat(vec). //把向量转化成Mat, 复杂度为O(1)的运算

reshape(1). // 从Nx1的3通道矩阵得出Nx3 的单通道矩阵

//同样是复杂度为O(1)的运算

t(); // 最后转置Nx3 的矩阵

//这个过程要复制所有的元素

Mat::t

转置矩阵。.

C++: MatExpr Mat::t() const

该方法通过矩阵表达式(matrix expression)实现矩阵的转置The method performs matrix transposition by means of matrix expressions. 它并未真正完成了转置但却返回一个临时的可以进一步用在更复杂的矩阵表达式中或赋给一个矩阵的转置矩阵对象:

Mat A1 = A + Mat::eye(A.size(), A.type)*lambda;

Mat C = A1.t()*A1; //计算(A + lambda*I)^t * (A + lamda*I).

Mat::inv

反转矩阵

C++: MatExpr Mat::inv(int method=DECOMP_LU) const

参数:

method – 反转矩阵的方法。有以下几种可能的值:

– DECOMP_LU是 LU 分解一定不能是单数的。

– DECOMP_CHOLESKY 是 Cholesky LLT只适用于对称正矩阵的分解。该类型在处理大的矩阵时的速度是LU的两倍左右。

– DECOMP_SVD是 SVD 分解。如果矩阵是单数或甚至不是2维,函数就会计算伪反转矩阵。

该方法执行矩阵的反转矩阵表达。这意味着该方法返回一个临时矩阵反转对象并可进一步用于更复杂的矩阵表达式的中或分配给一个矩阵。

Mat::mul

执行两个矩阵按元素相乘或这两个矩阵的除法。

C++: MatExpr Mat::mul(InputArray m, double scale=1) const

参数:

m – 与*this具有相同类型和大小的矩阵,或矩阵表达式。

scale – 可选缩放系数。

该方法返回一个用可选的缩放比率编码了每个元素的数组乘法的临时的对象。 注意:这不是一个对应“*”运算符的简单的矩阵乘法。.

例::

Mat C = A.mul(5/B); // 等价于divide(A, B, C, 5)

Mat::cross

计算3元素向量的一个叉乘积。

C++: Mat Mat::cross(InputArray m) const

参数:

–另一个叉乘操作对象。

该方法计算了两个3元素向量的叉乘的积被操作向量必须是3元素浮点型的具有相同形状和尺寸的向量。结果也是一语被操作对象的具有相同形状和大小的浮点型3元素向量。

Mat::dot

计算两向量的点乘。

C++: double Mat::dot(InputArray m) const

参数:

–另一个点积操作对象。

方法计算两个矩阵的点积。如果矩阵不单列或单行的向量,用顶部到底部从左到右扫描次序将它们视为 1 D向量。这些向量必须具有相同的大小和类型。如果矩阵有多个通道,从所有通道得到的点积会被加在一起。

Mat::zeros

返回指定的大小和类型的零数组。

C++: static MatExpr Mat::zeros(int rows, int cols, int type)

C++: static MatExpr Mat::zeros(Size size, int type)

C++: static MatExpr Mat::zeros(int ndims, const int* sizes, int type)

参数

ndims – 数组的维数。

rows–行数。

cols  –列数。

size–替代矩阵大小规格Size(cols, rows)的方法。

sizes– 指定数组的形状的整数数组。

type– 创建的矩阵的类型。

该方法返回一个 Matlab 式的零数组初始值设定项。它可以用于快速形成一个常数数组作为函数参数,作为矩阵的表达式或矩阵初始值设定项的一部分。

Mat A;

A = Mat::zeros (3,3,CV_32F);

在上面的示例中,只要A不是 3 x 3浮点矩阵它就会被分配新的矩阵。否则为现有的

矩阵 A填充零。

Mat::ones

返回一个指定的大小和类型的全为1的数组。

C++: static MatExpr Mat::ones(int rows, int cols, int type)

C++: static MatExpr Mat::ones(Size size, int type)

C++: static MatExpr Mat::ones(int ndims, const int* sizes, int type)

参数:

ndims –数组的维数。

rows –行数。.

cols –列数。

size –替代矩阵大小规格Size(cols, rows)的方法。

sizes –指定数组的形状的整数数组。

type –创建的矩阵的类型。

该方法返回一个 Matlab 样式 1 的数组初始值设定项,类似Mat::zeros()。请注意,这种方法中你可以使用任意一个值和Matlab 语法初始化数组如下:

Mat A = Mat::ones (100,100,CV_8U) * 3 ;/ / 使100 x 100 矩阵里充满 3。

上述操作不会形成一个 100 x 100 1 的矩阵,然后乘以 3。相反,它只是记住

缩放因子(在本例中 3)在实际调用矩阵初始值设定项时使用它。

Mat::eye

返回一个恒等指定大小和类型矩阵。

C++: static MatExpr Mat::eye(int rows, int cols, inttype)

C++: static MatExpr Mat::eye(Size size, int type)

参数

rows –的行数。

cols– 的列数。

size –替代矩阵大小规格Size(cols, rows)的方法。

type – 创建的矩阵的类型。

该方法返回 Matlab 式恒等矩阵初始值设定项,类似 Mat::zeros()和 Mat::ones(),你可以用缩放操作高效地创建缩放的恒等矩阵:

/ / 创建4 x 4 的对角矩阵并在对角线上以0.1的比率缩小。

Mat A = Mat::eye(4, 4, CV_32F)*0.1;

Mat::create

分配新的阵列数据 (如果需要)。

C++: void Mat::create(int rows, int cols, int type)

C++: void Mat::create(Size size, int type)

C++: void Mat::create(int ndims, const int* sizes, inttype)

参数

ndims – 新数组的维数。

rows –新的行数。

cols – 新的列数。

size – 替代新矩阵大小规格:Size(cols, rows)。

sizes – 指定一个新的阵列形状的整数数组。

type – 新矩阵的类型。

这是关键的Mat方法之一。大多数新样式 OpenCV 函数和产生阵列的方法每个输出数组都调用这个方法。此方法使用如下算法:

1.如果当前数组形状和类型匹配新的请立即返回。否则,通过调用 Mat::release()取消引用以前的数据。

2.初始化新矩阵头。

3.分配新的 total()*elemSize() 个字节的数据空间。

4.分配新的关联数据的引用计数并将其设置为 1。

这项计划使内存管理强大高效同时还为用户减少了额外输入。这意味着通常不需要显式分配输出数组。也就是说,可以不写成:

Mat color;

...

Mat gray(color.rows, color.cols,color.depth());

cvtColor(color, gray, CV_BGR2GRAY);

而写成:

Mat color;

...

Mat gray;

cvtColor(color, gray, CV_BGR2GRAY);

因为 cvtColor,与大多数 OpenCV 函数相同,在输出数组时内部调用Mat::create()。

Mat::addref

计数器参考。

C++: void Mat::addref()

该方法递增与矩阵数据关联的引用计数。如果矩阵头指向外部的数据集(见 Mat::Mat()),则引用计数为 NULL,并且该方法在这种情况下不起作用。通常情况下,为避免内存泄漏,不应显式调用该方法。它是由该矩阵赋值运算符隐式调用。在支持的它平台上,引用计数器递增是一个原子操作。因此,对相同的矩阵,在不同的线程异步操作是安全的。

Mat::release

在必要的情况下,递减引用计数并释放该矩阵。

C++: void Mat::release()

该方法递减与矩阵的数据关联的引用计数。当引用计数减为0时,矩阵的数据将被释放,数据和引用计数器指针设置为 NULL。如果矩阵头指向外部数据集 (见 Mat::Mat()), 引用计数为 NULL,并且该方法在这种情况下无效。

可以手动调用此方法强制矩阵数据释放。但由于这种方法在析构函数中是自动调用的,或以更改数据指针的其他方法,因此通常不需要调用这个函数。在支持它的平台上,引用计数器递减并检查是否为0 是一个原子操作。因此,在不同的线程异步调用相同的矩阵是安全的操作。

Mat::resize

更改矩阵的行数。

C++: void Mat::resize(size_t sz)

C++: void Mat::resize(size_t sz, const Scalar& s)

参数

sz –新的行数。

s –分配给新添加的元素的值。

该方法更改矩阵的行数。如果矩阵重新分配,第一最少(Mat::rows,sz) 行数要保留下来。该方法模拟相应的 STL 向量类的方法。

Mat::reserve

保留一定数量的行的空间。

C++: void Mat::reserve(size_t sz)

参数

sz –的行数。

该方法sz行存储空间。如果矩阵已经有足够的空间来存储sz行,没有任何异常发生。如果矩阵重新分配,保留前(Mat::rows) 行。该方法模拟了相应的STL 向量类的方法。

Mat::push_back

将元素添加到矩阵的底部。

C++: template<typename T> voidMat::push_back(const T& elem)

C++: void Mat::push_back(const Mat& elem)

参数

elem –增加的一个或多个元素。

该方法将一个或多个元素添加到矩阵的底部。他们是模拟相应的 STL 向量类的方法。元素为Mat时,其类型和列的数目必须和矩阵容器是相同的。

Mat::pop_back

从底部的列表中删除元素。

C++: template<typename T> voidMat::pop_back(size_t nelems=1)

参数

nelems –删除的行的数目。如果它大于总的行数,则会引发异常。

该方法从底部的列表中删除一行或多行。

Mat::locateROI

父矩阵内定位矩阵头。

C++: void Mat::locateROI(Size& wholeSize,Point& ofs) const

参数

wholeSize–输出参数,其中包含的整个矩阵包含大小 * 这是其中一部分。

ofs –输出参数包含*this在整个的矩阵里面的偏移量。你使用Mat::row()、 Mat::col()、 Mat::rowRange()、Mat::colRange()以及其他的方法从矩阵中提取子阵后该结果子阵只指向原始大矩阵的一部分。然而,每个子阵包含有助于重建的最初矩阵大小和提取子阵在原始矩阵中的位置信息( datastart  dataend fields表示)。locateROI方法正是这样做的。

Mat::adjustROI

调整子阵大小及其在父矩阵中的位置。

C++: Mat& Mat::adjustROI(int dtop, int dbottom,int dleft, int dright)

参数

dtop –顶部子阵边界向上的平移量。

dbottom –底部子阵边界向下的平移量。

dleft –左子阵边界向左的平移量。

dright –右子阵边界向右的平移量。

该方法是 Mat::locateROI() 的互补性方法。这些函数的典型应用是确定父矩阵中子阵的位置,然后以某种方式改变位置。尤其典型的是,当滤镜操作中要考虑ROI外的像素时就需要它。当方法的所有参数都是正的时候,ROI需要以指定量全方位增长,例如:

A.adjustROI(2, 2, 2,2);

在此示例中,每个方向 4 元素增加矩阵大小。矩阵向左侧和上侧分别平移2 个元素,这会产生5 x 5 内核的滤镜所需的所有像素。你的责任是确保 adjustROI 不超出父矩阵边界。如果超出,该函数发出错误提示。OpenCV的滤镜函数在内部调用该函数,像filter2D(),形态学的操作,等等。

另请参阅:

copyMakeBorder()

Mat::operator()

提取矩形子阵。

C++: Mat Mat::operator()(Range rowRange, RangecolRange) const

C++: Mat Mat::operator()(const Rect& roi) const

C++: Mat Mat::operator()(const Ranges* ranges) const

参数:

rowRange –提取的子阵的开始和结束的行。不包括的上限。若要选择的所有行,请使用 Range::all()。

colRange –提取的子阵的开始和结束的列。不包括的上限。若要选择的所有列,请使用 Range::all()。

roi – 抽出子阵 specified 作为一个矩形。

ranges – 选定范围沿每个数组维度的数组。

该运算符为*this的子数组创建新的头。他们是Mat::row()、 Mat::col()、 Mat::rowRange(),和Mat::colRange()最普遍的形式。例如,A(Range(0, 10),Range::all()) 是相当于A.rowRange(0, 10)。与上述所有操作相同,该操作运算符是复杂度为O(1)的操作,就是没有矩阵数据将被复制。

Mat::operator CvMat

创建矩阵 CvMat 头。

C++: Mat::operator CvMat() const

该运算符创建矩阵 CvMat 的头,而不复制的基础数据。引用计数未被考虑到此操作中。因此,您应该确保CvMat 头在使用的时候不释放原始矩阵。该运算符对于新旧OpenCV API混用是有用的,例如:

Mat img(Size(320, 240), CV_8UC3);

...

CvMat cvimg = img;

mycvOldFunc( &cvimg, ...);

其中 mycvOldFunc 是用于OpenCV 1.x 数据结构的函数。

Mat::operator IplImage

创建IplImage矩阵头。

C++: Mat::operator IplImage() const

运算符创建矩阵 IplImage 头,而不复制的基础数据。您应该确保使用IplImage头时不释放原矩阵。与Mat::operatorCvMat类似,该运算符在OpenCV新旧API混用中很有用。

Mat::total

返回数组元素的总数。

C++: size_t Mat::total() const

该方法返回数组元素(如果该数组表示图像的像素数)的数目。

Mat::isContinuous

返回矩阵是否连续。

C++: bool Mat::isContinuous() const

如果在每一行的结尾无间隙连续存储矩阵的元素,该方法返回 true。否则,它将返回 false。很明显,1 x 1 或 1xN 矩阵始终是连续的。使用 Mat::create() 创建矩阵是始终是连续的。但是,如果您提取的矩阵,使用 Mat::col()、 Mat::diag(),等等,或外部已分配的数据构造的矩阵头的一部分,那么这种矩阵可能不再具有此属性。连续性标记在Mat::flags域内用一个位存储,构造矩阵头时可以自动计算出来。因此,连续性检查是非常快速的操作,虽然理论上可以做,如下所示:

/ / 等价的Mat::isContinuous() 的执行情况

boolmyCheckMatContinuity(const Mat& m)

{

//返回 (m.flags & Mat::CONTINUOUS_FLAG) != 0;

return m.rows ==1 || m.step == m.cols*m.elemSize();

}

很多的OpenCV 函数中使用该方法。关键在于按元素操作(如算术和逻辑运算、 数学函数、 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(DataType<T>::depth, 4) &&

src1.size() == src2.size());

Size size = src1.size();

dst.create(size, src1.type());

// 规定如下: 检查阵列的连续性

//如果的确如此,阵列连续

// 把阵列看做一维的向量。

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++ )

{// 当阵列连续,

// 外循环只执行一次

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);

}

}

}

这种方法,不仅很简单,而且在简单的元素的操作中可以提高10-20%性能,尤其在图像很小且操作非常简单的时候。

在此函数中另一个 OpenCV 语法,目标数组中Mat::create() 的调用会对没有适当大小和类型的目标数组分配空间。虽然新分配的数组始终是连续的,但您仍需要检查目标数组,因为 Mat::create()不是总会分配一个新的矩阵。

Mat::elemSize

返回矩阵元素大小 (以字节为单位)。

C++: size_t Mat::elemSize() const

该方法返回以字节为单位的矩阵元素大小。例如,如果矩阵类型是 CV_16SC3,该方法返回3*sizeof(short)或 6。

Mat::elemSize1

以字节为单位返回每个矩阵元素通道的大小。

C++: size_t Mat::elemSize1() const

该方法返回以字节为单位的矩阵元素通道大小,也就是忽略通道的数量。例如,

如果矩阵类型是 CV_16SC3,该方法返回 sizeof(short) 或 2。

Mat::type

返回一个矩阵元素的类型。

C++: int Mat::type() const

该方法返回一个矩阵的元素类型。这是兼容CvMat 类型系统,像 CV_16SC3标识符

或 16 位有符号的3 通道阵列,等等。

Mat::depth

返回一个矩阵元素的深度。

C++: int Mat::depth() const

该方法返回矩阵元素深度(每个单独的通道类型)的标识符。例如,对于16位有符号的3通道数组,该方法返回CV_16S。矩阵类型的完整列表包含以下内容值:

• CV_8U-8 位无符号整数 (0…..255)

• CV_8S-8 位符号整数 (-128…..127)

• CV_16U-16 位无符号整数 (0……65535)

• CV_16S-16 位符号整数 (-32768…..32767)

• CV_32S-32 位符号整数 (-2147483648……2147483647)

• CV_32F-32 位浮点数 (-FLT_MAX ………FLT_MAX,INF,NAN)

• CV_64F-64 位浮点数(-DBL_MAX ……….DBL_MAX,INF,NAN)

Mat::channels

返回矩阵通道的数目。

C++: int Mat::channels() const

该方法返回矩阵通道的数目。

Mat::step1

返回矩阵归一化迈出的一步。

C + +: size_t const Mat::step1()

该方法返回以矩阵的step除以Mat::elemSize1()。它对快速访问任意矩阵元素很有用。

Mat::size

返回一个矩阵大小。

C++: Size Mat::size() const

该方法返回一个矩阵大小:Size(cols, rows)。矩阵超过 2 维时返回大小为(-1,-1)。

Mat::empty

如果数组有没有 elemens,则返回 true。

C++: bool Mat::empty() const

如果 Mat::total() 是 0 或 Mat::data 为 NULL,则方法返回 true。因为pop_back() 和 resize()方法M.total()= = 0,并不意味着M.data = =NULL。

Mat::ptr

返回指定矩阵行的指针。

C++: uchar* Mat::ptr(int i=0)

C++: const uchar* Mat::ptr(int i=0) const

C++: template<typename _Tp> _Tp* Mat::ptr(inti=0)

C++: template<typename _Tp> const _Tp*Mat::ptr(int i=0) const

参数:

i –一个基于0的行索引。

该方法返回uchar*,或指向由输入指定矩阵行的指针。参看Mat::isContinuous()的中示例了解如何使用这些方法。

Mat::at

返回对指定数组元素的引用。

C++: template<typename T> T& Mat::at(int i)const

C++: template<typename T> const T&Mat::at(int i) const

C++: template<typename T> T& Mat::at(int i,int j)

C++: template<typename T> const T&Mat::at(int i, int j) const

C++: template<typename T> T& Mat::at(Pointpt)

C++: template<typename T> const T&Mat::at(Point pt) const

C++: template<typename T> T& Mat::at(int i,int j, int k)

C++: template<typename T> const T&Mat::at(int i, int j, int k) const

C++: template<typename T> T& Mat::at(constint* idx)

C++: template<typename T> const T&Mat::at(const int* idx) const

参数

i –索引 0 维度

j – 1 维度的索引

k – 沿 2 维度的索引

pt – Point(j,i) 作为指定元素的位置。

idx – Mat::dims 数组的索引。

该模板方法返回指定数组元素的引用。为了具有更高的性能,索引范围检查只在调试配置下执行。请注意使用具有单个索引 (i) 的变量可以访问的单行或单列的2 维的数组元素。也就是比方说,如果A是1 x N 浮点矩阵和B是M x 1的整数矩阵,您只需编写A.at<float>(k+4) 和 B.at<int>(2*i+1) 分别代替A.at<float>(0,k+4)和

B.at<int>(2*i+1,0)。

下面的示例将初始化希尔伯特矩阵:

Mat H(100100, CV_64F);

for(inti=0; i<H.rows; i++)

for(intj=0; j<H.cols; j++)

H.at<double>(i,j)=1./(i+j+1);

Mat::begin

返回矩阵迭代器,并将其设置为第一矩阵元。

C++: template<typename _Tp>MatIterator_<_Tp> Mat::begin()

C++: template<typename _Tp>MatConstIterator_<_Tp> Mat::begin() const

该方法返回矩阵的只读或读写的迭代器。矩阵迭代器的使用和双向 STL 迭代器的使用是非常相似的。在下面的示例中,alpha融合函数是使用矩阵迭代器重写:

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() == DataType<VT>::type&&

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::end

返回矩阵迭代器,并将其设置为 最后元素之后(after-last)的矩阵元。

C++: template<typename _Tp>MatIterator_<_Tp> Mat::end()

C++: template<typename _Tp>MatConstIterator_<_Tp> Mat::end() const

该方法返回矩阵只读或读写的迭代器,设置为紧随最后一个矩阵元素的点。

原文见http://blog.csdn.net/giantchen547792075/article/details/9107877
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值