matlab被广泛用于算法的仿真,往往是整个项目的最上层设计时使用到的工具;而具体的高效的代码实现则要借助C\C++,而opencv提供了很多这方面的接口函数,里面有一部分是与matlab相对应的,这一点更进一步方便了算法的代码实现过程。当然,首先不考虑代码的效率,因为本来opencv就不见得具有多高的效率,更何况是再进一步封装成matlab风格的接口呢?但对于项目开发还是有一定的便利性的。
1、用得最多的莫过于——imread、imshow
区别在于数据结构:matlab中是N维数组,CV中是Mat类
2、矩阵运算
这里有大量的矩阵运算、矩阵变换函数,因为matlab名字就是矩阵实验室,在矩阵运算的效率上非常可观。其本质也是C++,估计CV会用到相似的算法,正如乔布斯说的:好的艺术家抄袭,伟大的艺术家剽窃。
如,两矩阵点乘、减法;矩阵乘方;等等。参考:http://blog.csdn.net/kelvin_yan/article/details/41726843
3、矩阵赋值
matlab中又一使用率很高的操作——zeros、ones、eye,CV中Mat下有对应方法
另外,matlab中a=[1,2,3;4,5,6],CV中则麻烦一点 Mat a = (Mat_<double>(2,3) << 1, 4, 2, 5, 3, 6);
4、矩阵元素寻访
matlab中B(5:9, :) ,CV中B(Range(4, 9), Range::all())
注意,Range的区间定义为:[left,right),右边界不是刚好等于right噢,实际是right-1,如上面的例子是取B的第5到第9个行,在matlab中下标是从1开始算的,所以直接是5:9,但在opencv里,下标从0开始,其数组下标范围是 i = 4:8,由于Range的区间定义,所以应写为 Range(4,9)
opencv这样定义Range区间主要是为了更好地表示单一一行或者一列,如,表示第10行,记为Range(10,11),如果按照我们习惯应该是Range(10,10),这样明显存在歧义不利于编程
还需要注意的是,matlab里面我们可以这样来赋值:
B(5:9,:) = C(5:9,:);
但opencv里不行,这样做没有任何意义,B的值不会改变,要用copyTo方法:
C(Range(4,9),Range::all()).copyTo(B(Range(4,9),Range::all()));
往往“简单易用”与“效率”是矛盾的,那Range方法又是不是中看不中用呢?下面做个小测试,跟指针方式访问相比较:
建立两个Mat
Mat D(h,w,CV_64F,cv::Scalar(0));
Mat E(h,w,CV_64F,cv::Scalar(0));
具体任务是把D的第1~500行、第5~700列的值拷贝至E的相同位置,分别通过指针访问Mat和Range访问Mat的方式实现,测量运行5000次的平均耗时:
double tcnt=0;
for(int cnt=0;cnt<5000;cnt++)
{
tt.interval(); //定时函数
D(Range(0,500),Range(4,700)).copyTo(E(Range(0,500),Range(4,700)));
tcnt += tt.interval();
}
cout<<"ptr time: "<<tcnt/5000<<endl;
指针方式:
double tcnt=0;
for(int cnt=0;cnt<5000;cnt++)
{
tt.interval(); //定时函数
double* ptr1 = (double*)E.data;
double* ptr0 = (double*)D.data;
for(int y=0;y<500;y++)
{
for(int x=4;x<700;x++)
{
*(ptr1 + y*w+x) = *(ptr0 + y*w+x);
}
}
tcnt += tt.interval();
}
结果显示,Range比指针的效率更高!!!不过也就高出10%,两者效率还是比较相近的,当然实际中采用Range会提高代码的可读性
5、reshape
改变已有矩阵的形状,例如一维变二维、三维变一维,又或者改变矩阵的尺寸
但与matlab不一样,opencv需要考虑数据在内存中的排列方式,所以cv::Mat本身不仅只有行、列两个参数,它还有type、step、channel等等参数。cv::Mat::reshape不会重新分配内存,它是在已有内存的基础上,根据用户输入的channel、rows参数,重新计算cv::Mat的头信息
如,一个500*400的二维矩阵,希望将其大小改为400*500
cv::Mat mat(500, 400, CV_8UC1); //单通道的500*400矩阵
mat = mat.reshape(1, 400); //调整mat的行数为400,通道数仍然是1,内部会根据mat原来的大小rows*cols*channels() 计算新的列数,即500*400*1 / 1 / 400 = 500,同时mat的另外一些成员变量也会更新,如step、elemenSize等
6、高级操作
奇异值分解
矩阵求逆
(to be continued)