OpenCV(四)对矩阵的操作(CvMat详细讲解)

OpenCV包含了非常多的图像处理算法,而我们知道图像其实就是由矩阵数据构成,所以OpenCV中肯定有处理矩阵的函数和数据结构。
牛人说过,程序就是数据结构+算法。OpenCV这么厉害的库当然也不会例外。
在前几篇文章中,我们多次用到过IplImage这个数据结构,每当我们想获得图像时都会用到这个结构,图像和矩阵有关系,那IplImage肯定也跟矩阵有关系吧!

     看到上图,如果学过java和C+的同学肯定就感觉到了,这不是类之间的继承关系吗?但是OpenCV应该是用C写的呀?C里面怎么会有继承,怎么会有面向对象这些东西?
     C语言里面肯定不会有面向对象,但是面向对象是一种思想,思想是可以用任何语言来实现的,只不过容易和困难之分吧。所以设计OpenCV的那些牛人们就把面向对象的思想给融入了进去。
     那么从图上就可以知道了,IplImage由CvMat派生,CvMat由CvArr派生。CvMat就是OpenCV的矩阵结构。下面就详细讲讲CvMat结构
CvMat矩阵结构
在开始学习矩阵的相关内容之前,我们要知道两点。一,在OpenCV中没有向量结构,需要向量的话可以生成列矩阵或者行矩阵。二,OpenCV中的矩阵元素不仅仅可以取数值类型。
typedef struct CvMat{
        int type;
        int step;
        int* refcount;
        union{
            uchar* ptr;
            short* s;
            int * i;
            float* fl;
            double* db;
//ptr - pointer to 8-bit unsigned elements
//s - pointer to 16-bit signed elements
//i - pointer to 32-bit signed elements
//fl - pointer to 32-bit floating-point elements
//db - pointer to 64-bit floating-point elements
            }data;

        union{
            int rows;
            int height;
        };
        union{
            int cols;
            int width;

        };

}CvMat;


以上可以看出,CvMat结构相当简单,矩阵由宽度(width),高度(height),类型(type),行数据长度(step),和一个指向数据的指针构成。可以通过一个指向CvMat的指针来访问这些成员。或者用一些现成的函数来获得,比如调用函数vGetSize(CvMat*),返回一个CvSize结构。
此类信息通常称作为矩阵头。很多程序是区分矩阵头和数据体的,后者是data成员指向的内存位置。
矩阵的创建
     矩阵有多种创建方法,最常见的方法是用cvCreateMat(),由多个原函数组成,如cvCreateMatHeader()和cvCreateData()。cvCreateMatHeader()函数创建CvMat结构,不为数据分配内存,而cvCreateData()函数只负责数据的内存分配。另外一种方法是函数cvCloneMat(CvMat*),依据一个现有矩阵来创建一个新矩阵。当这个矩阵不在需要时,当然是一直强调的释放,调用函数cvRealeaseMat(CvMat*)来释放。
让我们来看看代码是怎么说?
//Create Matrix
//Create a new rows by cols matrix of type 'type'
CvMat* cvCreateMat(int rows,int cols,int type);
//Create only matrix header without allocating data
CvMat* cvCreateMatHeader(int rows ,itn cols, int type);
//Initialize header on existing CvMat structure
CvMat* cvInitMatHeader(
         CvMat* mat,
         int rows,
         int cols,
         int type,
         void * data=NULL,
         int step=CV_AUTOSTEP
        );
CvMat cvMat(
     int rows,
     int cols,
     int type,
     void *data = NULL
    );

CvMat* cvCloneMat(CvMat** mat);
 //Free the Matrix 'mat',both header and data .
void cvReleaseMat(cvMat** mat);


与很多OpenCV结构类似,构造函数cvMat()可以创建CvMat结构,实际上不分配存储空间,仅创建头结构(与cvInitMatHeader()类似)。这些方法对与存取到处散放的数据很有用,可以让矩阵头指向这些数据,实现对这些数据的打包,并用操作矩阵的函数来处理这些数据。来看代码怎么说:
//用固定数据创建一个OpenCV矩阵
float vals[]={0.8,-0.5,0.5,0.8};
CvMat rotmat;
cvInitMatHeader(
      &rotmat,
      2,
      2,
      CV_32FC1,
      vals
    );


矩阵数据存取
访问矩阵中的数据有三种方法:简单的方法,麻烦的方法和恰当的方法。
简单的方法
从矩阵中得到一个元素最简单的方法就是利用宏CV_MAT_ELEM()。
CvMat* mat=cvCreateMat(5,5,CV_32FC1);
float element_3_2 =CV_MAT_ELEM(*mat,float ,3,2);
宏的参数:传入矩阵,待提取元素的类型,行和列数。
返回值:提取出的元素的值
更进一步,还有一个与CV_MAT_ELEM类似的宏CV_MAST_ELEM_PTR()。看下面代码。
CvMat* mat=cvCreateMat(5,5,CV_32FC1);
loat element_3_2 = 7.7;
*((float*)CV_MAT_ELEM_PTR(*mat ,3,2))=element_3_2;
简单的方法肯定有很多缺点,宏每次被调用都会重新计算指针。顺序访问矩阵中的元素时,这种方法会变得相当麻烦。
麻烦的方法
麻烦的方法就不介绍了,既然麻烦就是用的少,或者基本不用。
恰当的方法

//累加一个三通道矩阵中的所有元素
float sum(const CvMat* mat){
    float s=0.0f;
    for(int row=0;row<mat->rows;row++){

        const float* ptr=(const float*)(mat->data.ptr+row*mat->step);
        for(col=0;col<mat->cols;col++){
            s+=*ptr++;
        }
    }

    return s;

}
注:这段内容引用博客: http://blog.csdn.net/lcmliao/article/details/21788473,若有不明白的地方可以参考。
     我们假设这是一个5*3的二维数据,数据类型为CV_8CU3,上面的格式就是它在Mat中存储的样子,首先它是一个三通道的数据,用我们熟悉的彩色图片RGB来表示,图像在Mat的存储刚好为BGR,调过来。那我们看到,我们想访问最右下角的那个像素点其为M(4,2)【记得下标从0开始】,step是如何帮忙的呢?这时候step是二维矩阵,step[0]和step[1]~那么M点的地址是M.data + M.step[0] * row + M.step[1] * col,其中row为4,col为2。M.data是指向起始位置,也就是左上角第一个蓝色,我把它颜色加深了一点。那step[0]表示的是每一行元素的数据大小是多少,最确切的来说每一行由多少个8bit排列而成。这里因为数据类型为CV_8CU3,每个像素点有三个通道,所以step[0]=3*3=9。M.data + M.step[0] * row 这时候的指针就跑到了第五行第一个蓝色那里,step[1]表示的是一个元素的数据大小,也就是一个像素由多少个8bit排列而来,显然这里是3。最后M.data + M.step[0] * row + M.step[1] * col 这个指的就是最后一个像素点的B通道。
这样就基本讲解清楚。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值