OpenCV矩阵用法
来源:互联网 发布:欧洲卡车模拟2mac联机 编辑:程序博客网 时间:2024/06/06 03:35
以下转自三篇文章,留着明天看,由于老花,字体就加粗加大了
转自:http://blog.csdn.net/abcjennifer/article/details/7385917
有很多函数有mask,代表掩码,如果某位mask是0,那么对应的src的那一位就不计算,mask要和矩阵/ROI/的大小相等
大多数函数支持ROI,如果图像ROI被设置,那么只处理ROI部分
少部分函数支持COI,如果COI设置,只处理感兴趣的通道
矩阵逻辑运算
void cvAnd(const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL);//
void cvAndS(const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL);//
void cvOr(const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL);//
void cvOrS(const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL);//
void cvXor(const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL);//
void cvXorS(const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL);//
void cvNot(const CvArr* src,CvArr* dst);//矩阵取反
矩阵算术运算
绝对值
void cvAbs(const CvArr* src,CvArr* dst);
void cvAbsDiff(const CvArr* src1,const CvArr* src2, CvArr* dst);//两矩阵相减取绝对值
void cvAbsDiffS(const CvArr* src, CvArr* dst,CvScalar value);//矩阵减去一个数取绝对值
加减
void cvAdd(const CvArr* src1,const CvArr* src2,CvArr* dst,const CvArr* mask = NULL);//两数组相加,dst(I)=src1(I)+src2(I) if mask(I)!=0
void cvAddS(const CvArr* src,CvScalar value,CvArr*dst,const CvArr* mask = NULL);//数组和一个数相加,dst(I)=src(I)+value if mask(I)!=0
void cvAddWeighted(const CvArr* src1,double alpha,const CvArr* src2,double beta,double gamma,CvArradded to each sum* dst);//带权相加相当于dst(x,y) = α ? src1(x,y) + β ? src2(x,y) + γ
void cvSub(const CvArr* src1, const CvArr* src2, CvArr* dst, const CvArr* mask=NULL);//矩阵减法,dst(I)=src1(I)-src2(I) if mask(I)!=0
void cvSubS(const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL);//矩阵减数,dst(I)=src(I)-value if mask(I)!=0
void cvSubRS(const CvArr* src, CvScalar value, CvArr* dst, const CvArr* mask=NULL);//数减矩阵,dst(I)=value-src(I) if mask(I)!=0
乘除
void cvDiv(const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1);//scale*src1(i)/src2(i),如果src1=NULL,则计算scale/src2(i)
void cvMul(const CvArr* src1,const CvArr* src2,CvArr* dst,double scale=1);//两矩阵元素之间的简单乘法,一般的矩阵点乘用cvGEMM();
次方
void cvPow(const CvArr* src, CvArr* dst, double power);//为每个src的数求power次方
指数
void cvExp(const CvArr* src, CvArr* dst);//dst(I)=EXP(src(I))
对数
void cvLog(const CvArr* src, CvArr* dst);//
线性代数计算
加&乘
void cvScaleAdd(const CvArr* src1, CvScalar scale, const CvArr* src2, CvArr* dst);//src1和scale的乘积加上src2
void cvCrossProduct(const CvArr* src1,const CvArr* src2,CvArr* dst);//计算两个3D向量(单通道)的叉乘运算
double cvDotProduct(const CvArr* src1, const CvArr* src2);//两个向量点乘
void cvGEMM(const CvArr* src1, const CvArr* src2, double alpha, const CvArr* src3, double beta, CvArr* dst, int tABC=0);//乘加运算的始祖
由通用乘加函数参与定义的两个具体宏
cvMatMul(const CvArr* src1,const CvArr* src2,CvArr* dst);
cvMatMulAdd(const CvArr* src1,const CvArr* src2,const CvArr* src3,CvArr* dst);
CvScalar cvTrace(const CvArr* mat);//计算对角线上的元素和
变换
void cvTransform(const CvArr* src, CvArr* dst, const CvMat* transmat, const CvMat* shiftvec=NULL);//dst=transmat · src + shiftvec
void cvPerspectiveTransform(const CvArr* src, CvArr* dst, const CvMat* mat);//把矩阵每个元素中三个通道当做一个矩阵,乘mat,mat是一个3×3或者4×4的转换矩阵
转置
void cvTranspose(const CvArr* src, CvArr* dst);
void cvMulTransposed(const CvArr* src, CvArr* dst, int order, const CvArr* delta=NULL, double scale=1.0);//(src-delta)乘以它的转置再乘以scale
逆矩阵
double cvInvert(const CvArr* src,CvArr* dst,int method=CV_LU);//求原矩阵的逆矩阵,默认使用高斯消去法
方阵可逆的充要条件是|A|!=0
method取值为CV_LU高斯消去法(默认) CV_SVD 奇异值分解SVD CV_SVD_SYM对称矩阵的SVD
行列式
double cvDet(const CvArr* mat);//计算方阵行列式,一定是单通道的
小型方阵直接计算,大型方阵用高斯消去法计算
如果矩阵正定对称,用奇异值分解的方法解决cvSVD();
特征向量特征值
void cvEigenVV(CvArr* mat, CvArr* evects, CvArr* evals, double eps=0);//计算对称矩阵的特征值和特征向量,evects输出特征向量,evals输出特征值,eps雅可比方法停止参数
要求三个矩阵都是浮点类型,10×10以下该方法有效,20×20以上的矩阵不能计算出结果,为节约计算量,eps通常设为DBL_EPSILON(10^-15)
如果给定的矩阵是对称正定矩阵,那么考虑使用cvSVD();
协方差
void cvCalcCovarMatrix(const CvArr** vects, int count, CvArr* cov_mat, CvArr* avg, int flags);//给定一组大小和类型相同的向量,向量的个数,计算标记,输出协方差正阵和每个向量的平均值矩阵
CV_COVAR_NORMAL 普通计算协方差和平均值,输出的是n×n的协方差阵
CV_COVAR_SCRAMBLED 快速PCA“Scrambled”协方差,输出的是m×m的协方差阵
CV_COVAR_USE_AVERAGE 平均值是输入的
CV_COVAR_SCALE 重新缩放输出的协方差矩阵
四个flag通过并运算协同发挥作用,前两个不能并
CvSize cvMahalonobis(const CvArr* vec1,const CvArr* vec2,CvArr* mat);
int cvSolve(const CvArr* src1, const CvArr* src2, CvArr* dst, int method=CV_LU);//Solves a linear system or least-squares problem.
void cvSVD(CvArr* A, CvArr* W, CvArr* U=NULL, CvArr* V=NULL, int flags=0);//Performs singular value decomposition of a real floating-point matrix.
void cvSVBkSb(const CvArr* W, const CvArr* U, const CvArr* V, const CvArr* B, CvArr* X, int flags);//Performs singular value back substitution.
数组比较
void cvCmp(const CvArr* src1, const CvArr* src2, CvArr* dst, int cmp_op);//两矩阵比较运算
CV_CMP_EQ - src1(I) 是否相等
CV_CMP_GT - src1(I) 是否大于
CV_CMP_GE - src1(I) 是否大于等于
CV_CMP_LT - src1(I) 是否小于
CV_CMP_LE - src1(I) 是否小于等于
CV_CMP_NE - src1(I) 是否不等
如果判断为假,dst设为0,如果判断为真,dst设为0xff
void cvCmpS(const CvArr* src, double value, CvArr* dst, int cmp_op);//矩阵和一个数字比较运算
矩阵内转换
类型转换
void cvConvertScale(const CvArr* src,CvArr* dst,double scale,double shift);//矩阵首先乘以scale再加上shift,然后把src中的数据类型转换成dst类型,但是src和dst通道数需要相等
void cvConvertScaleAbs(const CvArr* src,CvArr* dst,double scale,double shift);//在src到dst类型转换前,先做绝对值
void cvCvtColor(const CvArr* src,CvArr* dst, int code);//图像 颜色空间转换,src要为8U 16U 32F,dst的数据类型需要和src相同,通道数看code
code格式如:CV_原色彩空间2目的色彩空间 色彩空间要考虑RGB的顺序
支持的颜色空间包括:RGB RGB565 RGB555 GRAY RGBA XYZ YCrCb HSV HLS Luv BayerRG
空间转换
void cvFlip(const CvArr* src, CvArr* dst=NULL, int flip_mode=0);//图像绕x、y轴旋转。当用在一维数组上时并且flip_mode>0,可以用来颠倒数据排列
flip_mode=0:左右对称values of the conversion resul
flip_mode>0:上下对称
flip_mode<0:中心对称
转自:http://blog.csdn.net/Sunshine_in_Moon/article/details/50250553
通用矩阵乘法
void cvGEMM( const CvArr* src1, const CvArr* src2, double alpha, const CvArr* src3, double beta, CvArr* dst, int tABC=0 ); #define cvMatMulAdd( src1, src2, src3, dst ) cvGEMM( src1, src2, 1, src3, 1, dst, 0 ) #define cvMatMul( src1, src2, dst ) cvMatMulAdd( src1, src2, 0, dst )
- src1
- 第一输入数组
- src2
- 第二输入数组
- src3
- 第三输入数组 (偏移量),如果没有偏移量,可以为空( NULL) 。
- dst
- 输出数组
- tABC
- T操作标志,可以是 0 或者下面列举的值的组合:
- CV_GEMM_A_T - 转置 src1
- CV_GEMM_B_T - 转置 src2
- CV_GEMM_C_T - 转置 src3
- 例如, CV_GEMM_A_T+CV_GEMM_C_T 对应
- alpha*src1T*src2 + beta*src3T
函数 cvGEMM 执行通用矩阵乘法:
dst = alpha*op(src1)*op(src2) + beta*op(src3), 这里 op(X) 是 X 或者 XT
所有的矩阵应该有相同的数据类型和协调的矩阵大小。支持实数浮点矩阵或者复数浮点矩阵。
Transform
对数组每一个元素执行矩阵变换
void cvTransform( const CvArr* src, CvArr* dst, const CvMat* transmat, const CvMat* shiftvec=NULL );
- src
- 输入数组
- dst
- 输出数组
- transmat
- 变换矩阵
- shiftvec
- 可选偏移向量
函数 cvTransform 对数组 src 每一个元素执行矩阵变换并将结果存储到 dst:
- dst(I)=transmat*src(I) + shiftvec
或者
- dst(I)k=sumj(transmat(k,j)*src(I)j) + shiftvec(k)
N-通道数组 src 的每一个元素都被视为一个N元向量,使用一个 M×N 的变换矩阵 transmat 和偏移向量 shiftvec 把它变换到一个 M-通道的数组 dst 的一个元素中。 这里可以选择将偏移向量 shiftvec 嵌入到 transmat 中。这样的话 transmat 应该是 M×N+1 的矩阵,并且最右边的一列被看作是偏移向量 。
输入数组和输出数组应该有相同的位深(depth)和同样的大小或者 ROI 大小。 transmat 和 shiftvec 应该是实数浮点矩阵。
该函数可以用来进行 ND 点集的几何变换,任意的线性颜色空间变换,通道转换等。
MulTransposed
计算数组和数组的转置的乘积
void cvMulTransposed( const CvArr* src, CvArr* dst, int order, const CvArr* delta=NULL );
- src
- 输入矩阵
- dst
- 目标矩阵
- order
- 乘法顺序
- delta
- 一个可选数组, 在乘法之前从 src 中减去该数组。
函数 cvMulTransposed 计算 src 和它的转置的乘积。
函数求值公式:
如果 order=0
- dst=(src-delta)*(src-delta)T
否则
- dst=(src-delta)T*(src-delta)
Trace
返回矩阵的迹
CvScalar cvTrace( const CvArr* mat );
- mat
- 输入矩阵
函数 cvTrace 返回矩阵mat的对角线元素的和。
- tr(src) =∑mat(i,i)
i
Transpose
矩阵的转置
void cvTranspose( const CvArr* src, CvArr* dst ); #define cvT cvTranspose
- src
- 输入矩阵
- dst
- 目标矩阵
函数 cvTranspose 对矩阵 src 求转置:
- dst(i,j)=src(j,i)
注意,假设是复数矩阵不会求得复数的共轭。共轭应该是独立的:查看的 cvXorS 例子代码。
Det
返回矩阵的行列式值
double cvDet( const CvArr* mat );
- mat
- 输入矩阵
函数 cvDet 返回方阵 mat 的行列式值。对小矩阵直接计算,对大矩阵用高斯(GAUSSIAN)消去法。对于对称正定(positive-determined)矩阵也可以用 SVD 函数来求,U=V=NULL ,然后用 w 的对角线元素的乘积来计算行列式。
Invert
查找矩阵的逆矩阵或伪逆矩阵
double cvInvert( const CvArr* src, CvArr* dst, int method=CV_LU ); #define cvInv cvInvert
- src
- 输入矩阵
- dst
- 目标矩阵
- method
- 求逆方法:
- CV_LU -最佳主元选取的高斯消除法
- CV_SVD - 奇异值分解法 (SVD)
- CV_SVD_SYM - 正定对称矩阵的 SVD 方法
函数 cvInvert 对矩阵 src 求逆并将结果存储到 dst。
如果是 LU 方法该函数返回 src 的行列式值 (src 必须是方阵)。 如果是 0, 矩阵不求逆, dst 用 0 填充。
如果 SVD 方法该函数返回 src 的条件数的倒数(最小奇异值和最大奇异值的比值) ,如果 src 全为 0 则返回0。 如果 src 是奇异的, SVD 方法计算一个伪逆矩阵。
Solve
求解线性系统或者最小二乘法问题
int cvSolve( const CvArr* src1, const CvArr* src2, CvArr* dst, int method=CV_LU );
- src1
- 输入矩阵
- src2
- 线性系统的右部
- dst
- 输出解答
- method
- 解决方法(矩阵求逆) :
- CV_LU - 最佳主元选取的高斯消除法
- CV_SVD - 奇异值分解法 (SVD)
- CV_SVD_SYM - 对正定对称矩阵的 SVD 方法
函数 cvSolve 解决线性系统或者最小二乘法问题 (后者用 SVD 方法可以解决):
如果使用 CV_LU 方法。 如果 src1 是非奇异的,该函数则返回 1 ,否则返回 0 ,在后一种情况下 dst 是无效的。
SVD
对实数浮点矩阵进行奇异值分解
void cvSVD( CvArr* A, CvArr* W, CvArr* U=NULL, CvArr* V=NULL, int flags=0 );
- A
- M×N 的输入矩阵
- W
- 结果奇异值矩阵 (M×N 或者 N×N) 或者 向量 (N×1).
- U
- 可选的左部正交矩阵 (M×M or M×N). 如果 CV_SVD_U_T 被指定,应该交换上面所说的行与列的数目。
- V
- 可选右部正交矩阵(N×N)
- flags
- 操作标志; 可以是 0 或者下面的值的组合:
- CV_SVD_MODIFY_A 通过操作可以修改矩阵 src1 。这样处理速度会比较快。
- CV_SVD_U_T 意味着会返回转置矩阵 U ,指定这个标志将加快处理速度。
- CV_SVD_V_T 意味着会返回转置矩阵 V ,指定这个标志将加快处理速度。
函数 cvSVD 将矩阵 A 分解成一个对角线矩阵和两个正交矩阵的乘积:
这里 W 是一个奇异值的对角线矩阵,它可以被编码成奇异值的一维向量,U 和 V 也是一样。所有的奇异值都是非负的并按降序存储。(U 和 V 也相应的存储)。
SVD 算法在数值处理上已经很稳定,它的典型应用包括:
- 当 A 是一个方阵、对称阵和正矩阵时精确的求解特征值问题,例如, 当 A 时一个协方差矩阵时。在这种情况下 W 将是一个特征值的的向量,并且 U=V是矩阵的特征向量(因此,当需要计算特征向量时 U 和 V 只需要计算其中一个就可以了) 。
- 精确的求解病态线性系统。
- 超定线性系统的最小二乘求解。上一个问题和这个问题都可以用指定 CV_SVD 的 cvSolve 方法。
- 精确计算矩阵的不同特征,如秩(非零奇异值的数目), 条件数(最大奇异值和最小奇异值的比例), 行列式值(行列式的绝对值等于奇异值的乘积).上述的所有这些值都不要求计算矩阵 U 和 V 。
SVBkSb
奇异值回代算法(back substitution)
void cvSVBkSb( const CvArr* W, const CvArr* U, const CvArr* V, const CvArr* B, CvArr* X, int flags );
- W
- 奇异值矩阵或者向量
- U
- 左正交矩阵 (可能是转置的)
- V
- 右正交矩阵 (可能是转置的)
- B
- 原始矩阵 A 的伪逆的乘法矩阵。这个是可选参数。如果它被省略则假定它是一个适当大小的单位矩阵(因此 x 将是 A 的伪逆的重建).。
- X
- 目标矩阵: 奇异值回代算法的结果
- flags
- 操作标志, 和刚刚讨论的 cvSVD 的标志一样。
函数 cvSVBkSb 为被分解的矩阵 A 和矩阵 B 计算回代逆(back substitution) (参见 cvSVD 说明) :
- X=V*W-1*UT*B
这里
- W-1(i,i)=1/W(i,i) 如果 W(i,i) > epsilon•sumiW(i,i),
- 否则:0.
epsilon 是一个依赖于矩阵数据类型的的很小的数。该函数和 cvSVD 函数被用来执行 cvInvert 和 cvSolve, 用这些函数 (svd & bksb)的原因是初级函数(low-level)函数可以避免高级函数 (inv & solve) 计算中内部分配的临时矩阵。
EigenVV
计算对称矩阵的特征值和特征向量
void cvEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, double eps=0 );
- mat
- 输入对称方阵。在处理过程中将被改变。
- evects
- 特征向量输出矩阵, 连续按行存储
- evals
- 特征值输出矩阵,按降序存储(当然特征值和特征向量的排序是同步的)。
- eps
- 对角化的精确度 (典型地, DBL_EPSILON=≈10-15 就足够了)。
函数 cvEigenVV 计算矩阵 A 的特征值和特征向量:
mat*evects(i,:)' = evals(i)*evects(i,:)' (在 MATLAB 的记法)
矩阵 A 的数据将会被这个函数修改。
目前这个函数比函数 cvSVD 要慢,精确度要低, 如果已知 A 是正定的,(例如, 它是一个协方差矩阵), 它通常被交给函数 cvSVD 来计算其特征值和特征向量,尤其是在不需要计算特征向量的情况下
CalcCovarMatrix
计算向量集合的协方差矩阵
void cvCalcCovarMatrix( const CvArr** vects, int count, CvArr* cov_mat, CvArr* avg, int flags );
- vects
- 输入向量。他们必须有同样的数据类型和大小。这个向量不一定非是一维的,他们也可以是二维(例如,图像)等等。
- count
- 输入向量的数目
- cov_mat
- 输出协方差矩阵,它是浮点型的方阵。
- avg
- 输入或者输出数组 (依赖于标记“flags”) - 输入向量的平均向量。
- flags
- 操作标志,下面值的组合:
- CV_COVAR_SCRAMBLED - 输出协方差矩阵按下面计算:
- scale * [vects[0] − avg,vects[1] − avg,...]T * [vects[0] − avg,vects[1] − avg,...], 即协方差矩阵是 count×count. 这样一个不寻常的矩阵用于一组大型向量的快速PCA方法(例如, 人脸识别的 EigenFaces 技术)。这个混杂("scrambled")矩阵的特征值将和真正的协方差矩阵的特征值匹配,真正的特征向量可以很容易的从混杂("scrambled")协方差矩阵的特征向量中计算出来。
- CV_COVAR_NORMAL - 输出协方差矩阵被计算成:
- scale * [vects[0] − avg,vects[1] − avg,...] * [vects[0] − avg,vects[1] − avg,...]T, 也就是说, cov_mat 将是一个和每一个输入向量的元素数目具有同样线性大小的通常协方差矩阵。 CV_COVAR_SCRAMBLED 和 CV_COVAR_NORMAL 只能同时指定其中一个。
- CV_COVAR_USE_AVG - 如果这个标志被指定, 该函数将不会从输入向量中计算 avg ,而是用过去的 avg 向量,如果 avg 已经以某种方式计算出来了这样做是很有用的。或者如果协方差矩阵是部分计算出来的 - 倘若这样, avg 不是输入向量的子集的平均值,而是整个集合的平均向量。
- CV_COVAR_SCALE - 如果这个标志被指定,协方差矩阵被缩放了。 the covariation matrix is scaled.在 "normal" 模式下缩放比例是 1./count, 在 "scrambled" 模式下缩放比例是每一个输入向量的元素总和的倒数。 缺省地(如果没有指定标志) 协方差矩阵不被缩放 (scale=1)。
- CV_COVAR_SCRAMBLED - 输出协方差矩阵按下面计算:
函数 cvCalcCovarMatrix 计算输入向量的协方差矩阵和平均向量。该函数可以被运用到主成分分析中(PCA),以及马氏距离(Mahalanobis distance)比较向量中等等。
Mahalanobis
计算两个向量之间的马氏距离(Mahalanobis distance)
double cvMahalanobis( const CvArr* vec1, const CvArr* vec2, CvArr* mat );
- vec1
- 第一个一维输入向量
- vec2
- 第二个一维输入向量
- mat
- 协方差矩阵的逆矩阵
函数 cvMahalanobis 计算两个向量之间的加权距离,其返回结果是:
协方差矩阵可以用函数cvCalcCovarMatrix 计算出来,逆矩阵可以用函数 cvInvert 计算出来 (CV_SVD 方法是一个比较好的选择, 因为矩阵可能是奇异的).
CalcPCA
对一个向量集做PCA变换
void cvCalcPCA( const CvArr* data, CvArr* avg, CvArr* eigenvalues, CvArr* eigenvectors, int flags );
- data
- 输入数据,每个向量是单行向量(CV_PCA_DATA_AS_ROW)或者单列向量(CV_PCA_DATA_AS_COL).
- avg
- 平均向量,在函数内部计算或者由调用者提供
- eigenvalues
- 输出的协方差矩阵的特征值
- eigenvectors
- 输出的协方差矩阵的特征向量(也就是主分量),每个向量一行
- flags
- 操作标志,可以是以下几种方式的组合:
- CV_PCA_DATA_AS_ROW - 向量以行的方式存放(也就是说任何一个向量都是连续存放的)
- CV_PCA_DATA_AS_COL - 向量以列的方式存放(也就是说某一个向量成分的数值是连续存放的)
- (上面两种标志是互相排斥的)
- CV_PCA_USE_AVG - 使用预先计算好的平均值
该函数对某个向量集做PCA变换.它首先利用cvCalcCovarMatrix计算协方差矩阵然后计算协方差矩阵的特征值与特征向量.输出的特征值/特征向量的个数小于或者等于MIN(rows(data),cols(data)).
ProjectPCA
把向量向某个子空间投影
void cvProjectPCA( const CvArr* data, const CvArr* avg, const CvArr* eigenvectors, CvArr* result )
- data
- 输入数据,每个向量可以是单行或者单列
- avg
- 平均向量.要么它是单行向量那么意味着输入数据以行数据的形式存放,要么就是单列向量,那么就意味着那么输入向量就是以列的方式存放.
- eigenvectors
- 特征向量(主分量),每个向量一行.
- result
- 输出的分解系数矩阵,矩阵的行数必须与输入向量的个数相等,矩阵的列数必须小于特征向量的行数.
该函数将输入向量向一个正交系(eigenvectors)投影.在计算点乘之前,输入向量要减去平均向量:
result(i,:)=(data(i,:)-avg)*eigenvectors' // for CV_PCA_DATA_AS_ROW layout.
BackProjectPCA
根据投影系数重构原来的向量
void cvBackProjectPCA( const CvArr* proj, const CvArr* avg, const CvArr* eigenvects, CvArr* result );
- proj
- 输入数据,与cvProjectPCA里面的格式一致
- avg
- 平均向量.如果它是单行向量,那么意味着输出向量是以行的方式存放.否则就是单列向量,那么输出向量就是以列的方式存放.
- eigenvectors
- 特征向量(主分量),每个向量一行.
- result
- 输出的重构出来的矩阵
该函数根据投影系数重构原来的向量:
result(i,:)=proj(i,:)*eigenvectors + avg // for CV_PCA_DATA_AS_ROW layout
附
矩阵操作
分配释放矩阵空间
- 综述:
- OpenCV有针对矩阵操作的C语言函数. 许多其他方法提供了更加方便的C++接口,其效率与OpenCV一样.
- OpenCV将向量作为1维矩阵处理.
- 矩阵按行存储,每行有4字节的校整.
- 分配矩阵空间:
CvMat* cvCreateMat(int rows, int cols, int type);
type: 矩阵元素类型. 格式为CV_<bit_depth>(S|U|F)C<number_of_channels>. 例如: CV_8UC1 表示8位无符号单通道矩阵, CV_32SC2表示32位有符号双通道矩阵. 例程: CvMat* M = cvCreateMat(4,4,CV_32FC1); - 释放矩阵空间:
CvMat* M = cvCreateMat(4,4,CV_32FC1);cvReleaseMat(&M);
- 复制矩阵:
CvMat* M1 = cvCreateMat(4,4,CV_32FC1);CvMat* M2;M2=cvCloneMat(M1);
- 初始化矩阵:
double a[] = { 1,
2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; CvMat Ma=cvMat(3, 4, CV_64FC1, a); 另一种方法:
CvMat Ma;cvInitMatHeader(&Ma, 3, 4, CV_64FC1, a);
- 初始化矩阵为单位阵:
CvMat* M = cvCreateMat(4,4,CV_32FC1);cvSetIdentity(M); // 这里似乎有问题,不成功
存取矩阵元素
- 假设需要存取一个2维浮点矩阵的第(i,j)个元素.
- 间接存取矩阵元素:
cvmSet(M,i,j,2.0); // Set M(i,j)t = cvmGet(M,i,j); // Get M(i,j)
- 直接存取,假设使用4-字节校正:
CvMat* M
= cvCreateMat(4,4,CV_32FC1);int n = M->cols;float *data = M->data.fl; data[i*n+j] = 3.0; - 直接存取,校正字节任意:
CvMat* M
= cvCreateMat(4,4,CV_32FC1);int step = M->step/sizeof(float);float *data = M->data.fl; (data+i*step)[j] = 3.0; - 直接存取一个初始化的矩阵元素:
double a[16];CvMat Ma = cvMat(3, 4, CV_64FC1, a);a[i*4+j] = 2.0; // Ma(i,j)=2.0;
矩阵/向量操作
- 矩阵-矩阵操作:
CvMat *Ma, *Mb, *Mc;cvAdd(Ma, Mb, Mc);
// Ma+Mb -> MccvSub(Ma, Mb, Mc); // Ma-Mb -> MccvMatMul(Ma, Mb, Mc); // Ma*Mb -> Mc - 按元素的矩阵操作:
CvMat *Ma, *Mb, *Mc;cvMul(Ma, Mb, Mc);
// Ma.*Mb -> MccvDiv(Ma, Mb, Mc); // Ma./Mb -> MccvAddS(Ma, cvScalar(-10.0), Mc); // Ma.-10 -> Mc - 向量乘积:
double va[] = {1, 2, 3};double vb[] = {0, 0, 1};double vc[3]; CvMat Va=cvMat(3, 1, CV_64FC1, va);CvMat Vb=cvMat(3, 1, CV_64FC1, vb);CvMat Vc=cvMat(3, 1, CV_64FC1, vc); double res=cvDotProduct(&Va,&Vb); // 点乘:
Va . Vb -> rescvCrossProduct(&Va, &Vb, &Vc); // 向量积: Va x Vb -> Vcend{verbatim} 注意 Va, Vb, Vc 在向量积中向量元素个数须相同.
- 单矩阵操作:
CvMat *Ma, *Mb;cvTranspose(Ma, Mb);
// transpose(Ma) -> Mb (不能对自身进行转置)CvScalar t = cvTrace(Ma); // trace(Ma) -> t.val[0] double d = cvDet(Ma); // det(Ma) -> dcvInvert(Ma, Mb); // inv(Ma) -> Mb - 非齐次线性系统求解:
CvMat* A
= cvCreateMat(3,3,CV_32FC1);CvMat* x = cvCreateMat(3,1,CV_32FC1);CvMat* b = cvCreateMat(3,1,CV_32FC1);cvSolve(&A, &b, &x); // solve (Ax=b) for x - 特征值分析(针对对称矩阵):
CvMat* A
= cvCreateMat(3,3,CV_32FC1);CvMat* E = cvCreateMat(3,3,CV_32FC1);CvMat* l = cvCreateMat(3,1,CV_32FC1);cvEigenVV(&A, &E, &l); // l = A的特征值 (降序排列) // E = 对应的特征向量 (每行) - 奇异值分解SVD:
CvMat* A
= cvCreateMat(3,3,CV_32FC1);CvMat* U = cvCreateMat(3,3,CV_32FC1);CvMat* D = cvCreateMat(3,3,CV_32FC1);CvMat* V = cvCreateMat(3,3,CV_32FC1);cvSVD(A, D, U, V, CV_SVD_U_T|CV_SVD_V_T); // A = U D V^T 标号使得 U 和 V 返回时被转置(若没有转置标号,则有问题不成功!!!).
视频序列操作
从视频序列中抓取一帧
- OpenCV支持从摄像头或视频文件(AVI)中抓取图像.
- 从摄像头获取初始化:
CvCapture* capture = cvCaptureFromCAM(0); // capture from video device #0
- 从视频文件获取初始化:
CvCapture* capture = cvCaptureFromAVI("infile.avi");
- 抓取帧:
IplImage* img = 0; if(!cvGrabFrame(capture)){
// 抓取一帧 printf("Could not grab a frame\n\7"); exit(0);}img=cvRetrieveFrame(capture); // 恢复获取的帧图像 要从多个摄像头同时获取图像, 首先从每个摄像头抓取一帧. 在抓取动作都结束后再恢复帧图像.
- 释放抓取源:
cvReleaseCapture(&capture);
注意由设备抓取的图像是由capture函数自动分配和释放的. 不要试图自己释放它.
获取/设定帧信息
- 获取设备特性:
cvQueryFrame(capture); // this call is necessary to get correct
// capture propertiesint frameH = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT);int frameW = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH);int fps = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);int numFrames = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_COUNT); 所有帧数似乎只与视频文件有关. 用摄像头时不对,奇怪!!!.
- 获取帧信息:
float posMsec
= cvGetCaptureProperty(capture, CV_CAP_PROP_POS_MSEC);int posFrames = (int) cvGetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES);float posRatio = cvGetCaptureProperty(capture, CV_CAP_PROP_POS_AVI_RATIO); 获取所抓取帧在视频序列中的位置, 从首帧开始按[毫秒]算. 或者从首帧开始从0标号, 获取所抓取帧的标号. 或者取相对位置,首帧为0,末帧为1, 只对视频文件有效.
- 设定所抓取的第一帧标号:
// 从视频文件相对位置0.9处开始抓取cvSetCaptureProperty(capture, CV_CAP_PROP_POS_AVI_RATIO, (double)0.9);
只对从视频文件抓取有效. 不过似乎也不成功!!!
存储视频文件
- 初始化视频存储器:
CvVideoWriter *writer = 0;int isColor = 1;int fps
= 25; // or 30int frameW = 640; // 744 for firewire camerasint frameH = 480; // 480 for firewire cameraswriter=cvCreateVideoWriter("out.avi",CV_FOURCC('P','I','M','1'), fps,cvSize(frameW,frameH),isColor); 其他有效编码:
CV_FOURCC('P','I','M','1')
= MPEG-1 codecCV_FOURCC('M','J','P','G') = motion-jpeg codec (does not work well)CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codecCV_FOURCC('D', 'I', 'V', '3') = MPEG-4.3 codecCV_FOURCC('D', 'I', 'V', 'X') = MPEG-4 codecCV_FOURCC('U', '2', '6', '3') = H263 codecCV_FOURCC('I', '2', '6', '3') = H263I codecCV_FOURCC('F', 'L', 'V', '1') = FLV1 codec 若把视频编码设为-1则将打开一个编码选择窗口(windows系统下).
- 存储视频文件:
IplImage* img = 0; int nFrames = 50;for(i=0;i<nFrames;i++){
cvGrabFrame(capture); // 抓取帧 img=cvRetrieveFrame(capture); // 恢复图像 cvWriteFrame(writer,img); // 将帧添加入视频文件} 若想在抓取中查看抓取图像, 可在循环中加入下列代码:
cvShowImage("mainWin", img); key=cvWaitKey(20);
// wait 20 ms 若没有20[毫秒]延迟,将无法正确显示视频序列.
- 释放视频存储器:
cvReleaseVideoWriter(&writer);
作者:gnuhpc
出处:http://www.cnblogs.com/gnuhpc/
1.初始化矩阵:
方式一、逐点赋值式:
CvMat* mat = cvCreateMat( 2, 2, CV_64FC1 );
cvZero( mat );
cvmSet( mat, 0, 0, 1 );
cvmSet( mat, 0, 1, 2 );
cvmSet( mat, 1, 0, 3 );
cvmSet( mat, 2, 2, 4 );
cvReleaseMat( &mat );
方式二、连接现有数组式:
double a[] = { 1, 2, 3, 4,
5, 6, 7, 8,
9, 10, 11, 12 };
CvMat mat = cvMat( 3, 4, CV_64FC1, a ); // 64FC1 for double
// 不需要cvReleaseMat,因为数据内存分配是由double定义的数组进行的。
2.IplImage 到cvMat的转换
方式一、cvGetMat方式:
CvMat mathdr, *mat = cvGetMat( img, &mathdr );
方式二、cvConvert方式:
CvMat *mat = cvCreateMat( img->height, img->width, CV_64FC3 );
cvConvert( img, mat );
// #define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 )
3.cvArr(IplImage或者cvMat)转化为cvMat
方式一、cvGetMat方式:
int coi = 0;
cvMat *mat = (CvMat*)arr;
if( !CV_IS_MAT(mat) )
{
mat = cvGetMat( mat, &matstub, &coi );
if (coi != 0) reutn; // CV_ERROR_FROM_CODE(CV_BadCOI);
}
写成函数为:
// This is just an example of function
// to support both IplImage and cvMat as an input
CVAPI( void ) cvIamArr( const CvArr* arr )
{
CV_FUNCNAME( "cvIamArr" );
__BEGIN__;
CV_ASSERT( mat == NULL );
CvMat matstub, *mat = (CvMat*)arr;
int coi = 0;
if( !CV_IS_MAT(mat) )
{
CV_CALL( mat = cvGetMat( mat, &matstub, &coi ) );
if (coi != 0) CV_ERROR_FROM_CODE(CV_BadCOI);
}
// Process as cvMat
__END__;
}
4.图像直接操作
方式一:直接数组操作 int col, row, z;
uchar b, g, r;
for( y = 0; row < img->height; y++ )
{
for ( col = 0; col < img->width; col++ )
{
b = img->imageData[img->widthStep * row + col * 3]
g = img->imageData[img->widthStep * row + col * 3 + 1];
r = img->imageData[img->widthStep * row + col * 3 + 2];
}
}
方式二:宏操作:
int row, col;
uchar b, g, r;
for( row = 0; row < img->height; row++ )
{
for ( col = 0; col < img->width; col++ )
{
b = CV_IMAGE_ELEM( img, uchar, row, col * 3 );
g = CV_IMAGE_ELEM( img, uchar, row, col * 3 + 1 );
r = CV_IMAGE_ELEM( img, uchar, row, col * 3 + 2 );
}
}
注:CV_IMAGE_ELEM( img, uchar, row, col * img->nChannels + ch )
5.cvMat的直接操作
数组的直接操作比较郁闷,这是由于其决定于数组的数据类型。
对于CV_32FC1 (1 channel float):
CvMat* M = cvCreateMat( 4, 4, CV_32FC1 );
M->data.fl[ row * M->cols + col ] = (float)3.0;
对于CV_64FC1 (1 channel double):
CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
M->data.db[ row * M->cols + col ] = 3.0;
一般的,对于1通道的数组:
CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
CV_MAT_ELEM( *M, double, row, col ) = 3.0;
注意double要根据数组的数据类型来传入,这个宏对多通道无能为力。
对于多通道:
看看这个宏的定义:#define CV_MAT_ELEM_CN( mat, elemtype, row, col ) /
(*(elemtype*)((mat).data.ptr + (size_t)(mat).step*(row) + sizeof(elemtype)*(col)))
if( CV_MAT_DEPTH(M->type) == CV_32F )
CV_MAT_ELEM_CN( *M, float, row, col * CV_MAT_CN(M->type) + ch ) = 3.0;
if( CV_MAT_DEPTH(M->type) == CV_64F )
CV_MAT_ELEM_CN( *M, double, row, col * CV_MAT_CN(M->type) + ch ) = 3.0;
更优化的方法是:
#define CV_8U 0
#define CV_8S 1
#define CV_16U 2
#define CV_16S 3
#define CV_32S 4
#define CV_32F 5
#define CV_64F 6
#define CV_USRTYPE1 7
int elem_size = CV_ELEM_SIZE( mat->type );
for( col = start_col; col < end_col; col++ ) {
for( row = 0; row < mat->rows; row++ ) {
for( elem = 0; elem < elem_size; elem++ ) {
(mat->data.ptr + ((size_t)mat->step * row) + (elem_size * col))[elem] =
(submat->data.ptr + ((size_t)submat->step * row) + (elem_size * (col - start_col)))[elem];
}
}
}
对于多通道的数组,以下操作是推荐的:
for(row=0; row< mat->rows; row++)
{
p = mat->data.fl + row * (mat->step/4);
/* 除以4是因为一个float占4个字节,若为double则除以8,uchar不除*/
for(col = 0; col < mat->cols; col++)
{
*p = (float) row+col;
*(p+1) = (float) row+col+1;
*(p+2) =(float) row+col+2;
p+=3;
}
}
对于两通道和四通道而言:
CvMat* vector = cvCreateMat( 1, 3, CV_32SC2 );
CV_MAT_ELEM( *vector, CvPoint, 0, 0 ) = cvPoint(100,100);
CvMat* vector = cvCreateMat( 1, 3, CV_64FC4 );
CV_MAT_ELEM( *vector, CvScalar, 0, 0 ) = cvScalar(0,0,0,0);
6.间接访问cvMat
cvmGet/Set是访问CV_32FC1 和 CV_64FC1型数组的最简便的方式,其访问速度和直接访问几乎相同
cvmSet( mat, row, col, value );
cvmGet( mat, row, col );
举例:打印一个数组
inline void cvDoubleMatPrint( const CvMat* mat )
{
int i, j;
for( i = 0; i < mat->rows; i++ )
{
for( j = 0; j < mat->cols; j++ )
{
printf( "%f ",cvmGet( mat, i, j ) );
}
printf( "/n" );
}
}
而对于其他的,比如是多通道的后者是其他数据类型的,cvGet/Set2D是个不错的选择
CvScalar scalar = cvGet2D( mat, row, col );
cvSet2D( mat, row, col, cvScalar( r, g, b ) );
注意:数据不能为int,因为cvGet2D得到的实质是double类型。
举例:打印一个多通道矩阵:
inline void cv3DoubleMatPrint( const CvMat* mat )
{
int i, j;
for( i = 0; i < mat->rows; i++ )
{
for( j = 0; j < mat->cols; j++ )
{
CvScalar scal = cvGet2D( mat, i, j );
printf( "(%f,%f,%f) ", scal.val[0], scal.val[1], scal.val[2] );
}
printf( "/n" );
}
}
7.修改矩阵的形状——cvReshape的操作
经实验表明矩阵操作的进行的顺序是:首先满足通道,然后满足列,最后是满足行。
注意:这和Matlab是不同的,Matlab是行、列、通道的顺序。
我们在此举例如下:
对于一通道:
// 1 channel
CvMat *mat, mathdr;
double data[] = { 11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34 };
CvMat* orig = &cvMat( 3, 4, CV_64FC1, data );
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, 1, 1 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11 12 13 14 21 22 23 24 31 32 33 34
mat = cvReshape( mat, &mathdr, 1, 3 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, 1, 12 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11
// 12
// 13
// 14
// 21
// 22
// 23
// 24
// 31
// 32
// 33
// 34
mat = cvReshape( mat, &mathdr, 1, 3 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, 1, 2 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14 21 22
//23 24 31 32 33 34
mat = cvReshape( mat, &mathdr, 1, 3 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, 1, 6 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11 12
// 13 14
// 21 22
// 23 24
// 31 32
// 33 34
mat = cvReshape( mat, &mathdr, 1, 3 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
// Use cvTranspose and cvReshape( mat, &mathdr, 1, 2 ) to get
// 11 23
// 12 24
// 13 31
// 14 32
// 21 33
// 22 34
// Use cvTranspose again when to recover
对于三通道
// 3 channels
CvMat mathdr, *mat;
double data[] = { 111, 112, 113, 121, 122, 123,
211, 212, 213, 221, 222, 223 };
CvMat* orig = &cvMat( 2, 2, CV_64FC3, data );
//(111,112,113) (121,122,123)
//(211,212,213) (221,222,223)
mat = cvReshape( orig, &mathdr, 3, 1 ); // new_ch, new_rows
cv3DoubleMatPrint( mat ); // above
// (111,112,113) (121,122,123) (211,212,213) (221,222,223)
// concatinate in column first order
mat = cvReshape( orig, &mathdr, 1, 1 );// new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 111 112 113 121 122 123 211 212 213 221 222 223
// concatinate in channel first, column second, row third
mat = cvReshape( orig, &mathdr, 1, 3); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//111 112 113 121
//122 123 211 212
//213 221 222 223
// channel first, column second, row third
mat = cvReshape( orig, &mathdr, 1, 4 ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//111 112 113
//121 122 123
//211 212 213
//221 222 223
// channel first, column second, row third
// memorize this transform because this is useful to
// add (or do something) color channels
CvMat* mat2 = cvCreateMat( mat->cols, mat->rows, mat->type );
cvTranspose( mat, mat2 );
cvDoubleMatPrint( mat2 ); // above
//111 121 211 221
//112 122 212 222
//113 123 213 223
cvReleaseMat( &mat2 );
8.计算色彩距离
我们要计算img1,img2的每个像素的距离,用dist表示,定义如下
IplImage *img1 = cvCreateImage( cvSize(w,h), IPL_DEPTH_8U, 3 );
IplImage *img2 = cvCreateImage( cvSize(w,h), IPL_DEPTH_8U, 3 );
CvMat *dist = cvCreateMat( h, w, CV_64FC1 );
比较笨的思路是:cvSplit->cvSub->cvMul->cvAdd
代码如下:
IplImage *img1B = cvCreateImage( cvGetSize(img1), img1->depth, 1 );
IplImage *img1G = cvCreateImage( cvGetSize(img1), img1->depth, 1 );
IplImage *img1R = cvCreateImage( cvGetSize(img1), img1->depth, 1 );
IplImage *img2B = cvCreateImage( cvGetSize(img1), img1->depth, 1 );
IplImage *img2G = cvCreateImage( cvGetSize(img1), img1->depth, 1 );
IplImage *img2R = cvCreateImage( cvGetSize(img1), img1->depth, 1 );
IplImage *diff = cvCreateImage( cvGetSize(img1), IPL_DEPTH_64F, 1 );
cvSplit( img1, img1B, img1G, img1R );
cvSplit( img2, img2B, img2G, img2R );
cvSub( img1B, img2B, diff );
cvMul( diff, diff, dist );
cvSub( img1G, img2G, diff );
cvMul( diff, diff, diff);
cvAdd( diff, dist, dist );
cvSub( img1R, img2R, diff );
cvMul( diff, diff, diff );
cvAdd( diff, dist, dist );
cvReleaseImage( &img1B );
cvReleaseImage( &img1G );
cvReleaseImage( &img1R );
cvReleaseImage( &img2B );
cvReleaseImage( &img2G );
cvReleaseImage( &img2R );
cvReleaseImage( &diff );
比较聪明的思路是
int D = img1->nChannels; // D: Number of colors (dimension)
int N = img1->width * img1->height; // N: number of pixels
CvMat mat1hdr, *mat1 = cvReshape( img1, &mat1hdr, 1, N ); // N x D(colors)
CvMat mat2hdr, *mat2 = cvReshape( img2, &mat2hdr, 1, N ); // N x D(colors)
CvMat diffhdr, *diff = cvCreateMat( N, D, CV_64FC1 ); // N x D, temporal buff
cvSub( mat1, mat2, diff );
cvMul( diff, diff, diff );
dist = cvReshape( dist, &disthdr, 1, N ); // nRow x nCol to N x 1
cvReduce( diff, dist, 1, CV_REDUCE_SUM ); // N x D to N x 1
dist = cvReshape( dist, &disthdr, 1, img1->height ); // Restore N x 1 to nRow x nCol
cvReleaseMat( &diff );
- OpenCV矩阵用法
- opencv矩阵元素类型转换和cvConvertScale()函数用法
- opencv 矩阵
- opencv 矩阵
- OpenCV----矩阵操作
- OpenCV 矩阵操作 CvMat
- opencv CvMat矩阵学习
- OPENCV矩阵操作
- OpenCV 矩阵访问
- opencv-矩阵操作总结
- OpenCV 保存矩阵
- OpenCV 矩阵操作
- opencv 矩阵学习
- OpenCV图像、矩阵、数组
- opencv CvMat矩阵学习
- OPENCV 矩阵操作
- Opencv矩阵操作
- OpenCv矩阵操作
- 极简笔记:Dynamic Routing Between Capsule
- CentOS 7之firewalld
- [7]_编译内核错误提示2:recipe for target 'kernel/timeconst.h' failed 和 "mkimage" command not found
- 位置无关码、链接地址与加载地址
- 从tomcat移植到weblogic中session丢失的解决法
- OpenCV矩阵用法
- MatLab学习1-矩阵操作
- 70. Climbing Stairs
- Ubuntu 16.04 + cuda8.0 + opencv3 + caffe的安装,tensorflow-gpu的安装
- Java常用的框架介绍
- L
- HDOJ1004 Let the Balloon Rise
- JavaSE基础知识学习—Object类
- jzoj 3467. 【NOIP2013模拟联考7】最长上升子序列(lis) dfs+lis+手工栈