OpenCV矩阵操作(二)
====================================================================
cvDet
double cvDet(const
CvArr* mat);
cvDet()用于计算一个方阵的行列式。这个数组可以是任何数据类型,但它必须是单通道的,如果是小的矩阵,则直接用标准公式计算。然而对于大型矩阵,这样就不是很有效,行列式的计算使用高斯消去法。值得指出的是,如果已知一个矩阵是对称正定的,也可以通过奇异值分解的策略来解决。欲了解更多信息,请参阅“cvSVD”一节。但这个策略是将U和V设置为NULL,然后矩阵W的乘积就是所求正定矩阵。
cvDiv
void
cvDiv(
const CvArr*
src1,
const CvArr*
src2,
CvArr*
dst,
double scale =
1
);
cvDiv是一个实现除法的简单函数;它用src2除以src1中对应元素,然后把最终的结果存到dst中。如果mask非空,那么dst中的任何与mask中0元素相对应的元素都不改变。如果对数组中所有元素求倒数,则可以设置src1为NULL,函数将假定src1是一个元素全为1的数组。
cvDotProduct
double
cvDotProduct(
const CvArr*
src1,
const CvArr*
src2
);
这个函数主要计算两个N维向量的点积[Lagrange1773]。与叉积函数相同,点积函数也不太关注向量是行或者是列的形式。src1和src2都应该是单通道的数组,并且数组的数据类型应该一致。
cvEigenVV
double
cvEigenVV(
CvArr*
mat,
CvArr*
evects,
CvArr*
evals,
double eps =
0
);
对对称矩阵mat,cvEigenVV()会计算出该矩阵的特征值和相应的特征向量。函数实现的是雅可比方法[Bronshtein97],对于小的矩阵是非常高效的,雅可比方法需要一个停止参数,它是最终矩阵中偏离对角线元素最大尺寸。可选参数eps用于设置这个值。在计算的过程中,所提供的矩阵mat的数据空间被用于计算,所以,它的值会在调用函数后改变。函数返回时,你会在evects中找到以行顺序保存的特征向量。对应的特征值被存储到evals中。特征向量的次序以对应特征值的重要性按降序排列。该cvEigenVV()函数要求所有三个矩阵具有浮点类型。正如cvDet()(前述),如果被讨论的向量是已知的对称和正定矩阵,那么最好使用SVD计算mat的特征值和特征向量。
cvFlip
void
cvFlip(
const CvArr*
src,
CvArr* dst =
NULL,
int flip_mode =
0
);
本函数是将图像绕着在X轴或Y轴或者绕着X轴或Y轴上同时旋转。当参数flip_mode被设置为0的时候,图像只会绕X轴旋转。flip_mode被设置为正值时(例如,+1),图像会围绕Y轴旋转,如果被设置成负值(例如,-1),图像会围绕X轴和Y轴旋转。Win32运行视频处理系统时,你会发现自己经常使用此功能来进行图像格式变换,也就是坐标原点在左上角和左下角的变换。
cvGEMM
double
cvGEMM(
const CvArr*
src1,
const CvArr*
src2,
double
alpha,
const CvArr*
src3,
double
beta,
CvArr*
dst,
int tABC =
0
);
广义矩阵乘法(generalized
matrix
multiplicatipm,GEMM)在OpenCV中是由cvGEMM()来实现的,可实现矩阵乘法、转置后相乘、比例缩放等。最常见的情况下,cvGEMM()计算如下:其中A,B和C分别是矩阵src1,src2和src3,α和β是数值系数,op()是附在矩阵上的可选转置。参数src3可以设置为空。在这种情况下,不会参与计算。转置将由可选参数tABC来控制,它的值可以是0或者(通过布尔OR操作)CV_GEMM_A_T、CV_GEMM_B_T和CV_GEMM_C_T的任何组合(每一个标志都有一个矩阵转换相对应)。过去的OpenCV包含cvMatMul()和cvMatMulAdd()方法,但是,它们很容易和cvMul()混淆,其实它们的功能是完全不一样的(即两个数组的元素与元素相乘)。这个函数以宏的形式继续存在,它们直接调用cvGEMM()。两者对应关系如表3-7所示。
表3-7:cvGEMM()一般用法的宏别名
cvMatMul(A,B,
D)
cvGEMM(A,B,1,NULL,0,D,0)
cvMatMulAdd(A,B,C,D)
cvGEMM(A,B,1,C,1,D,0)
只有大小符合约束的矩阵才能进行乘法运算,并且所有的数据类型都应该是浮点型。cvGEMM()函数支持双通道矩阵,在这种情况下,它将双通道视为一个复数的两个部分。
cvGetCol和cvGetCols
CvMat*
cvGetCol(
const CvArr*
arr,
CvMat*
submat,
int col
);
CvMat*
cvGetCols(
const CvArr*
arr,
CvMat*
submat,
int
start_col,
int
end_col
);
cvGetCol()函数被用作提取矩阵中的某一列,并把它以向量的形式返回(即只有一列的矩阵)。在这种情况下,矩阵指针submat将被修改为指向arr中的特定列,必须指出的是,该指针在修改过程中并未涉及内存的分配或数据的复制;submat的内容仅仅是作了简单修改以使它正确地指出arr中所选择的列。它支持所有数据类型。cvGetCols()函数的工作原理与cvGetCols完全一致,区别只在于前者将选择从start_col到end_col之间的所有列。这两个函数都返回一个与被调用的特定列或者多列(即,submat)相对应的头指针。
cvGetDiag
CvMat*
cvGetDiag(
const CvArr*
arr,
CvMat*submat,
int diag=
0
);
cvGetDiag
()类似于cvGetCol();它能从一个矩阵选择某一条对角线并将其作为向量返回。submat是一个矩阵类型的头指针。函数cvGetDiag()将填充该向量头指针中的各分量,以使用指向arr中的正确信息。注意,调用cvGetDiag()会修改输入的头指针,将数据指针指向arr对角线上的数据,实际上,并没有复制arr的数据。可选参数diag表明submat指向哪一条对角线的。如果diag被设置为默认值0,主对角线将被选中。如果diag大于0,则始于(diag,0)的对角线将被选中,如果diag小于0,则始于(0,-diag)的对角线将被选中。cvGetDiag()并不要求矩阵arr是方阵,但是数组submat长度必须与输入数组的尺寸相匹配。当该函数被调用时,最终的返回结果与输入的submat相同。
cvGetDims和cvGetDimSize
int
cvGetDims(
const CvArr*
arr,
int*
sizes=NULL
);
int
cvGetDimSize(
const CvArr*
arr,
int index
);
您一定还记得OpenCV中的矩阵维数可以远远大于2。函数cvGetDims()返回指定数组的维数并可返回每一个维数的大小。如果数组sizes非空,那么大小将被写入sizes。如果使用了参数sizes,它应该是一个指向n个整数的指针,这里的n指维数。如果无法事先获知维数,为了安全起见,可以把sizes大小指定为CV_MAX_DIM。函数cvGetDimSize()返回一个由index参数指定的某一维大小。如果这个数组是矩阵或者图像,那么cvGetDims()将一直返回为2。对于矩阵和图像,由cvGetDims()返回的sizes的次序将总是先是行数然后是列数。
cvGetRow和cvGetRows
CvMat*
cvGetRow(
const CvArr*
arr,
CvMat*
submat,
int row
);
CvMat*
cvGetRows(
const CvArr*
arr,
CvMat*submat,
int
start_row,
int
end_row
);
cvGetRow()获取矩阵中的一行让它作为向量(仅有一行的矩阵)返回。跟cvGetCol()类似,矩阵头指针submat将被修改为指向arr中的某个特定行,并且对该头指针的修改不涉及内存的分配和数据的复制;submat的内容仅是作为适当的修改以使它正确地指向arr中所选择的行。该指针所有数据类型。cvGetRows()函数的工作原理与cvGetRow()完全一致,区别只在于前者将选择从start_row到end_row之间的所有行。这两个函数都返回一个的头指针,指向特定行或者多个行。
cvGetSize
CvSize cvGetSize(
const CvArr* arr );
它与cvGetDims()密切相关,cvGetDims()返回一个数组的大小。主要的不同是cvGetSize()是专为矩阵和图像设计的,这两种对象的维数总是2。其尺寸可以以CvSize结构的形式返回,例如当创建一个新的大小相同的矩阵或图像时,使用此函数就很方便。
cvGetSubRect
cvGetSubRect
CvSize
cvGetSubRect(
const CvArr*
arr,
CvArr*
submat,
CvRect
rect
);
cvGetSubRect()与cvGetColumns()或cvGetRows()非常类似,区别在于cvGetSubRect()通过参数rect在数组中选择一个任意的子矩阵。与其他选择数组子区域的函数的函数一样,submat仅仅是一个被cvGetSubRect()函数填充的头,它将指向用户期望的子矩阵数据,这里并不涉及内存分配和数据的复制。
cvInRange和cvInRangeS
void
cvInRange(const CvArr* src,
const CvArr*
lower,
const CvArr*
upper,
CvArr* dst
);
void
cvInRangeS(
const CvArr*
src,
CvScalar lower,
CvScalar upper,
CvArr* dst
);
这两个函数可用于检查图像中像素的灰度是否属于某一指定范围。cvInRange()检查,src的每一个像素点是否落在lower和upper范围中。如果src的值大于或者等于lower值,并且小于upper值,那么dst中对应的对应值将被设置为0xff;否则,dst的值将被设置为0。cvInRangeS()的原理与之完全相同,但src是与lower和upper中的一组常量值(类型CvScala)进行比较。对于这两个函数,图像src可以是任意类型;如果图像有多个通道,那么每一种通道都将被分别处理。注意,dst的尺寸和通道数必须与src一致,且必须为8位的图像。
cvInvert
double
cvInvert(
const CvArr*
src,
CvArr*
dst,
Int method =
CV_LU
);
cvInvert()求取保存在src中的矩阵的逆并把结果保存在dst中。这个函数支持使用多种方法来计算矩阵的逆(见表3-8),但默认采取的是高斯消去法。该函数的返回值与所选用的方法有关。
表3-8:cvInvert()函数中指定方法的参数值
方法的参数值
含义
CV_LU
高斯消去法 (LU
分解)
CV_SVD
奇异值分解(SVD)
CV_SVD_SYM
对称矩阵的SVD
就高斯消去法(method=CV_LU)来说,当函数执行完毕,src的行列式将被返回。如果行列式是0,那么事实上不进行求逆操作,并且数组dst将被设置为全0。
就CV_SVD或者CV_SVD_SYM,来说,返回值是矩阵的逆条件数(最小特征值跟最大特征值的比例)。如果src是奇异的,那么cvInvert()在SVD模式中将进行伪逆计算。
cvMahalonobis
CvSize
cvMahalonobis(
const CvArr*
vec1,
const CvArr*
vec2,
CvArr* mat
);
Mahalonobis距离(Mahal)被定义为一点和高斯分布中心之间的向量距离,该距离使用给定分布的协方差矩阵的逆作为归一化标准。参见图3-5。直观上,这是与基础统计学中的标准分数(Z-score)类似,某一点到分布中心的距离是以该分布的方差作为单位。马氏距离则是该思路在高维空间中的推广。
cvMahalonobis()计算的公式如下:
假设向量vec1对应x点,向量vec2是分布的均值。mat是协方差矩阵的逆。
实际上,这个协方差矩阵通常用cvCalcCovarMatrix()(前面所述)来进行计算,然后用cvInvert()来求逆。使用SV_SVD方法求逆是良好的程序设计习惯,因为其中一个特征值为0的分布这种情况在所难免!