通用矩阵乘法
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 )
函数 cvGEMM 执行通用矩阵乘法:
dst = alpha*op(src1)*op(src2) + beta*op(src3), 这里 op(X) 是 X 或者 XT
所有的矩阵应该有相同的数据类型和协调的矩阵大小。支持实数浮点矩阵或者复数浮点矩阵。
对数组每一个元素执行矩阵变换
void cvTransform( const CvArr* src, CvArr* dst, const CvMat* transmat, const CvMat* shiftvec=NULL );
函数 cvTransform 对数组 src 每一个元素执行矩阵变换并将结果存储到 dst:
或者
N-通道数组 src 的每一个元素都被视为一个N元向量,使用一个 M×N 的变换矩阵 transmat 和偏移向量 shiftvec 把它变换到一个 M-通道的数组 dst 的一个元素中。 这里可以选择将偏移向量 shiftvec 嵌入到 transmat 中。这样的话 transmat 应该是 M×N+1 的矩阵,并且最右边的一列被看作是偏移向量 。
输入数组和输出数组应该有相同的位深(depth)和同样的大小或者 ROI 大小。 transmat 和 shiftvec 应该是实数浮点矩阵。
该函数可以用来进行 ND 点集的几何变换,任意的线性颜色空间变换,通道转换等。
计算数组和数组的转置的乘积
void cvMulTransposed( const CvArr* src, CvArr* dst, int order, const CvArr* delta=NULL );
函数 cvMulTransposed 计算 src 和它的转置的乘积。
函数求值公式:
如果 order=0
否则
返回矩阵的迹
CvScalar cvTrace( const CvArr* mat );
函数 cvTrace 返回矩阵mat的对角线元素的和。
tr(src) = | ∑ | mat(i,i) |
i |
矩阵的转置
void cvTranspose( const CvArr* src, CvArr* dst ); #define cvT cvTranspose
函数 cvTranspose 对矩阵 src 求转置:
注意,假设是复数矩阵不会求得复数的共轭。共轭应该是独立的:查看的 cvXorS 例子代码。
返回矩阵的行列式值
double cvDet( const CvArr* mat );
函数 cvDet 返回方阵 mat 的行列式值。对小矩阵直接计算,对大矩阵用高斯(GAUSSIAN)消去法。对于对称正定(positive-determined)矩阵也可以用 SVD 函数来求,U=V=NULL ,然后用 w 的对角线元素的乘积来计算行列式。
查找矩阵的逆矩阵或伪逆矩阵
double cvInvert( const CvArr* src, CvArr* dst, int method=CV_LU ); #define cvInv cvInvert
函数 cvInvert 对矩阵 src 求逆并将结果存储到 dst。
如果是 LU 方法该函数返回 src 的行列式值 (src 必须是方阵)。 如果是 0, 矩阵不求逆, dst 用 0 填充。
如果 SVD 方法该函数返回 src 的条件数的倒数(最小奇异值和最大奇异值的比值) ,如果 src 全为 0 则返回0。 如果 src 是奇异的, SVD 方法计算一个伪逆矩阵。
求解线性系统或者最小二乘法问题
int cvSolve( const CvArr* src1, const CvArr* src2, CvArr* dst, int method=CV_LU );
函数 cvSolve 解决线性系统或者最小二乘法问题 (后者用 SVD 方法可以解决):
如果使用 CV_LU 方法。 如果 src1 是非奇异的,该函数则返回 1 ,否则返回 0 ,在后一种情况下 dst 是无效的。
对实数浮点矩阵进行奇异值分解
void cvSVD( CvArr* A, CvArr* W, CvArr* U=NULL, CvArr* V=NULL, int flags=0 );
函数 cvSVD 将矩阵 A 分解成一个对角线矩阵和两个正交矩阵的乘积:
这里 W 是一个奇异值的对角线矩阵,它可以被编码成奇异值的一维向量,U 和 V 也是一样。所有的奇异值都是非负的并按降序存储。(U 和 V 也相应的存储)。
SVD 算法在数值处理上已经很稳定,它的典型应用包括:
奇异值回代算法(back substitution)
void cvSVBkSb( const CvArr* W, const CvArr* U, const CvArr* V, const CvArr* B, CvArr* X, int flags );
函数 cvSVBkSb 为被分解的矩阵 A 和矩阵 B 计算回代逆(back substitution) (参见 cvSVD 说明) :
这里
epsilon 是一个依赖于矩阵数据类型的的很小的数。该函数和 cvSVD 函数被用来执行 cvInvert 和 cvSolve, 用这些函数 (svd & bksb)的原因是初级函数(low-level)函数可以避免高级函数 (inv & solve) 计算中内部分配的临时矩阵。
计算对称矩阵的特征值和特征向量
void cvEigenVV( CvArr* mat, CvArr* evects, CvArr* evals, double eps=0 );
函数 cvEigenVV 计算矩阵 A 的特征值和特征向量:
mat*evects(i,:)' = evals(i)*evects(i,:)' (在 MATLAB 的记法)
矩阵 A 的数据将会被这个函数修改。
目前这个函数比函数 cvSVD 要慢,精确度要低, 如果已知 A 是正定的,(例如, 它是一个协方差矩阵), 它通常被交给函数 cvSVD 来计算其特征值和特征向量,尤其是在不需要计算特征向量的情况下
计算向量集合的协方差矩阵
void cvCalcCovarMatrix( const CvArr** vects, int count, CvArr* cov_mat, CvArr* avg, int flags );
函数 cvCalcCovarMatrix 计算输入向量的协方差矩阵和平均向量。该函数可以被运用到主成分分析中(PCA),以及马氏距离(Mahalanobis distance)比较向量中等等。
计算两个向量之间的马氏距离(Mahalanobis distance)
double cvMahalanobis( const CvArr* vec1, const CvArr* vec2, CvArr* mat );
函数 cvMahalanobis 计算两个向量之间的加权距离,其返回结果是:
协方差矩阵可以用函数cvCalcCovarMatrix 计算出来,逆矩阵可以用函数 cvInvert 计算出来 (CV_SVD 方法是一个比较好的选择, 因为矩阵可能是奇异的).
对一个向量集做PCA变换
void cvCalcPCA( const CvArr* data, CvArr* avg, CvArr* eigenvalues, CvArr* eigenvectors, int flags );
该函数对某个向量集做PCA变换.它首先利用cvCalcCovarMatrix计算协方差矩阵然后计算协方差矩阵的特征值与特征向量.输出的特征值/特征向量的个数小于或者等于MIN(rows(data),cols(data)).
把向量向某个子空间投影
void cvProjectPCA( const CvArr* data, const CvArr* avg, const CvArr* eigenvectors, CvArr* result )
该函数将输入向量向一个正交系(eigenvectors)投影.在计算点乘之前,输入向量要减去平均向量:
result(i,:)=(data(i,:)-avg)*eigenvectors' // for CV_PCA_DATA_AS_ROW layout.
根据投影系数重构原来的向量
void cvBackProjectPCA( const CvArr* proj, const CvArr* avg, const CvArr* eigenvects, CvArr* result );
该函数根据投影系数重构原来的向量:
result(i,:)=proj(i,:)*eigenvectors + avg // for CV_PCA_DATA_AS_ROW layout
附
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); // 这里似乎有问题,不成功
cvmSet(M,i,j,2.0); // Set M(i,j)
t = cvmGet(M,i,j); // Get M(i,j)
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 -> Mc cvSub(Ma, Mb, Mc); // Ma-Mb -> Mc cvMatMul(Ma, Mb, Mc); // Ma*Mb -> Mc
CvMat *Ma, *Mb, *Mc; cvMul(Ma, Mb, Mc); // Ma.*Mb -> Mc cvDiv(Ma, Mb, Mc); // Ma./Mb -> Mc cvAddS(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 -> res cvCrossProduct(&Va, &Vb, &Vc); // 向量积: Va x Vb -> Vc end{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) -> d cvInvert(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 = 对应的特征向量 (每行)
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 返回时被转置(若没有转置标号,则有问题不成功!!!).
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 properties int 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 30 int frameW = 640; // 744 for firewire cameras int frameH = 480; // 480 for firewire cameras writer=cvCreateVideoWriter("out.avi",CV_FOURCC('P','I','M','1'), fps,cvSize(frameW,frameH),isColor);
其他有效编码:
CV_FOURCC('P','I','M','1') = MPEG-1 codec CV_FOURCC('M','J','P','G') = motion-jpeg codec (does not work well) CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codec CV_FOURCC('D', 'I', 'V', '3') = MPEG-4.3 codec CV_FOURCC('D', 'I', 'V', 'X') = MPEG-4 codec CV_FOURCC('U', '2', '6', '3') = H263 codec CV_FOURCC('I', '2', '6', '3') = H263I codec CV_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);