Announce

PukiWiki contents have been moved into SONOTS Plugin (20070703)

OpenCV Matrix Operations

Table of Contents

OpenCV Matrix Operations

Let me laze. See Operations on Arrays

  • cvXxx is Matrix point wise computation, e.g., cvMul, cvAdd
  • cvXxxS is Matrix vs scalar, e.g., cvAddS. But, cvMulS does not exist, use cvConvertScale
  • cvmXxx or cvMatXxx is Matrix operation, e.g., cvMatMul, cvmAdd. But, cvmXxx exists just for compatibility with lower OpenCV versions.
  • cvReduce to operate in rows, cols, but only CV_REDUCE_SUM, CV_REDUCE_AVG, CV_REDUCE_MAX, CV_REDUCE_MIN are available.

Optional Matrix Operations

cxcore/include/cxcore.h

#define cvZero cvSetZero
#define cvReshapeND( arr, header, new_cn, new_dims, new_sizes )   \
      cvReshapeMatND( (arr), sizeof(*(header)), (header),         \
                      (new_cn), (new_dims), (new_sizes))
#define cvMirror cvFlip
#define cvCvtPixToPlane cvSplit
#define cvCvtPlaneToPix cvMerge
#define cvCvtScale cvConvertScale
#define cvScale  cvConvertScale
#define cvConvert( src, dst )  cvConvertScale( (src), (dst), 1, 0 )
#define cvCvtScaleAbs cvConvertScaleAbs
#define cvAbs(src, dst) cvAbsDiffS(src, dst, cvScalarAll(0))
#define cvMulAddS cvScaleAdd
/* Matrix transform: dst = A*B + C, C is optional */
#define cvMatMulAdd( src1, src2, src3, dst ) cvGEMM( (src1), (src2), 1., (src3), 1., (dst), 0 )
#define cvMatMul( src1, src2, dst )  cvMatMulAdd( (src1), (src2), NULL, (dst))
#define cvT cvTranspose
#define cvInv cvInvert
  • cvScale instead of cvMulS.

cv/include/compat.h (Avoid to use them, they exist just for compatibility)

#define cvmSetZero( mat )               cvSetZero( mat )
#define cvmSetIdentity( mat )           cvSetIdentity( mat )
#define cvmAdd( src1, src2, dst )       cvAdd( src1, src2, dst, 0 )
#define cvmSub( src1, src2, dst )       cvSub( src1, src2, dst, 0 )
#define cvmCopy( src, dst )             cvCopy( src, dst, 0 )
#define cvmMul( src1, src2, dst )       cvMatMulAdd( src1, src2, 0, dst )
#define cvmTranspose( src, dst )        cvT( src, dst )
#define cvmInvert( src, dst )           cvInv( src, dst )
#define cvmMahalanobis(vec1, vec2, mat) cvMahalanobis( vec1, vec2, mat )
#define cvmDotProduct( vec1, vec2 )     cvDotProduct( vec1, vec2 )
#define cvmCrossProduct(vec1, vec2,dst) cvCrossProduct( vec1, vec2, dst )
#define cvmTrace( mat )                 (cvTrace( mat )).val[0]
#define cvmMulTransposed( src, dst, order ) cvMulTransposed( src, dst, order )
#define cvmEigenVV( mat, evec, eval, eps)   cvEigenVV( mat, evec, eval, eps )
#define cvmDet( mat )                   cvDet( mat )
#define cvmScale( src, dst, scale )     cvScale( src, dst, scale )

cxcore/include/cxtypes.h

#define CV_IS_IMAGE(img) \
    (CV_IS_IMAGE_HDR(img) && ((IplImage*)img)->imageData != NULL)

/* get reference to pixel at (col,row),
   for multi-channel images (col) should be multiplied by number of channels */
#define CV_IMAGE_ELEM( image, elemtype, row, col )       \
    (((elemtype*)((image)->imageData + (image)->widthStep*(row)))[(col)])

#define CV_MAT_CN_MASK          ((CV_CN_MAX - 1) << CV_CN_SHIFT)
#define CV_MAT_CN(flags)        ((((flags) & CV_MAT_CN_MASK) >> CV_CN_SHIFT) + 1)
#define CV_MAT_DEPTH_MASK       (CV_DEPTH_MAX - 1)
#define CV_MAT_DEPTH(flags)     ((flags) & CV_MAT_DEPTH_MASK)
#define CV_MAT_TYPE_MASK        (CV_DEPTH_MAX*CV_CN_MAX - 1)
#define CV_MAT_TYPE(flags)      ((flags) & CV_MAT_TYPE_MASK)
#define CV_MAT_CONT_FLAG_SHIFT  14
#define CV_MAT_CONT_FLAG        (1 << CV_MAT_CONT_FLAG_SHIFT)
#define CV_IS_MAT_CONT(flags)   ((flags) & CV_MAT_CONT_FLAG)
#define CV_IS_CONT_MAT          CV_IS_MAT_CONT
#define CV_MAT_TEMP_FLAG_SHIFT  15
#define CV_MAT_TEMP_FLAG        (1 << CV_MAT_TEMP_FLAG_SHIFT)
#define CV_IS_TEMP_MAT(flags)   ((flags) & CV_MAT_TEMP_FLAG)


#define CV_IS_MAT_HDR(mat) \
    ((mat) != NULL && \
    (((const CvMat*)(mat))->type & CV_MAGIC_MASK) == CV_MAT_MAGIC_VAL && \
    ((const CvMat*)(mat))->cols > 0 && ((const CvMat*)(mat))->rows > 0)

#define CV_IS_MAT(mat) \
    (CV_IS_MAT_HDR(mat) && ((const CvMat*)(mat))->data.ptr != NULL)

#define CV_IS_MASK_ARR(mat) \
    (((mat)->type & (CV_MAT_TYPE_MASK & ~CV_8SC1)) == 0)

#define CV_ARE_TYPES_EQ(mat1, mat2) \
    ((((mat1)->type ^ (mat2)->type) & CV_MAT_TYPE_MASK) == 0)

#define CV_ARE_CNS_EQ(mat1, mat2) \
    ((((mat1)->type ^ (mat2)->type) & CV_MAT_CN_MASK) == 0)

#define CV_ARE_DEPTHS_EQ(mat1, mat2) \
    ((((mat1)->type ^ (mat2)->type) & CV_MAT_DEPTH_MASK) == 0)

#define CV_ARE_SIZES_EQ(mat1, mat2) \
    ((mat1)->height == (mat2)->height && (mat1)->width == (mat2)->width)

#define CV_IS_MAT_CONST(mat)  \
    (((mat)->height|(mat)->width) == 1)

/* size of each channel item,
   0x124489 = 1000 0100 0100 0010 0010 0001 0001 ~ array of sizeof(arr_type_elem) */
#define CV_ELEM_SIZE1(type) \
    ((((sizeof(size_t)<<28)|0x8442211) >> CV_MAT_DEPTH(type)*4) & 15)

/* 0x3a50 = 11 10 10 01 01 00 00 ~ array of log2(sizeof(arr_type_elem)) */
#define CV_ELEM_SIZE(type) \
    (CV_MAT_CN(type) << ((((sizeof(size_t)/4+1)*16384|0x3a50) >> CV_MAT_DEPTH(type)*2) & 3))

#define CV_MAT_ELEM_PTR_FAST( mat, row, col, pix_size )  \
    (assert( (unsigned)(row) < (unsigned)(mat).rows &&   \
             (unsigned)(col) < (unsigned)(mat).cols ),   \
     (mat).data.ptr + (size_t)(mat).step*(row) + (pix_size)*(col))

#define CV_MAT_ELEM_PTR( mat, row, col )                 \
    CV_MAT_ELEM_PTR_FAST( mat, row, col, CV_ELEM_SIZE((mat).type) )

#define CV_MAT_ELEM( mat, elemtype, row, col )           \
    (*(elemtype*)CV_MAT_ELEM_PTR_FAST( mat, row, col, sizeof(elemtype)))

#define CV_IS_MATND_HDR(mat) \
    ((mat) != NULL && (((const CvMatND*)(mat))->type & CV_MAGIC_MASK) == CV_MATND_MAGIC_VAL)

#define CV_IS_MATND(mat) \
    (CV_IS_MATND_HDR(mat) && ((const CvMatND*)(mat))->data.ptr != NULL)

#define CV_IS_SPARSE_MAT_HDR(mat) \
    ((mat) != NULL && \
    (((const CvSparseMat*)(mat))->type & CV_MAGIC_MASK) == CV_SPARSE_MAT_MAGIC_VAL)

#define CV_IS_SPARSE_MAT(mat) \
    CV_IS_SPARSE_MAT_HDR(mat)

...

CV_INLINE int cvCvToIplDepth( int type )
{
    int depth = CV_MAT_DEPTH(type);
    return CV_ELEM_SIZE1(depth)*8 | (depth == CV_8S || depth == CV_16S ||
           depth == CV_32S ? IPL_DEPTH_SIGN : 0);
}

....

#define CV_MAJOR_VERSION    1
#define CV_MINOR_VERSION    1
#define CV_SUBMINOR_VERSION 0
#define CV_VERSION          "1.1.0"

See the source code directly cxtypes.h for more

cxcore/src/_cxcore.h

#define icvIplToCvDepth( depth ) \
    icvDepthToType[(((depth) & 255) >> 2) + ((depth) < 0)]

where ./cxcore/src/cxtables.cpp

const signed char icvDepthToType[] =
{
    -1, -1, CV_8U, CV_8S, CV_16U, CV_16S, -1, -1,
    CV_32F, CV_32S, -1, -1, -1, -1, -1, -1, CV_64F, -1
};

Examples

Initialize 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 );
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
// No cvReleaseMat is required because memory allocation is 
// done at double a[]
float a[] = { 1,  2,  3,  4,
               5,  6,  7,  8,
               9, 10, 11, 12 };
CvMat mat = cvMat( 3, 4, CV_32FC1, a ); // 32FC1 for float

IplImage to cvMat

CvMat mathdr, *mat = cvGetMat( img, &mathdr );
CvMat *mat = cvCreateMat( img->height, img->width, CV_64FC3 );
cvConvert( img, mat ); 
// #define cvConvert( src, dst )  cvConvertScale( (src), (dst), 1, 0 )

cvArr (IplImage or cvMat) to cvMat

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__;
}

Direct Access to IplImage

 int col, row, z;
 uchar b, g, r;
 for( y = 0; row < img->height; y++ )
 {
   for ( col = 0; col < img->width; col++ )
   {
     //for( z = 0; z < img->nChannels; z++ )
     //{
     //   c = img->imageData[img->widthStep * row + col * img->nChannels + z];
     //}
     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 )

Refer Official Manual How to access image pixel

Direct Access to cvMat

Direct access is bothersome because it depends on cvMat data type.

If CV_32FC1 (1 channel float)

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

If CV_64FC1 (1 channel double)

CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
M->data.db[ row * M->cols + col ] = 3.0;

In general, if 1 channel

CvMat* M = cvCreateMat( 4, 4, CV_64FC1 );
CV_MAT_ELEM( *M, double, row, col ) = 3.0;

Change elemtype 'double'. CV_MAT_ELEM is not available for multi-channels (there is assert col < M->cols).

#define CV_MAT_ELEM_CN( mat, elemtype, row, col ) \
    (*(elemtype*)((mat).data.ptr + (size_t)(mat).step*(row) + sizeof(elemtype)*(col)))
CvMat* M = cvCreateMat( 4, 4, CV_64FC3 );
CV_MAT_ELEM_CN( *M, double, row, col * CV_MAT_CN(M->type) + ch ) = 3.0;

for multi-channels

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;

any smart way?

   #define CV_8U   0
   #define CV_8S   1
   #define CV_16U  2
   #define CV_16S  3
   #define CV_32S  4
   #define CV_32F  5
   #define CV_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];
        }
    }
}
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);

Indirect Access to cvMat

cvmGet/Set is easy and quick way for CV_32FC1 and CV_64FC1. The access speed is almost same with the direct access.

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

Use cvGet/Set2D for multi-channels and other depths.

CvScalar scalar = cvGet2D( mat, row, col );
cvSet2D( mat, row, col, cvScalar( r, g, b ) );
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" );
    }
}

Behaviors of cvReshape

Conclusion: channel first, column second, row third. Just look the format of the original array. such as

double data[] =  // channel first, column second, row third
{ 111, 112, 113, 121, 122, 123,
211, 212, 213, 221, 222, 223 };

By the way, in matlab, row (1st dim) first, column (2nd dim) second, channel (3rd dim) third.

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

Color Distance of Two Image

Color Distance in each pixel (not sum in all pixels). Assume img1, img2, and dist are as follows:

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

Not smart (cvSplit)

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

Smarter way (cvReshape and cvReduce)

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 x nCol 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 x nCol
cvReleaseMat( &diff );