opencv java 矩阵运算_opencv CvMat矩阵学习

本文详细介绍了使用OpenCV在Java中进行矩阵运算的各种方法,包括初始化矩阵、IplImage到cvMat的转换、图像直接操作、cvMat的直接操作以及矩阵的重塑。同时,还探讨了如何计算色彩距离,提供了较优和较笨的两种实现思路。通过这些方法,开发者可以更好地理解和应用OpenCV进行图像处理。
摘要由CSDN通过智能技术生成

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

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

}

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;

更优化的方法是:

#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];

}

}

}

对于多通道的数组,以下操作是推荐的:

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

#pragma comment( lib, "cxcore.lib" )

#include "cv.h"

#include

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

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值