opencv里面矩阵的表示有很多种,以cvCreateMat为例,矩阵操作整体流程大致为:
1. 分配矩阵空间。
CvMat* cvCreateMat(int rows, int cols, int type);
2. 初始化矩阵。
double a[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
CvMat Ma=cvMat(2, 4, CV_64FC1, a);
3. 操作矩阵。
//如复制矩阵:
CvMat* M1 = cvCreateMat(4,4,CV_32FC1);
CvMat* M2;
M2=cvCloneMat(M1);
4. 释放矩阵空间。
CvMat* M = cvCreateMat(4,4,CV_32FC1);
cvReleaseMat(&M);
OK,下面详细介绍这些个流程
1)分配矩阵空间
常用两种方法:
1.1 CvMat* cvCreateMat( int rows, int cols, int type )。
a,看他返回是一个CvMat *,所以要先定义一个CvMat *mat;指针;
b,分配指针空间,mat=cvCreateMat(2,4,CV_64FC1);
c,分配了指针空间当然要释放掉,用cvReleaseMat(&mat);
1.2 CvMat* cvInitMatHeader( CvMat* mat, int rows, int cols, int type,void* data=NULL, int step=CV_AUTOSTEP )。
a,第一个参数必须是CvMat格式的,官方的文档是CvMat *mat,但是这里要注意,mat必须是初始化过的,单独定义一个指针如CvMat *data1;把data1带入cvInitMatHeader函数后,编译器会报错,显示没有初始化!
b,分配矩阵头指针,cvInitMatHeader(&mat,3,4,CV_64FC1);
c,当然是必不可少的释放掉空间,但是这个释放空间是用的函数是free(&mat).
2)矩阵的赋值
矩阵的赋值方式,有一个共同的方式,就是用cvmSet(&mat,i,j,data);
在前面定义的时候,如果是用的CvMat * ,这个样子的就是CvmSet(mat,i,j,data);
如果是用CvMat ,这个时候就是CvmSet(&mat,i,j,data);
例如:
double a[] = { 1, 2, 3, 4, 5, 6, 7, 8};
CvMat Ma;
cvInitMatHeader( &Ma, 2, 4, CV_64FC1, a );
3)存取矩阵元素
假设需要存取一个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;
4)向量操作
//矩阵-矩阵操作:
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 = 对应的特征向量 (每行)
//奇异值分解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
好吧,思路有些乱,用OpenCV也有很长时间了,但是一直没有整理过,导致感觉知识很散,这段时间相对较闲,从以前的笔记、网络上搜集的资料,一起整理下自己知识体系。
===附注:如果知晓CV_32FC1和CV_64FC1,请忽略此部分===
关于cvInitMatHeader之CV_32FC1和CV_64FC1的说明:
type: 矩阵元素类型. 格式为CV_<bit_depth>(S|U|F)C<number_of_channels>. 例如: CV_8UC1 表示8位无符号单通道矩阵, CV_32SC2表示32位有符号双通道矩阵. 例程: CvMat* M = cvCreateMat(4,4,CV_32FC1);
CV_32FC1和CV_64FC1,前者是32位数据,后者是64位数据。因此前者类型的数据必须以指向32位数据类型的指针存取,否则会报错,而后者类型的数据必须以指向64位数据类型的指针存取,否则会报错。
如果用cv_32fc1,那么后面对该矩阵的输入输出的数据指针类型都应该是float,这在32位编译器上是32位浮点数,也就是单精度。如果用cv_64fc1,那么后面对该矩阵的输入输出的数据指针类型都应该是double,这在32位编译器上是64位浮点数,也就是双精度。
好吧,看下网上的一个例子,稍作修改:
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
void PrintMat(CvMat *A); // 显示矩阵
void Test_Multiply(); // 测试矩阵乘法
void Test_Multiply64();
int main()
{
printf("=== Test multiply ===\n");
printf("CV_32FC1:");
Test_Multiply(); // pass
printf("\n\nCV_64FC1:");
Test_Multiply64();
getchar();
return 0;
}
// 显示矩阵
void PrintMat(CvMat* A)
{
int i,j;
printf("\nMatrix:");
for(i=0;i<A->rows;i++)
{
printf("\n");
switch( CV_MAT_DEPTH(A->type) )
{
case CV_32F:
case CV_64F:
for(j=0;j<A->cols;j++)
printf("%9.3f ", (float) cvGetReal2D( A, i, j ));
break;
case CV_8U:
case CV_16U:
for(j=0;j<A->cols;j++)
printf("%6d",(int)cvGetReal2D( A, i, j ));
break;
default:
break;
}
}
printf("\n");
}
void Test_Multiply()
{
float a[] = { 1, 2, 3, 4 };
float b[] = { 1, 2, 3, 4 };
float c[1];
CvMat Ma, Mb, Mc;
//CV_32FC1和CV_64FC1,前者是32位数据,后者是64位数据。
//因此前者类型的数据必须以指向32位数据类型的指针存取,否则会报错,
//而后者类型的数据必须以指向64位数据类型的指针存取,否则会报错。
//cvInitMatHeader( &Ma, 3, 4, CV_64FC1, a, CV_AUTOSTEP );
//cvInitMatHeader( &Mb, 4, 3, CV_64FC1, b, CV_AUTOSTEP );
//cvInitMatHeader( &Mc, 3, 3, CV_64FC1, c, CV_AUTOSTEP );
//如果用cv_32fc1,那么后面对该矩阵的输入输出的数据指针类型都应该是float,这在32位编译器上是32位浮点数,也就是单精度。
//如果用cv_64fc1,那么后面对该矩阵的输入输出的数据指针类型都应该是double,这在32位编译器上是64位浮点数,也就是双精度。
cvInitMatHeader( &Ma, 1, 4, CV_32FC1, a, CV_AUTOSTEP );
cvInitMatHeader( &Mb, 4, 1, CV_32FC1, b, CV_AUTOSTEP );
cvInitMatHeader( &Mc, 1, 1, CV_32FC1, c, CV_AUTOSTEP );
cvMatMulAdd( &Ma, &Mb, 0, &Mc );
PrintMat(&Ma);
PrintMat(&Mb);
PrintMat(&Mc);
return;
}
void Test_Multiply64()
{
double a[] = { 1, 2, 3, 4 };
double b[] = { 1, 2, 3, 4 };
double c[1];
CvMat Ma, Mb, Mc;
//CV_32FC1和CV_64FC1,前者是32位数据,后者是64位数据。
//因此前者类型的数据必须以指向32位数据类型的指针存取,否则会报错,
//而后者类型的数据必须以指向64位数据类型的指针存取,否则会报错。
cvInitMatHeader( &Ma, 1, 4, CV_64FC1, a, CV_AUTOSTEP );
cvInitMatHeader( &Mb, 4, 1, CV_64FC1, b, CV_AUTOSTEP );
cvInitMatHeader( &Mc, 1, 1, CV_64FC1, c, CV_AUTOSTEP );
//如果用cv_32fc1,那么后面对该矩阵的输入输出的数据指针类型都应该是float,这在32位编译器上是32位浮点数,也就是单精度。
//如果用cv_64fc1,那么后面对该矩阵的输入输出的数据指针类型都应该是double,这在32位编译器上是64位浮点数,也就是双精度。
//cvInitMatHeader( &Ma, 1, 4, CV_32FC1, a, CV_AUTOSTEP );
//cvInitMatHeader( &Mb, 4, 1, CV_32FC1, b, CV_AUTOSTEP );
//cvInitMatHeader( &Mc, 1, 1, CV_32FC1, c, CV_AUTOSTEP );
cvMatMulAdd( &Ma, &Mb, 0, &Mc );
PrintMat(&Ma);
PrintMat(&Mb);
PrintMat(&Mc);
return;
}