opencv 中的cvMat详解

1 cvMat简介

图像在计算机中是以数组的形式存放的,Mat是OpenCV中用于存放图像的数据结构。通过调用相关方法,我们能够实现对图像的输入输出以及一些操作。同时,Mat又不止可以作为图像容器,它也可以作为一种比较纯粹的描述矩阵这种数学对象的结构。它比C中的IplImage好的地方在于,由于它的“计数器”机制,我们不需要对它进行手动的内存回收,从而避免了常常困扰C/C++程序员的“内存泄露”问题。

tips:

(1)关于CvMat的通道数的理解:

可以理解为CvMat的一个矩阵值中包含几个数据,单通道即表示矩阵中的一个值中只有一个数据;

当用CvMat保存彩色图像数据时,可以理解为3通道矩阵,即矩阵中一个值包含R、G、B三个数据

 

(2)当CvMat的数据类型为CV_8UC1时,对于数据的访问,需选择CvMat.data.ptr指针

不同的数据类型,访问元素时指针的选择不同。

 

2 矩阵CvMat的两种声明和初始化方法

(1)直接给CvMat赋值,逐点赋值

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 ); 

 

(2)利用现有数组,对CvMat赋值

double a[12]  =  {  1, 2,3, 4,            

                  5, 6, 7, 8,                

                  9,10, 11, 12 };

CvMat mat = cvMat( 3, 4, CV_64FC1, a );

(3)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 )

(4)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__;
}

(5)图像直接操作
方式一:直接数组操作 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 );
   }
}

下面是运用cvMat的代码

[cpp]  view plain copy print ?
  1. #include <iostream>  
  2. #include <opencv2/highgui/highgui.hpp>  
  3. #include <opencv2/core/core.hpp>  
  4. //#include <opencv/cv.hpp>  
  5. using namespace std;  
  6. using namespace cv;  
  7.   
  8. int main()  
  9. {  
  10.     string namePic="pic.jpg";  
  11.       
  12.     //Part 1  Read and Load Image  
  13.     /************************************************************************/  
  14.     /* 下面的代码显示了如何用imread函数进行图像读取操作                                                                     */  
  15.     /************************************************************************/  
  16.       
  17.     Mat Img_Color=imread (namePic,CV_LOAD_IMAGE_COLOR);     //生成了彩色图  
  18.     Mat Img_Gray=imread (namePic,CV_LOAD_IMAGE_GRAYSCALE);   //生成了灰度图  
  19.       
  20.     /************************************************************************/  
  21.     /* 下面的代码显示了如何用imshow函数进行图像的显示                                                                     */  
  22.     /************************************************************************/  
  23.       
  24.     imshow ("彩色小女孩",Img_Color);  
  25.     imshow ("灰度小女孩",Img_Gray);  
  26.         waitKey();  
  27.     return 0;  
  28. }  

运行结果

opencv 中的cvMat详解_第1张图片


opencv 中的cvMat详解_第2张图片


代码解释:

(1)imread()函数读入图像

Mat imread(const string& filename, int flags=1 )

返回值是Mat对象,第一个参数为字符串型,表示源图像的名称(如果不在源文件目录下,应该写成绝对路径),第二个参数决定了读入图像的颜色格式。上例代码中所出现的CV_LOAD_IMAGE_COLOR和CV_LOAD_IMAGE_GRAYSCALE分别表示读入彩色图像和灰度图像。

(2)imshow()输出图像

void imshow(const string& winname, InputArray mat)

可以看到,第一个参数为字符串型,定义了窗口的名称,第二个参数为Mat,表明要显示的图片。


Mat的数据结构包含了两部分数据,一部分是矩阵头,表明了矩阵的基本信息。另一部分就是用于存储图像的矩阵数组了。由于图像一般都很很大,所以对矩阵数组这部分内存进行频繁的读写复制操作是很费时间的。为了克服这个问题,OpenCV允许两个Mat对象有不同的矩阵头,却包含同一块矩阵数据。这样,在复制操作的时候,就避开了对大块内存空间的读写,节省了时间。比如下面的几种赋值方式,它们都只是复制了Mat A的矩阵头,同时复制了指向图像矩阵的内存的指针,但是却没有复制矩阵:

[cpp]  view plain copy print ?
  1. Mat C=A;      //利用等号  
  2. Mat E(A);      //利用Mat的构造函数  

如果想要用矩形截取图像的一部分可以使用如下的构造函数:

[cpp]  view plain copy print ?
  1. Mat B=Mat(A,Rect(0,0,100,100));      //截取A的100*100的矩形区域  

复制矩阵可以使用Mat类实例提供的clone方法和copyto方法

[cpp]  view plain copy print ?
  1. Mat G;  
  2. A.copyto(G);  
  3. Mat H=A.clone();  


生成这样的Mat对象实例步骤:

首先,我们可以使用Mat的构造函数,Mat的构造函数多种多样,下面举的例子只是其中之一,更多内容还是应该访问网页http://docs.opencv.org/modules/core/doc/basic_structures.html#Mat来获得。

  
  
  
  
[cpp] view plain copy print ?
  1. Mat B(20,20,CV_8UC1,Scalar(1));  
这样,就建立了一个20*20,并且矩阵全部元素都是1的矩阵。 其次,我们可以使用Create函数。如下所示:
[cpp] view plain copy print ?
  1. Mat B;  
  2. B.create (10,10,CV_8UC3);  
不过要注意的是,这样方法不能同时对B矩阵元素进行初始化操作,就像这个样子: opencv 中的cvMat详解_第3张图片 或者,我们可以使用MATLAB风格的代码进行赋值:
[cpp] view plain copy print ?
  1. Mat E = Mat::eye(4, 4, CV_64F);  //生成单位阵  
  2. Mat E=Mat::ones(2, 2, CV_32F);  //生成全1阵  
  3. Mat E=Mat::zeros(2, 2, CV_8UC1);//生成0矩阵  
  4.    
获取某个矩阵的一行或者一列可以采取下面的方式:
[cpp] view plain copy print ?
  1. Mat RowClone = C.row(1).clone();  

你可能感兴趣的:(opencv 中的cvMat详解)