opencv--图像的基本操作【2】

1、图像的表示

1.1、数字图像
一副尺寸为 M × N 的图像可以用一个 M × N 的矩阵来表示,矩阵元素的值表示这个位置上的像素的亮度,一般来说像素值越大表示该点越亮。

如图图像,我们看到的是 Lena 的头像,但是计算机看来,这副图像只是一堆亮度各异的点。
在这里插入图片描述

图中白色圆圈内的区域,进行放大并仔细查看,将会如图所示。
在这里插入图片描述

1.2、一般来说,灰度图用 2 维矩阵表示,彩色(多通道)图像用 3 维矩阵(M× N × 3)表示。

对于图像显示来说,目前大部分设备都是用无符号 8 位整数(类型为CV_8U)表示像素亮度。

1.3、图像数据在计算机内存中的存储顺序为以图像最左上点(也可能是最左下点)开始,存储如表所示。

灰度图像的存储示意图:
在这里插入图片描述
I ij 表示第 i 行 j 列的像素值。如果是多通道图像,比如 RGB 图像,则每个
像素用三个字节表示。在 OpenCV 中,RGB 图像的通道顺序为 BGR ,存储如
表 所示

在这里插入图片描述

2、Mat类

早期的 OpenCV 中,使用 IplImage 和 CvMat 数据结构来表示图像。IplImage和CvMat 都是 C 语言的结构。使用这两个结构的问题是内存需要手动管理,开发者必须清楚知道何时需要申请内存,何时需要释放内存。这个开发者带来了一定的负担,开发者应将更多精力用于算法设计,因此在新版本的 OpenCV 中引入了 Mat 类。

新加入的 Mat 类能够自动管理内存。使用 Mat 类,你不再需要花费大量精力在内存管理上。而且你的代码会变得很简洁,代码行数会变少。但 C++接口唯一的不足是当前一些嵌入式开发系统可能只支持 C 语言,如果你的开发平台支持C++,完全没有必要再用 IplImage 和 CvMat。在新版本的 OpenCV 中,开发者依然可以使用 IplImage 和 CvMat,但是一些新增加的函数只提供了 Mat 接口。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.1、Mat 类的定义如下所示,关键的属性如下方代码所示:

class CV_EXPORTS Mat
{
public:
//一系列函数
...
/* flag 参数中包含许多关于矩阵的信息,如:
-Mat 的标识
-数据是否连续
-深度
-通道数目
*/
int flags;
//矩阵的维数,取值应该大于或等于 2
int dims;
//矩阵的行数和列数,如果矩阵超过 2 维,这两个变量的值都为-1
int rows, cols;
//指向数据的指针
uchar* data;
//指向引用计数的指针
//如果数据是由用户分配的,则为 NULL
int* refcount;
//其他成员变量和成员函数
...
};

2.2、创建Mat对象

Mat 是一个非常优秀的图像类,它同时也是一个通用的矩阵类,可以用来创建和操作多维矩阵。有多种方法创建一个 Mat 对象。

构造函数方法

Mat 类提供了一系列构造函数,可以方便的根据需要创建 Mat 对象。

下面是一个使用构造函数创建对象的例子。

Mat M(3,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl;

第一行代码创建一个行数(高度)为 3,列数(宽度)为 2 的图像,图像元素是 8 位无符号整数类型,且有三个通道。
图像的所有像素值被初始化为(0, 0,255)。由于 OpenCV 中默认的颜色顺序为 BGR,因此这是一个全红色的图像。
第二行代码是输出Mat类的实例M的所有像素值。Mat重定义了<<操作符,使用这个操作符,可以方便地输出所有像素值,而不需要使用 for 循环逐个像素输出。在这里插入图片描述

  • 常用的构造函数有:

Mat::Mat()
无参数构造方法;

Mat::Mat(int rows, int cols, int type)
创建行数为 rows,列数为 col,类型为 type 的图像;

Mat::Mat(Size size, int type)
创建大小为 size,类型为 type 的图像;

Mat::Mat(int rows, int cols, int type, const Scalar& s)
创建行数为 rows,列数为 col,类型为 type 的图像,并将所有元素初始化为值 s;

Mat::Mat(Size size, int type, const Scalar& s)
创建大小为 size,类型为 type 的图像,并将所有元素初始化为值 s;

Mat::Mat(const Mat& m)
将 m 赋值给新创建的对象,此处不会对图像数据进行复制,m 和新对象共用图像数据;

Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
创建行数为 rows,列数为 col,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step指定。

Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
创建大小为 size,类型为 type 的图像,此构造函数不创建图像数据所需内存,而是直接使用 data 所指内存,图像的行步长由 step 指定。

Mat::Mat(const Mat& m, const Range& rowRange, const Range& colRange)
创建的新图像为 m 的一部分,具体的范围由 rowRange 和 colRange 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据;

Mat::Mat(const Mat& m, const Rect& roi)
创建的新图像为 m 的一部分,具体的范围 roi 指定,此构造函数也不进行图像数据的复制操作,新图像与 m 共用图像数据。

1. 这些构造函数中,很多都涉及到类型type。type可以是CV_8UC1,CV_16SC1,…,CV_64FC4 等。
2. 里面的 8U 表示 8 位无符号整数,16S 表示 16 位有符号整数,64F表示 64 位浮点数(即 double 类型);
3. C 后面的数表示通道数,例如 C1 表示一个通道的图像,C4 表示 4 个通道的图像,以此类推。

如果你需要更多的通道数,需要用宏 CV_8UC(n),例如:
Mat M(3,2, CV_8UC(5));//创建行数为 3,列数为 2,通道数为 5 的图像

create()函数创建对象

除了在构造函数中可以创建图像,也可以使用 Mat 类的 create()函数创建图像。如果 create()函数指定的参数与图像之前的参数相同,则不进行实质的内存申请操作;如果参数不同,则减少原始数据内存的索引,并重新申请内存。

Mat M(2,2, CV_8UC3);//构造函数创建图像
M.create(3,2, CV_8UC2);//释放内存重新创建图像

需要注意的时,使用 create()函数无法设置图像像素的初始值

Matlab 风格的创建对象方法

OpenCV 2 中提供了 Matlab 风格的函数,如 zeros(),ones()和 eyes()。这种方法使得代码非常简洁,使用起来也非常方便。

使用这些函数需要指定图像的大小和类型,使用方法如下:

Mat Z = Mat::zeros(2,3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl;
Mat O = Mat::ones(2, 3, CV_32F);
cout << "O = " << endl << " " << O << endl;
Mat E = Mat::eye(2, 3, CV_64F);
cout << "E = " << endl << " " << E << endl;

有些 type 参数如 CV_32F 未注明通道数目,这种情况下它表示单通道
上面代码的输出结果如图所示:
在这里插入图片描述

实例

#include"opencv2/opencv.hpp"
#include<iostream>
using  namespace std;
using namespace cv;
int main() {
	Mat src;
	src = imread("D:/111.jpg");
	if (src.empty()) {
		cerr << "open error" << endl;
		return -1;
	}

	namedWindow("原图", WINDOW_AUTOSIZE);
	imshow("原图", src);


	//构造 Mat::Mat(Size size,int type),没有数据
	Mat dst;
	dst = Mat(src.size(), src.type());
	dst = Scalar(127, 0, 255);  //B=127 G=0 R=255
	namedWindow("new1", WINDOW_AUTOSIZE);
	imshow("new1", dst);



//和原图一模一样
	//1. Mat.clone()
	//和原图一模一样
	Mat dst2;
	dst2 = src.clone();
	namedWindow("new2", WINDOW_AUTOSIZE);
	imshow("new2", dst2);
	
	//2. Mat.copyTo(output)
	Mat dst3;
	src.copyTo(dst3);
	namedWindow("new3", WINDOW_AUTOSIZE);
	imshow("new3", dst3);
	
	//3. cvtColor构造图片
	Mat dst4;
	cvtColor(src,dst4, CV_BGR2GRAY);
	cout << "dst4.cols=" << dst4.cols << endl ;
	cout << "dst4.rows=" << dst4.rows << endl ;
	cout << "src.channels=" << src.channels() << endl ;
	cout << "dst4.channels=" << dst4.channels() << endl;
	const uchar* p = dst4.ptr<uchar>(0);
	cout << "dst4 first pixel value=" << &p << endl;
	namedWindow("new4", WINDOW_AUTOSIZE);
	imshow("new4", dst4);

//图片内容为空
	//Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
	Mat dst5(3,3, CV_8UC3, Scalar(0, 0, 255));
	cout << "dst5=" << endl << dst5 << endl;
	namedWindow("new5", WINDOW_AUTOSIZE);
	imshow("new5", dst5);

	//Mat 
	Mat dst6;
	dst6.create(src.size(), src.type());
	dst6 = Scalar(0, 0, 255);
	namedWindow("new6", WINDOW_AUTOSIZE);
	imshow("new6", dst6);

	//
	Mat dst7;
	dst7=Mat::zeros(src.size(), src.type());
	namedWindow("new7", WINDOW_AUTOSIZE);
	imshow("new7", dst7);

	waitKey(0);
}

3、矩阵的基本元素表达

对于单通道图像,其元素类型一般为 8U(即 8 位无符号整数),当然也可以是 16S、32F 等;这些类型可以直接用 uchar、short、float 等 C/C++语言中的基本数据类型表达。

如果多通道图像,如 RGB 彩色图像,需要用三个通道来表示。在这种情况下,如果依然将图像视作一个二维矩阵,那么矩阵的元素不再是基本的数据类型。

OpenCV 中有模板类 Vec,可以表示一个向量。OpenCV 中使用 Vec 类预定义了一些小向量,可以将之用于矩阵元素的表达。

typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;

typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

例如 :

  • 8U 类型的 RGB 彩色图像可以使用 Vec3b,
  • 3 通道 float 类型的矩阵可以使用 Vec3f。

对于 Vec 对象,可以使用[]符号如操作数组般读写其元素,如:

Vec3b color; //用 color 变量描述一种 RGB 颜色
color[0]=255; //B 分量
color[1]=0; //G 分量
color[2]=0; //R 分量

5、选取图像局部区域

Mat 类提供了多种方便的方法来选择图像的局部区域。使用这些方法时需要注意,这些方法并不进行内存的复制操作。如果将局部区域赋值给新的 Mat 对象,新对象与原始对象共用相同的数据区域,不新申请内存,因此这些方法的执行速度都比较快。

5.1、单行或单列选择

提取矩阵的一行或者一列可以使用函数row()或col()。
函数的声明如下:

Mat  Mat::row(int i)  const
Mat  Mat::col  (int j)  const

参数i和j分别是行标和列标。例如取出A矩阵的第i行可以使用如下代码:
Mat line=A.row(i);
例如取出 A 矩阵的第 i 行,将这一行的所有元素都乘以 2,然后赋值给第 j行,可以这样写:
A.row(j) = A.row(i)*2;

5.2、用Range选择多行或多列

Range 是 OpenCV 中新增的类,该类有两个关键变量 star 和 end。Range 对
象可以用来表示矩阵的多个连续的行或者多个连续的列。其表示的范围为从 start
到 end,包含 start,但不包含 end。Range 类的定义如下:

class Range
{
	public:
	    ...
	    int start, end;
};

Range 类还提供了一个静态方法 all(),这个方法的作用如同 Matlab 中的“:”,表所有的行或者所有的列。

创建一个单位阵
    Mat A=Mat::eye(10,10,CV_32S);
提取第13列(不包含3列)
    Mat B=A(Range::all(),Range(1,3));
提取 B 的第 59 行(不包括 9)
    Mat C = B(Range(5, 9), Range::all());
    其实等价于 C = A(Range(5, 9), Range(1, 3))

5.3、 感兴趣区域

从图像中提取感兴趣区域(Region of interest)有两种方法:

  • 使用构造函数
//创建宽度为 320,高度为 240 的 3 通道图像
Mat img(Size(320,240),CV_8UC3);
//roi 是表示 img 中 Rect(10,10,100,100)区域的对象
Mat roi(img, Rect(10,10,100,100));
//使用构造函数
Mat roi4(img, Range(10,100),Range(10,100));
  • 使用括号运算符
Mat roi2 = img(Rect(10,10,100,100));

当然也可以使用 Range 对象来定义感兴趣区域,
如下使用括号运算符:

 Mat roi3 = img(Range(10,100),Range(10,100));

5.4、取对角线元素

矩阵的对角线元素可以使用 Mat 类的 diag()函数获取,该函数的定义如下:

Mat Mat::diag(int d) const
  1. 参数 d=0 时,表示取主对角线;
  2. 当参数 d>0 是,表示取主对角线下方的次对角线,
    如 d=1 时,表示取主对角线下方,且紧贴主多角线的元素;
  3. 当参数 d<0 时,表示取主对角线上方的次对角线。
    如同row()和col()函数,diag()函数也不进行内存复制操作,其复杂度也是O(1)。

6、Mat表达式

用 C++中的运算符重载,OpenCV 2 中引入了 Mat 运算表达式。这一新特点使得使用 C++进行编程时,就如同写 Matlab 脚本,代码变得简洁易懂,也便于维护。

如果矩阵 A 和 B 大小相同,则可以使用如下表达式:
C = A + B + 1;
其执行结果是 A 和 B 的对应元素相加,然后再加 1,并将生成的矩阵赋给 C变量。

下面给出 Mat 表达式所支持的运算。下面的列表中使用 A 和 B 表示 Mat 类型的对象,使用 s 表示 Scalar 对象,alpha 表示 double 值。

  • 加法,减法,取负:A+B,A-B,A+s,A-s,s+A,s-A,-A
  • 缩放取值范围:A*alpha
  • 矩阵对应元素的乘法和除法: A.mul(B),A/B,alpha/A
  • 矩阵乘法:A*B (注意此处是矩阵乘法,而不是矩阵对应元素相乘)
  • 矩阵转置:A.t()
  • 矩阵求逆和求伪逆:A.inv()
  • 矩阵比较运算:A cmpop B,A cmpop alpha,alpha cmpop A。
    此处 cmpop可以是>,>=,==,!=,<=,<。如果条件成立,则结果矩阵(8U 类型矩阵)的对应元素被置为 255;否则置 0。
  • 矩阵位逻辑运算:A logicop B,A logicop s,s logicop A,~A,
    此处 logicop可以是&,|和^。
  • 矩阵对应元素的最大值和最小值:min(A, B),min(A, alpha),max(A, B),max(A, alpha)。
  • 矩阵中元素的绝对值:abs(A)
  • 叉积和点积:A.cross(B),A.dot(B)
    下面例程展示了 Mat 表达式的使用方法,例程的输出结果如图所示。
#include <iostream>
#include "opencv2/opencv.hpp"
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
	//创建了一个4*4的单位矩阵,单通道,32位
	Mat A = Mat::eye(4, 4, CV_32SC1);
	cout << "A=" << A << endl;
	//单位矩阵的每个元素都是*3+1;
	Mat B = A * 3 + 1;
	//B矩阵的主对角线【4,4,4,4】与B矩阵的第二列元素【1,4,1,1】对应相加
	Mat C = B.diag(0) + B.col(1);

	//输出元素:
	cout << "A = " << A << endl << endl;
	cout << "B = " << B << endl << endl;
	cout << "C = " << C << endl << endl;
	cout << "A = " << A << endl << endl;
	//C矩阵与B的主对角线的点积 5*4+8*4+5*4+5*4;
	cout << "C .* diag(B) = " << C.dot(B.diag(0)) << endl;

	return 0;
}

在这里插入图片描述

7、Mat_类

Mat_类是对 Mat 类的一个包装,其定义如下:

template<typename _Tp> class Mat_ : public Mat
{
	public:
	//只定义了几个方法
	//没有定义新的属性
};

这是一个非常轻量级的包装,既然已经有 Mat 类,为何还要定义一个 Mat_?
下面我们看这段代码:

Mat M(600, 800, CV_8UC1);
for( int i = 0; i < M.rows; ++i){
    uchar * p = M.ptr<uchar>(i);
	for( int j = 0; j < M.cols; ++j ){
		double d1 = (double) ((i+j)%255);
		M.at<uchar>(i,j) = d1;
		double d2 = M.at<double>(i,j);//此行有错
	}
}

在读取矩阵元素时,以及获取矩阵某行的地址时,需要指定数据类型。这样首先需要不停地写“”,让人感觉很繁琐,在繁琐和烦躁中容易犯错,如上面代码中的错误,用 at()获取矩阵元素时错误的使用了 double 类型。这种错误不是语法错误,因此在编译时编译器不会提醒。在程序运行时,at()函数获取到的不是期望的(i,j)位置处的元素,数据已经越界,但是运行时也未必会报错。这样的错误使得你的程序忽而看上去正常,忽而弹出“段错误”,特别是在代码规模很大时,难以查错。

如果使用 Mat_类,那么就可以在变量声明时确定元素的类型,访问元素时不再需要指定元素类型,即使得代码简洁,又减少了出错的可能性。上面代码可以用 Mat_实现.实现代码如下面例程里的第二个双重 for 循环。

#include <iostream>
#include "opencv2/opencv.hpp"
#include <stdio.h>
using namespace std;
using namespace cv;
int main(int argc, char* argv[])
{
	Mat M(600, 800, CV_8UC1);
	for (int i = 0; i < M.rows; ++i)
	{
		//获取指针时需要指定类型
		uchar * p = M.ptr<uchar>(i);
		for (int j = 0; j < M.cols; ++j)
		{
			double d1 = (double)((i + j) % 255);
			//用 at()读写像素时,需要指定类型
			M.at<uchar>(i, j) = d1;
			//下面代码错误,应该使用 at<uchar>()
			//但编译时不会提醒错误
			//运行结果不正确,d2 不等于 d1
			//double d2 = M.at<double>(i, j);
		}
	}

	//在变量声明时指定矩阵元素类型
	Mat_<uchar> M1 = (Mat_<uchar>&)M;
	for (int i = 0; i < M1.rows; ++i)
	{
		//不需指定元素类型,语句简洁
        uchar * p = M1.ptr(i);
		for (int j = 0; j < M1.cols; ++j)
		{
		
			double d1 = (double)((i + j) % 255);
			//直接使用 Matlab 风格的矩阵元素读写,简洁
			M1(i, j) = d1;
			double d2 = M1(i, j);
		}
	}
	return 0;
}

8、Mat类的内存管理

使用 Mat 类,内存管理变得简单,不再像使用 IplImage 那样需要自己申请和释放内存。虽然不了解 Mat 的内存管理机制,也无碍于 Mat 类的使用,但是如果清楚了解 Mat 的内存管理,会更清楚一些函数到底操作了哪些数据。

Mat 是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸,存储方法,存储地址等信息)和一个指向存储所有像素值的矩阵的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大数个数量级。复制矩阵数据往往花费较多时间,因此除非有必要,不要复制大的矩阵。

为了解决矩阵数据的传递,OpenCV 使用了引用计数机制。其思路是让每个Mat 对象有自己的矩阵头信息,但多个 Mat 对象可以共享同一个矩阵数据。让矩阵指针指向同一地址而实现这一目的。很多函数以及很多操作(如函数参数传值)只复制矩阵头信息,而不复制矩阵数据。

前面提到过,有很多中方法创建 Mat 类。如果 Mat 类自己申请数据空间,那么该类会多申请 4 个字节,多出的 4 个字节存储数据被引用的次数。引用次数存储于数据空间的后面,refcount 指向这个位置,如图所示。当计数等于 0时,则释放该空间。

Dk4MjYx,size_16,color_FFFFFF,t_70)
关于多个矩阵对象共享同一矩阵数据,我们可以看这个例子:

Mat A(100,100, CV_8UC1);
Mat B = A;
Mat C = A(Rect(50,50,30,30));

上面代码中有三个 Mat 对象,分别是 A,B 和 C。这三者共有同一矩阵数据:
在这里插入图片描述

9、输出

从前面的例程中,可以看到 Mat 类重载了<<操作符,可以方便得使用流操作来输出矩阵的内容。默认情况下输出的格式是类似 Matlab 中矩阵的输出格式。除了默认格式,Mat 也支持其他的输出格式。代码如下:

  • 首先创建一个矩阵,并用随机数填充。填充的范围由 randu()函数的第二个参数和第三个参数确定,下面代码是介于 0 到 255 之间。

建立了3*2的一个图像,有3个通道
Mat R = Mat(3, 2, CV_8UC3);
三通道的BGR取值都是随机数 0~255
randu(R, Scalar::all(0), Scalar::all(255));

默认格式输出的代码如下:

cout << "R (default) = " << endl << R << endl << endl;

在这里插入图片描述

python 格式输出的代码如下:

//cout << "r(Python风格_openCV2) = " << format(r, “python”) << “;” << endl << endl;
cout << "r(Python风格_openCV3) = " << endl << format(r, Formatter::FMT_PYTHON) << endl << endl;
在这里插入图片描述

以逗号分割的输出的代码如下:

//cout << "r(,分隔风格_openCV2) = " << format(r, “csv”) << “;” << endl << endl;
cout << "r(,分隔风格_openCV3) = " << endl << format(r, Formatter::FMT_CSV) << endl << endl;
在这里插入图片描述

numpy 格式输出的代码如下:

//cout << "r(numpy风格_openCV2) = " << format(r, “numpy”) << “;” << endl << endl;
cout << "r(numpy风格_openCV3) = " << endl << format(r, Formatter::FMT_NUMPY)<< endl << endl;
在这里插入图片描述

C 语言格式输出的代码如下:

// cout << "r(C语言风格_openCV2) = " << format(r, “c”) << “;” << endl << endl;
cout << "r(C语言风格_openCV3) = " << endl << format(r, Formatter::FMT_C) << endl << endl;
在这里插入图片描述

  • 除了 Mat 对象可以使用<<符号输出,其他的很多类型也支持<<输出。

二维点:

Point2f P(5, 1);
cout << "Point (2D) = " << P << endl << endl;
在这里插入图片描述

三维点:

Point3f P3f(2, 6, 7);
cout << "Point (3D) = " << P3f << endl << endl;
在这里插入图片描述

10、Mat与IpIImage和CvMat的转换

在 OpenCV 2 中虽然引入了方便的 Mat 类,出于兼容性的考虑,OpenCV 依然是支持C 语言接口的 IplImage 和 CvMat 结构。如果你要与以前的代码兼容,将会涉及 Mat 与IplImage 和 CvMat 的转换。

10.1、Mat转为IpIImage和CvMat格式

假如你有一个以前写的函数,函数的定义为:

void mycvOldFunc(IplImage * p, …);

函数的参数需要 IplImage 类型的指针。Mat 转为 IplImage,可以用简单的等号赋值操作来进行类型转换,这样实现:

Mat img(Size(320, 240), CV_8UC3);
···
IplImage iplimg = img; //转为 IplImage 结构
mycvOldFunc( & iplimg, ...);//对 iplimg 取地址

如果要转为 CvMat 类型,操作类似:
CvMat cvimg = img; //转为 CvMat 结构

需要特别注意的是,类型转换后,IplImage 和 CvMat 与 Mat 共用同一矩阵数据,而 IplImage 和 CvMat 没有引用计数功能,如果上例中的 img 中数据被释放,iplimg 和 cvimg 也就失去了数据。因此要牢记不可将 Mat 对象提前释放。

10.2、IpIImage和CvMat格式转化为Mat

Mat 类有两个构造函数,可以实现 IplImage 和 CvMat 到 Mat 的转换。这两个函数都有一个参数copyData。如果copyData的值是false,那么Mat将与IplImage或 CvMat 共用同一矩阵数据;如果值是 true,Mat 会新申请内存,然后将 IplImage或 CvMat 的数据复制到 Mat 的数据区。

如果共用数据,Mat 也将不会使用引用计数来管理内存,需要开发者自己来管理。本书建议做此转换是将参数置为 true,这样内存管理变得简单。

Mat::Mat(const CvMat* m, bool copyData=false)
Mat::Mat(const IplImage* img, bool copyData=false)

例子代码如下:

IplImage * iplimg = cvLoadImage("lena.jpg");
Mat im(iplimg, true);

猜你喜欢

转载自blog.csdn.net/qq_41498261/article/details/83833780
今日推荐