OpenCV杂记 - Mat in C++

1 概述

Mat类是OpenCV表达二维图片的基础。

经过简单阅读有关Mat的documentation,记录其中提出的几个关键要点。

 

  • OpenCV 的API函数,对图像的输出参数都会自动管理内存,调用者可不必亲自管理。但是如果调用者已经对输出参数配置好了合适大小的内纯,API会自动福永该内存而不是重新申请。
  • 在使用OpenCV提供的API时,大部分时间不需要考虑内存管理。
  • Mat的 = 运算符重载和拷贝构造函数,都不会复制Mat的数据区。若想获得数据区的副本,则需要使用 Mat::clone()和Mat::copyTo()函数。

OpenCV使用BGR格式存储三原色。

对于二维的Mat,可以使用 << 操作符将矩阵内容显示在屏幕上(插入流)。

Mat的数据格式使用预定义的类似CV_8UC3一样的宏。其中8U代表8bit的无符号整数,C3代表每个pixel有三个通道,8UC3即表示每个pixcel共需要24 bit,3 byte的数据进行存储。注意,在C++代码中,若没有申明using namespace cv; 那么在使用CV_8UC3一类的宏时,不能在前面加 cv:: 作用域修饰,这会导致预编译器在做宏展开时报错

创建一个Mat可以使用如下形式的语句。

 

cv::Mat M(2,2, cv::CV_8UC3, cv::Scalar(0,0,255));

 

其中cv::Scalar是一个4元标量,这里仅使用了其前3个元素。

 

OpenCV提供类似于MATLAB的eye(), ones(), zeros()函数,但需要指定数据格式。

Mat的 << 操作符支持使用不同的格式输出,输出的格式涵盖python默认,numpy和c语言的格式。

 

经过学习Mat的一些特性,了解到Mat实际是为了处理图像而设计的一种矩阵表示方法,是图像表示成了矩阵,而不是用矩阵表示图像。这里我们不能假定Mat的所有特性与一般意义下的矩阵是一致的,这充分体现在Mat的数据格式和Mat的算术运算上。

 

2 获取单个数据

像std::vector那样提供了at()函数,但at()是一个模板函数,需要在使用是制定数据类型。

 

3 算数运算

对于一个提供矩阵表达和运算的库,应该测试一下某些关键函数的行为。这里测试了转置函数,加法,减法,数乘和矩阵乘法,确实发现了一些OpenCV的特性。

3.1 转置

与预想基本一致,Mat的转置函数t(), 返回一个MatExpr类型的对象,可以参与其他算数运算或者赋值给另外一个Mat对象。当进行赋值时,例如B = A.t(); B将不共享A的存储,B与A的存储是独立的,互不影响。

 

3.2 加法,数乘

测试了标量数值与矩阵的加减法,也测试了矩阵之间的加减法。OpenCV重载了诸如 +, -, *, +=, -=, *=这类运算符,但仅限于数据类型一致的情况。数据类型不一致时需要使用函数接口来完成特定的运算。例如加法使用cv::add()函数。

另外,对于矩阵加减法,数与矩阵加减法,在使用+, += 或者相应的减法运算符时,OpenCV会根据当前的数据格式进行自动截断。例如CV_8UC1类型的数据,最大值为255。所有加法运算结果超过255的元素,都将截断为255。这正是处理图像数据时需要的特性,而不能用于一般情况下的矩阵运算。当需要进行更一般的运算时,需要使用更大的数据深度,或者使用浮点数据类型。上述关于数据自动截断的特性,cv::add()函数同样具有。

 

3.3 矩阵乘法

更加关心的是矩阵乘法。这里特指与通常情况下数学上定义的乘法一致的矩阵乘法,而非element-wise multiplication。类比于MATLAB,也就是 * 而不是 .* 。

OpenCV强制规定,Mat之间的乘法只能在如下4种数据格式上进行:CV_32FC1, CV_32FC2, CV_64FC1 以及 CF_64FC2。并且两个Mat必须具有相同的数据格式。这个规定体现在OpenCV的源码上,以现在手上的版本(3.4.1)为例,该限制出现在 modules/core/src/matmul.cpp: 1558位置,如图所示。

 

 

在进行矩阵乘法前,可能需要对现有Mat对象进行数据格式的转换,这通过Mat的convertTo()成员函数实现。converTo()函数可以用于转换自身的数据格式,例如一个Mat对象A,可以这样:

 

A.convertTo(A, CV_64FC1);

 

另外,在运用convertTo()时,像进行加法和数乘一样,OpenCV也会进行自动数据截断。

 

矩阵乘法也重载了 *= 运算符。可以 A *= A; 这样使用。

今天先到这里,上一波简单代码。

 

cv::Mat A = ( cv::Mat_<unsigned char>(3, 3) << 1, 2, 3, 4, 5, 6, 7, 8, 9 );

	std::cout << "A = " << std::endl << A << std::endl << std::endl;

	// Test the t() function.
	std::cout << "A.t() = " << std::endl << A.t() << std::endl << std::endl;
	std::cout << "After transposition, A = " << std::endl << A << std::endl << std::endl;

	// Test the t() function with assignment.
	cv::Mat B = A.t();

	std::cout << "B = " << std::endl << B << std::endl << std::endl;
	B.at<unsigned char>(1,2) = 10;

	std::cout << "After assigning value to B." << std::endl;
	std::cout << "A = " << std::endl << A << std::endl;
	std::cout << "B = " << std::endl << B << std::endl;

	// Test arithmetic operations.
	cv::Mat C = A.clone();

	C += 1;

	std::cout << "C += 1 -> " << std::endl << C << std::endl << std::endl;

	C -= 1;

	std::cout << "C -= 1 -> " << std::endl << C << std::endl << std::endl;

	C *= 10;

	std::cout << "C *= 10 -> " << std::endl << C << std::endl << std::endl;

	C = A.clone();

	C += A;

	std::cout << "C += A -> " << std::endl << C << std::endl << std::endl;

	C -= A;

	std::cout << "C -= A -> " << std::endl << C << std::endl << std::endl;

	C = A.clone();
	C += 250;

	std::cout << "C += 250 -> " << std::endl << C << std::endl << std::endl;

	C = A.clone();
	C -= 250;

	std::cout << "C -= 250 -> " << std::endl << C << std::endl << std::endl;

	C = A.clone();
	C *= 100;

	std::cout << "C *= 100 -> " << std::endl << C << std::endl << std::endl;

	cv::Mat D;
	A.convertTo(D, CV_32FC1); // Only CV_32FC1, CV_32FC2, CV_64FC1 and, CV_64FC2 could be used to do matrix multiplication.

	std::cout << "D = " << std::endl << D << std::endl << std::endl;

	D *= D;

	std::cout << "D *= D -> " << std::endl << D << std::endl << std::endl;

	cv::Mat DD = A.clone();
	std::cout << "DD.type() = " << DD.type() << std::endl;
	std::cout << "DD = " << std::endl << DD << std::endl << std::endl;
	DD.convertTo(DD, CV_64FC1);
	std::cout << "DD.type() = " << DD.type() << std::endl;
	std::cout << "DD = " << std::endl << DD << std::endl << std::endl;

	DD *= 100;
	std::cout << "DD *= 100 -> " << std::endl << DD << std::endl << std::endl;

	DD.convertTo(DD, CV_8UC1);
	std::cout << "DD convert to CV_8UC1  -> "<< std::endl << DD << std::endl << std::endl;

	DD = A.clone();
	DD.convertTo(DD, CV_64FC1);
	DD -= 250;
	std::cout << "DD convert to CV_64FC1, -= 250 -> " << std::endl
			  << DD << std::endl << std::endl;
	DD.convertTo(DD, CV_8UC1);
	std::cout << "DD convert to CV_8UC1 -> " << std::endl
			  << DD << std::endl << std::endl;

	cv::Mat E;
//	E = D + A; // This will cause a runtime exception.
//	std::cout << "E.type() = " << E.type() << std::endl << std::endl;

	cv::add( A, D, E, cv::Mat(), CV_32SC1 );
	std::cout << "E = A + D -> " << E << std::endl << std::endl;
	std::cout << "E.type() = " << E.type() << std::endl << std::endl;

	B = 250;

	cv::add(B, D, E, cv::Mat(), CV_32SC1);
	std::cout << "B = 250, E = B + D with CV_32SC1 -> " << std::endl << E << std::endl << std::endl;

	cv::add(B, D, E, cv::Mat(), CV_8UC1);
	std::cout << "B = 250, E = B + D with CV_8UC1 -> " << std::endl << E << std::endl << std::endl;

 

 

 

发布了73 篇原创文章 · 获赞 24 · 访问量 13万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览