opencv 矩阵学习

来源:互联网 发布:unity 模型 优化 插件 编辑:程序博客网 时间:2024/06/08 10:47
1.初始化矩阵:

方式一、逐点赋值式:

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

方式二、连接现有数组式:

double a[] = { 1,  2, 3,  4,
              5,  6,  7, 8,
              9, 10, 11, 12 };
CvMat mat = cvMat( 3, 4, CV_64FC1, a ); // 64FC1 for double
// 不需要cvReleaseMat,因为数据内存分配是由double定义的数组进行的。

2.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 )

3.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 );
    CvMatmatstub, *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);
    }
    // Processas cvMat
   __END__;
}

4.图像直接操作
方式一:直接数组操作 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 );
   }
 }
注:CV_IMAGE_ELEM( img, uchar, row, col *img->nChannels + ch )

5.cvMat的直接操作
数组的直接操作比较郁闷,这是由于其决定于数组的数据类型。

对于CV_32FC1 (1 channel float):
CvMat* M = cvCreateMat( 4, 4, CV_32FC1 );
M->data.fl[ row * M->cols + col ] =(float)3.0;

对于CV_64FC1 (1 channel double):
CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
M->data.db[ row * M->cols + col ] =3.0;

一般的,对于1通道的数组:
CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
CV_MAT_ELEM( *M, double, row, col ) = 3.0;
注意double要根据数组的数据类型来传入,这个宏对多通道无能为力。

对于多通道:
看看这个宏的定义:#define CV_MAT_ELEM_CN( mat, elemtype, row, col ) /
   (*(elemtype*)((mat).data.ptr + (size_t)(mat).step*(row) +sizeof(elemtype)*(col)))
if( CV_MAT_DEPTH(M->type) == CV_32F )
   CV_MAT_ELEM_CN( *M, float, row, col *CV_MAT_CN(M->type) + ch ) = 3.0;
if( CV_MAT_DEPTH(M->type) == CV_64F )
   CV_MAT_ELEM_CN( *M, double, row, col *CV_MAT_CN(M->type) + ch ) = 3.0;
更优化的方法是:
   #defineCV_8U   0
   #defineCV_8S   1
   #defineCV_16U  2
   #defineCV_16S  3
   #defineCV_32S  4
   #defineCV_32F  5
   #defineCV_64F  6
   #define CV_USRTYPE1 7

int elem_size = CV_ELEM_SIZE( mat->type );
for( col = start_col; col < end_col; col++ ) {
    for( row =0; row < mat->rows; row++ ) {
       for( elem = 0; elem < elem_size; elem++ ) {
           (mat->data.ptr + ((size_t)mat->step *row) + (elem_size * col))[elem] =
               (submat->data.ptr +((size_t)submat->step * row) + (elem_size * (col -start_col)))[elem];
       }
    }
}

对于多通道的数组,以下操作是推荐的:
for(row=0; row< mat->rows;row++)
    {
       p = mat->data.fl + row *(mat->step/4);
       
       for(col = 0; col < mat->cols;col++)
       {
           *p = (float) row+col;
           *(p+1) = (float) row+col+1;
           *(p+2) =(float) row+col+2;
           p+=3;
       }
    }
对于两通道和四通道而言:
CvMat* vector = cvCreateMat( 1, 3, CV_32SC2 );
CV_MAT_ELEM( *vector, CvPoint, 0, 0 ) = cvPoint(100,100);

CvMat* vector = cvCreateMat( 1, 3, CV_64FC4 );
CV_MAT_ELEM( *vector, CvScalar, 0, 0 ) = cvScalar(0,0,0,0);

6.间接访问cvMat
cvmGet/Set是访问CV_32FC1 和 CV_64FC1型数组的最简便的方式,其访问速度和直接访问几乎相同
cvmSet( mat, row, col, value );
cvmGet( mat, row, col );
举例:打印一个数组
inline void cvDoubleMatPrint( const CvMat* mat )
{
    int i,j;
    for( i = 0;i < mat->rows; i++ )
    {
       for( j = 0; j < mat->cols; j++)
       {
           printf( "%f ",cvmGet( mat, i, j ) );
       }
       printf( "/n" );
    }
}

而对于其他的,比如是多通道的后者是其他数据类型的,cvGet/Set2D是个不错的选择
CvScalar scalar = cvGet2D( mat, row, col );
cvSet2D( mat, row, col, cvScalar( r, g, b ) );

注意:数据不能为int,因为cvGet2D得到的实质是double类型。
举例:打印一个多通道矩阵:
inline void cv3DoubleMatPrint( const CvMat* mat )
{
    int i,j;
    for( i = 0;i < mat->rows; i++ )
    {
       for( j = 0; j < mat->cols; j++)
       {
           CvScalar scal = cvGet2D( mat, i, j );
           printf( "(%f,%f,%f) ", scal.val[0], scal.val[1], scal.val[2]);
       }
       printf( "/n" );
    }
}

7.修改矩阵的形状——cvReshape的操作
经实验表明矩阵操作的进行的顺序是:首先满足通道,然后满足列,最后是满足行。
注意:这和Matlab是不同的,Matlab是行、列、通道的顺序。
我们在此举例如下:
对于一通道:
 // 1 channel
 CvMat *mat, mathdr;
 double data[] = { 11, 12, 13, 14,
                  21, 22, 23, 24,
                  31, 32, 33, 34 };
 CvMat* orig = &cvMat( 3, 4,CV_64FC1, data );
 //11 12 13 14
 //21 22 23 24
 //31 32 33 34
 mat = cvReshape( orig, &mathdr,1, 1 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 // 11 12 13 14 21 22 23 24 31 32 33 34
 mat = cvReshape( mat, &mathdr, 1,3 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 //11 12 13 14
 //21 22 23 24
 //31 32 33 34
 mat = cvReshape( orig, &mathdr,1, 12 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 // 11
 // 12
 // 13
 // 14
 // 21
 // 22
 // 23
 // 24
 // 31
 // 32
 // 33
 // 34
 mat = cvReshape( mat, &mathdr, 1,3 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 //11 12 13 14
 //21 22 23 24
 //31 32 33 34
 mat = cvReshape( orig, &mathdr,1, 2 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 //11 12 13 14 21 22
 //23 24 31 32 33 34
 mat = cvReshape( mat, &mathdr, 1,3 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 //11 12 13 14
 //21 22 23 24
 //31 32 33 34
 mat = cvReshape( orig, &mathdr,1, 6 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 // 11 12
 // 13 14
 // 21 22
 // 23 24
 // 31 32
 // 33 34
 mat = cvReshape( mat, &mathdr, 1,3 ); // new_ch, new_rows
 cvDoubleMatPrint( mat ); // above
 //11 12 13 14
 //21 22 23 24
 //31 32 33 34
 // Use cvTranspose and cvReshape( mat,&mathdr, 1, 2 ) to get
 // 11 23
 // 12 24
 // 13 31
 // 14 32
 // 21 33
 // 22 34
 // Use cvTranspose again when to recover
 
对于三通道
// 3 channels
CvMat mathdr, *mat;
double data[] = { 111, 112, 113, 121, 122, 123,
211, 212, 213, 221, 222, 223 };
CvMat* orig = &cvMat( 2, 2, CV_64FC3, data );
//(111,112,113) (121,122,123)
//(211,212,213) (221,222,223)
mat = cvReshape( orig, &mathdr, 3, 1 ); // new_ch,new_rows
cv3DoubleMatPrint( mat ); // above
// (111,112,113) (121,122,123) (211,212,213) (221,222,223)
// concatinate in column first order
mat = cvReshape( orig, &mathdr, 1, 1 );// new_ch,new_rows
cvDoubleMatPrint( mat ); // above
// 111 112 113 121 122 123 211 212 213 221 222 223
// concatinate in channel first, column second, row third
mat = cvReshape( orig, &mathdr, 1, 3); // new_ch,new_rows
cvDoubleMatPrint( mat ); // above
//111 112 113 121
//122 123 211 212
//213 221 222 223
// channel first, column second, row third
mat = cvReshape( orig, &mathdr, 1, 4 ); // new_ch,new_rows
cvDoubleMatPrint( mat ); // above
//111 112 113
//121 122 123
//211 212 213
//221 222 223
// channel first, column second, row third
// memorize this transform because this is useful to
// add (or do something) color channels
CvMat* mat2 = cvCreateMat( mat->cols,mat->rows, mat->type );
cvTranspose( mat, mat2 );
cvDoubleMatPrint( mat2 ); // above
//111 121 211 221
//112 122 212 222
//113 123 213 223
cvReleaseMat( &mat2 );

8.计算色彩距离
我们要计算img1,img2的每个像素的距离,用dist表示,定义如下
IplImage *img1 = cvCreateImage( cvSize(w,h), IPL_DEPTH_8U, 3);
IplImage *img2 = cvCreateImage( cvSize(w,h), IPL_DEPTH_8U, 3);
CvMat *dist  = cvCreateMat( h, w, CV_64FC1);
比较笨的思路是:cvSplit->cvSub->cvMul->cvAdd
代码如下:
IplImage *img1B = cvCreateImage( cvGetSize(img1),img1->depth, 1 );
IplImage *img1G = cvCreateImage( cvGetSize(img1),img1->depth, 1 );
IplImage *img1R = cvCreateImage( cvGetSize(img1),img1->depth, 1 );
IplImage *img2B = cvCreateImage( cvGetSize(img1),img1->depth, 1 );
IplImage *img2G = cvCreateImage( cvGetSize(img1),img1->depth, 1 );
IplImage *img2R = cvCreateImage( cvGetSize(img1),img1->depth, 1 );
IplImage*diff    =cvCreateImage( cvGetSize(img1), IPL_DEPTH_64F, 1 );
cvSplit( img1, img1B, img1G, img1R );
cvSplit( img2, img2B, img2G, img2R );
cvSub( img1B, img2B, diff );
cvMul( diff, diff, dist );
cvSub( img1G, img2G, diff );
cvMul( diff, diff, diff);
cvAdd( diff, dist, dist );
cvSub( img1R, img2R, diff );
cvMul( diff, diff, diff );
cvAdd( diff, dist, dist );
cvReleaseImage( &img1B );
cvReleaseImage( &img1G );
cvReleaseImage( &img1R );
cvReleaseImage( &img2B );
cvReleaseImage( &img2G );
cvReleaseImage( &img2R );
cvReleaseImage( &diff );

比较聪明的思路是
int D = img1->nChannels; // D: Number of colors(dimension)
int N = img1->width * img1->height;// N: number of pixels
CvMat mat1hdr, *mat1 = cvReshape( img1, &mat1hdr,1, N ); // N x D(colors)
CvMat mat2hdr, *mat2 = cvReshape( img2, &mat2hdr,1, N ); // N x D(colors)
CvMat diffhdr, *diff  = cvCreateMat( N, D,CV_64FC1 ); // N x D, temporal buff
cvSub( mat1, mat2, diff );
cvMul( diff, diff, diff );
dist = cvReshape( dist, &disthdr, 1, N ); // nRow xnCol to N x 1
cvReduce( diff, dist, 1, CV_REDUCE_SUM ); // N x D to N x 1
dist = cvReshape( dist, &disthdr, 1,img1->height ); // Restore N x 1 to nRow xnCol
cvReleaseMat( &diff );

 

 

#pragma comment( lib, "cxcore.lib" )
#include "cv.h"
#include <stdio.h>
int main()
{

 

CvMat* mat = cvCreateMat(3,3,CV_32FC1);
 
cvZero(mat);//将矩阵置0
//为矩阵元素赋值
 
CV_MAT_ELEM( *mat, float, 0, 0 ) = 1.f;
 
CV_MAT_ELEM( *mat, float, 0, 1 ) = 2.f;
 
CV_MAT_ELEM( *mat, float, 0, 2 ) = 3.f;
 
CV_MAT_ELEM( *mat, float, 1, 0 ) = 4.f;
 
CV_MAT_ELEM( *mat, float, 1, 1 ) = 5.f;
 
CV_MAT_ELEM( *mat, float, 1, 2 ) = 6.f;
 
CV_MAT_ELEM( *mat, float, 2, 0 ) = 7.f;
 
CV_MAT_ELEM( *mat, float, 2, 1 ) = 8.f;
 
CV_MAT_ELEM( *mat, float, 2, 2 ) = 9.f;
//获得矩阵元素(0,2)的值
 
float *p = (float*)cvPtr2D(mat, 0, 2);
 
printf("%f/n",*p);
 
   return0;
}