CvMat、Mat、IplImage之间的转换详解及实例
见原博客:http://blog.sina.com.cn/s/blog_74a459380101obhm.html
OpenCV学习之CvMat的用法详解及实例
CvMat是OpenCV比较基础的函数。初学者应该掌握并熟练应用。但是我认为计算机专业学习的方法是,不断的总结并且提炼,同时还要做大量的实践,如编码,才能记忆深刻,体会深刻,从而引导自己想更高层次迈进。
1.初始化矩阵:
方式一、逐点赋值式:
CvMat* mat = cvCreateMat( , , CV_64FC1 );
cvZero( mat );
cvmSet( mat, , , );
cvmSet( mat, , , );
cvmSet( mat, , , );
cvmSet( mat, , , );
cvReleaseMat( &mat );
方式二、连接现有数组式:
double a[] = { , , , , , , , , , , , };
CvMat mat = cvMat( , , CV_64FC1, a ); // 64FC1 for double
// 不需要cvReleaseMat,因为数据内存分配是由double定义的数组进行的。
2.IplImage <----->cvMat的转换
A.CvMat-> IplImage
IplImage* img = cvCreateImage(cvGetSize(mat),,);
cvGetImage(matI,img); cvSaveImage("rice1.bmp",img);
B.IplImage -> CvMat
IplImage* img = cvLoadimage("leda.jpg",);
//法2:
CvMat *mat = cvCreateMat( img->height, img->width, CV_64FC3 );
cvConvert( img, mat );
//法1:
CvMat mathdr;
CvMat *mat = cvGetMat( img, &mathdr );
3.IplImage <--->Mat的转换
(1)将IplImage----- > Mat类型
Mat::Mat(const IplImage* img, bool copyData=false);
默认情况下,新的Mat类型与原来的IplImage类型共享图像数据,转换只是创建一个Mat矩阵头。当将参数copyData设为true后,就会复制整个图像数据。
例:
IplImage*iplImg = cvLoadImage("greatwave.jpg", );
Matmtx(iplImg); // IplImage* ->Mat 共享数据
// or : Mat mtx = iplImg;或者是:Mat mtx(iplImg,0); // 0是不复制影像,也就是iplImg的data共用同个记意位置,header各自有
(2)将Mat类型转换-----> IplImage类型
同样只是创建图像头,而没有复制数据。
例:
IplImage ipl_img = img; // Mat -> IplImage IplImage*-> BYTE* BYTE* data= img->imageData;
4.CvMat<--->Mat的转换
(1)将CvMat类型转换为Mat类型
B.CvMat->Mat
与IplImage的转换类似,可以选择是否复制数据。
CvMat*m= cvCreatMat(int rows ,int cols , int type); Mat::Mat(const CvMat* m, bool copyData=false);
在openCV中,没有向量(vector)的数据结构。任何时候,但我们要表示向量时,用矩阵数据表示即可。
但是,CvMat类型与我们在线性代数课程上学的向量概念相比,更抽象,比如CvMat的元素数据类型并不仅限于基础数据类型,比如,下面创建一个二维数据矩阵:
CvMat*m= cvCreatMat(int rows ,int cols , int type);
这里的type可以是任意的预定义数据类型,比如RGB或者别的多通道数据。这样我们便可以在一个CvMat矩阵上表示丰富多彩的图像了。
(2)将Mat类型转换为CvMat类型
与IplImage的转换类似,不复制数据,只创建矩阵头。
例:
//假设Mat类型的imgMat图像数据存在
CvMat cvMat = imgMat; // Mat -> CvMat
5.cv::Mat--->const cvArr*
cvArr * 数组的指针。就是opencv里面的一种类型。
Mat img;
const CvArr* s=(CvArr*)&img;
上面就可以了,CvArr是Mat的虚基类,所有直接强制转换就可以了
void cvResize( const CvArr*src, CvArr* dst, int interpolation=CV_INTER_LINEAR );// src 就是之前的lplimage类型的一个指针变量
6.cvArr(IplImage或者cvMat)转化为cvMat
方式一、cvGetMat方式:
int coi = ;
cvMat *mat = (CvMat*)arr;
if( !CV_IS_MAT(mat) )
{
mat = cvGetMat( mat, &matstub, &coi );
if (coi != ) 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 = ;
if( !CV_IS_MAT(mat) )
{
CV_CALL( mat = cvGetMat( mat, &matstub, &coi ) );
if (coi != ) CV_ERROR_FROM_CODE(CV_BadCOI);
}
// Process as cvMat
__END__;
}
7.图像直接操作
方式一:直接数组操作 int col, row, z;
uchar b, g, r;
for( row = ; row < img->height; y++ )
{
for ( col = ; col < img->width; col++ )
{
b = img->imageData[img->widthStep * row + col * ]
g = img->imageData[img->widthStep * row + col * + ];
r = img->imageData[img->widthStep * row + col * + ];
}
}
方式二:宏操作:
int row, col;
uchar b, g, r;
for( row = ; row < img->height; row++ )
{
for ( col = ; col < img->width; col++ )
{
b = CV_IMAGE_ELEM( img, uchar, row, col * );
g = CV_IMAGE_ELEM( img, uchar, row, col * + );
r = CV_IMAGE_ELEM( img, uchar, row, col * + );
}
}
注:CV_IMAGE_ELEM( img, uchar, row, col * img->nChannels + ch )
8.cvMat的直接操作
数组的直接操作比较郁闷,这是由于其决定于数组的数据类型。
对于CV_32FC1 (1 channel float):
CvMat* M = cvCreateMat( , , CV_32FC1 );
M->data.fl[ row * M->cols + col ] = (float)3.0;
对于CV_64FC1 (1 channel double):
CvMat* M = cvCreateMat( , , CV_64FC1 );
M->data.db[ row * M->cols + col ] = 3.0;
一般的,对于1通道的数组:
CvMat* M = cvCreateMat( , , 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 = ; row < mat->rows; row++ ) {
for( elem = ; 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=; row< mat->rows; row++)
{
p = mat->data.fl + row * (mat->step/);
for(col = ; col < mat->cols; col++)
{
*p = (float) row+col;
*(p+) = (float) row+col+;
*(p+) =(float) row+col+;
p+=;
}
}
对于两通道和四通道而言:
CvMat* vector = cvCreateMat( , , CV_32SC2 );
CV_MAT_ELEM( *vector, CvPoint, , ) = cvPoint(,); CvMat* vector = cvCreateMat( , , CV_64FC4 );
CV_MAT_ELEM( *vector, CvScalar, , ) = cvScalar(,,,);
9.间接访问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 = ; i < mat->rows; i++ )
{
for( j = ; 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 = ; i < mat->rows; i++ )
{
for( j = ; j < mat->cols; j++ )
{
CvScalar scal = cvGet2D( mat, i, j );
printf( "(%f,%f,%f) ", scal.val[], scal.val[], scal.val[] );
}
printf( "\n" );
}
}
10.修改矩阵的形状——cvReshape的操作
经实验表明矩阵操作的进行的顺序是:首先满足通道,然后满足列,最后是满足行。
注意:这和Matlab是不同的,Matlab是行、列、通道的顺序。
我们在此举例如下:
对于一通道:
// 1 channel
CvMat *mat, mathdr;
double data[] = { , , , ,
, , , ,
, , , };
CvMat* orig = &cvMat( , , CV_64FC1, data );
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, , ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11 12 13 14 21 22 23 24 31 32 33 34
mat = cvReshape( mat, &mathdr, , ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, , ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11
// 12
// 13
// 14
// 21
// 22
// 23
// 24
// 31
// 32
// 33
//
mat = cvReshape( mat, &mathdr, , ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, , ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14 21 22
//23 24 31 32 33 34
mat = cvReshape( mat, &mathdr, , ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
//11 12 13 14
//21 22 23 24
//31 32 33 34
mat = cvReshape( orig, &mathdr, , ); // new_ch, new_rows
cvDoubleMatPrint( mat ); // above
// 11 12
// 13 14
// 21 22
// 23 24
// 31 32
// 33 34
mat = cvReshape( mat, &mathdr, , ); // 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
对于三通道
//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 );
11.计算色彩距离
我们要计算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, );
IplImage *img1G = cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img1R = cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img2B = cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img2G = cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *img2R = cvCreateImage( cvGetSize(img1), img1->depth, );
IplImage *diff = cvCreateImage( cvGetSize(img1), IPL_DEPTH_64F, );
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, , N ); // N x D(colors)
CvMat mat2hdr, *mat2 = cvReshape( img2, &mat2hdr, , 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, , N ); // nRow x nCol to N x 1
cvReduce( diff, dist, , CV_REDUCE_SUM ); // N x D to N x 1
dist = cvReshape( dist, &disthdr, , 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(,,CV_32FC1);
cvZero(mat);//将矩阵置0
//为矩阵元素赋值
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
CV_MAT_ELEM( *mat, float, , ) = .f;
//获得矩阵元素(0,2)的值
float *p = (float*)cvPtr2D(mat, , );
printf("%f\n",*p);
return ;
}
CvMat、Mat、IplImage之间的转换详解及实例的更多相关文章
- 图像处理之opencv---mat、cvmat、IplImage之间的转换
一.Mat类型:矩阵类型,Matrix. 在openCV中,Mat是一个多维的密集数据数组.可以用来处理向量和矩阵.图像.直方图等等常见的多维数据. Mat有3个重要的方法: 1.Mat mat = ...
- JAVA中list,set,数组之间的转换详解
JAVA的list,set,数组之间的转换,主要是使用Apache Jakarta Commons Collections,具体的方法如下:import org.apache.commons.coll ...
- JAVA中list,set,map与数组之间的转换详解
package test; import java.util.*; /** * Created by ming */ public class Test { public static void ma ...
- Android 应用程序之间内容分享详解(二)
转载请注明出处:http://blog.csdn.net/xiaanming/article/details/9428613 Android 应用程序之间内容分享详解(一) 之前给大家分享了你开发的应 ...
- PHP中IP地址与整型数字互相转换详解
这篇文章主要介绍了PHP中IP地址与整型数字互相转换详解,本文介绍了使用PHP函数ip2long与long2ip的使用,以及它们的BUG介绍,最后给出自己写的两个算法,需要的朋友可以参考下 IP转换成 ...
- GLSL-几何着色器详解跟实例(GS:Geometry Shader)[转]
[OpenGL4.0]GLSL-几何着色器详解和实例(GS:Geometry Shader) 一.什么是几何着色器(GS:Geometry Shader) Input Assembler(IA)从顶点 ...
- C语言操作WINDOWS系统存储区数字证书相关函数详解及实例
C语言操作WINDOWS系统存储区数字证书相关函数详解及实例 以下代码使用C++实现遍历存储区证书及使用UI选择一个证书 --使用CertOpenSystemStore打开证书存储区. --在循环中 ...
- httpd配置文件详解及实例
httpd配置文件详解及实例 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.http协议的组成 http协议是C/S架构:我们可以把浏览器(如:IE,Firefox,Safar ...
- log4j.properties配置详解与实例
log4j.properties配置详解与实例 第一步:加入log4j-1.x.x.jar到lib下. 第二步:在工程的src下下建立log4j.properties.内容如下: #OFF,syste ...
随机推荐
- 静态链表实现 (A-B)U(B-A)
图中黄色部分为(A-B)U(B-A)的实际意义,用结构数组做静态链表来实现该表达式 大致流程是先建立A链表,接着将挨个输入的B中元素在A链表中遍历.如果没找到,就加到A链表结尾下标为endpointe ...
- Tomcat部署项目的几种常见方式
以前学习的时候只知道在Eclipse或者MyEclipse中发布项目到Tomcat,最近实习时发现不同的项目还有不同的发布方式,所以特地学习了Tomcat发布项目的方式,在此记录下来. 1 直接将we ...
- paip.c++ qt 图片处理 检测损坏的图片
paip.c++ qt 图片处理 检测损坏的图片 作者Attilax , EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net ...
- 被Oracle全局暂时表坑了
今天凌晨4点多钟,在客户现场的负责人打电话给我,说非常奇怪,下载功能时快时慢.此下载功能非常复杂,之前一直是我优化,在半梦半醒中打开电脑,通过远程看着现场同事在PL/SQL developer中操作. ...
- 从头开始——重装ubuntu
一.装系统 环境:计算机原本装的系统是windows7+ubuntu 14.04,后来ubuntu的启动项被我手贱破坏了,进不了ubuntu,只能重新安装. 启动盘:使用UltraISO软碟通制作U ...
- angular在ie8下的一个bug
昨天拿项目在ie8下测试,发现不少bug,其中有一个bug让我很不解,报了一个thead开头的bug,因为已经切回到linux下了,我就不报具体是什么bug了,鼓捣了半天,发现引用angular的应用 ...
- ffmpeg常用参数一览
基本选项: -formats 输出所有可用格式 -f fmt 指定格式(音频或视频格式) -i filename 指定输入文件名,在linux下当然也能指定:0.0(屏幕录制)或摄像头 -y 覆盖已有 ...
- 使用DBCC CHECKIDENT重置自增标识
原来ID=8的记录删除后, 下一个Insert记录为9 当插入ID=10的记录后, 使用 ) 当再次Insert记录, 就会是ID为8. 不过以上不是真实的使用场景, 以上情况应该插入包含ID的记录就 ...
- java中关于String 类型数据 的存储方式
Constant Pool常量池的概念: 在讲到String的一些特殊情况时,总会提到String Pool或者Constant Pool,但是我想很多人都不太 明白Constant Pool到底是个 ...
- <转>GC其他:引用标记-清除、复制、标记-整理的说明
注:本文根据<深入理解Java虚拟机>第3章部分内容整理而成. 对象死亡历程 1.基本的mark&sweep是必须的,后续的都是对他的改进, 2.young代理的survivor就 ...