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