Opencv学习四基础容器Mat与常用数据结构

**

前言

**
加油啊!现在是学习core组件的时候了,虽然不知道有些什么内容,但是根据《Opencv3编程入门》看总共就只剩下,imgproc组件和feature2d组件了。真想快点入门,好有水平深入学习。
在这里插入图片描述

**

Mat类

**
Mat类由两个数据部分组成:矩阵头-----包括矩阵尺寸、存储方法、存储地址等信息。还有一个指向所有像素值的矩阵------根据所选存储方法的不同,矩阵可以是不同的维度----的指针。另外注意,一般情况下所有像素值的矩阵的元素在内存上是连续存储的,即无论几个维度,都会转换成1维存储----有时候则不是连续的。

矩阵头的尺寸是常数,但是矩阵本身的尺寸会改变,通常比矩阵头大数个数量级。因此,当程序中传递图像并创建副本时,大开销是由矩阵造成的。那么,除非万不得已,不应该进行大图像的复制,那会降低程序的运行速度。

  • 为了解决此问题,Opencv使用了引用计数机制------与智能指针相关------思路是让每个Mat对象有自己的信息头,但共享一个矩阵。这通过让矩阵指针指向同一地址实现。而拷贝构造函数只负责复制信息头和矩阵指针,不复制矩阵。
#include<opencv2/opencv.hpp>
#include<iostream>
#include<iomanip>
using namespace std;
using namespace cv;




int main() {

	//仅创建信息头部分
	Mat A,C;
	cout <<"A size is "<< A.size() <<setw(5)<<"\tA info header address is "<< &A<<setw(5)<<"\tA matrix address is "<<A.ptr<double>(0)<< endl;
	cout << "C size is " << C.size() << setw(5) << "\tC info header address is " << &C << setw(5) << "\tC matrix address is " <<C.ptr<double>(0) << endl;

	//为矩阵开辟内存
	A = imread("E:/File/002.jpg");
	cout << "A size is " << A.size() << setw(5) << "\tA info header address is " << &A << setw(5) << "\tA matrix address is " << A.ptr<double>(0) << endl;

	//拷贝构造
	Mat B(A);
	cout << "B size is " << B.size() << setw(5) << "\tB info header address is " << &B <<setw(5) << "\tB matrix address is " <<B.ptr<double>(0) << endl;

	//赋值运算
	C = A;
	cout << "C size is " << C.size() << setw(5) << "\tC info header address is " << &C << setw(5) << "\tC matrix address is " << A.ptr<double>(0) << endl;


	//使用矩阵界定
	Mat D(A, Rect(0, 0, 100, 100));
	cout << "D size is " << D.size() << setw(5) << "\tD info header address is " << &D << setw(5) << "\tD matrix address is " <<D.ptr<double>(0) << endl;


	//使用行、列来界定
	Mat E = A(Range::all(), Range(0,3));
	cout << "E size is " << E.size() << setw(5) << "\tE info header address is " << &E << setw(5) << "\tE matrix address is " << E.ptr<double>(0) << endl;


	system("pause");
}

虽然这样的确节约了空间,但是有时确实需要复制矩阵本身,可以使用.clone()或者copyTo()。

Mat F=A.clone();
Mat G;
A.copyTo(G);

像素值的存储方法和颜色空间
在这里插入图片描述
上面这个是BGR色彩空间在内存上的示意图,这个矩阵里每一个元素都是由三个块(blue、green、red)组成的。
下面是BGR色彩空间的逻辑示意图,了解色彩空间对OpenGL学习也有帮助。
在这里插入图片描述

颜色系统有很多:

  • RGB是最常见的,因为人眼采用相似的工作机制
  • HSV和HLS把颜色分为色调、饱和度和亮度(明度)。这是描述颜色更自然的方式,比如可以通过抛弃最后一个元素,使算法对输入图像的光照条件不敏感
  • YCrCb在JPEG图像格式中广泛使用
  • CIE Lab*是一种在感知上均匀的颜色空间,适合度量两个颜色之间的距离

图像每个元素有自己的定义域,而定义域取决于其数据类型,如何存储一个元素决定了我们在其定义域上能控制的精度。最小的数据类型是char,占1Byte(8-bit),可以是有符号型,0~255之间。
或者无符号型, -127~+127之间。

尽管使用3个char,已经可以表示1600万种可能的颜色(RGB色彩空间),但若使用float(4-Byte)或double(8-Byte),则能给出更加精细的颜色分辨能力。

  • 可是,增加元素的尺寸也会增加图像所占的内存空间

**

创建Mat对象

**

#include<opencv2/opencv.hpp>
#include<iostream>
#include<cmath>
using namespace std;
using namespace cv;

int main() {

	Mat img1 = Mat(4, 6, CV_8UC1);//创建一个4行6列,8位单通道矩阵
	/*
	Scalar::all(n) ---> 将Scalar结构体中的4个元素全部初始化为n
	randu()  ---> 用颜色类Scalar设定随机数范围,再把随机数放到矩阵中
	*/
	randu(img1, Scalar::all(0), Scalar::all(10));//从0-9产生随机数填充矩阵
	cout << "img1=" << endl << img1 << endl << endl;


	cout << "img1's rows = " << img1.rows << endl
		<< "img1's cols = " << img1.cols << endl
		<< endl << "img1's 2 row to 3 row are " <<endl<< img1.rowRange(1, 3)    //这个取前不取后 rowRange(x,y) --->  坐标(x,y-1)
		<< endl << endl << "img1's 1 col to 4 col are " <<endl<< img1.colRange(0, 4)
		<< endl << endl;


	//img1's 3 col equal to the number of img1's 6 col multiply 2
	img1.col(2)= img1.col(5) * 2;
	cout << "原始img1第三列元素变换成img1第六列元素乘2 = " << endl<<img1 << endl;

	//只能用()不能用[]
	Mat img2 = img1.row(2);
	cout << "img1's 3 row = " << endl << img2 << endl;

	img1.col(1).copyTo(img2);
	cout << "img1's 2 col = " << endl << img2 << endl;


	//创建3*3复数矩阵 1+5*j
	Mat img3(3, 3, CV_32FC2, Scalar(1, 5));
	cout << "img3 = " << img3 << endl;

	//重新创建5*5的8位无符号3通道矩阵
	img3.create(5, 5, CV_8UC(3));
	cout << "img3's channels : " << img3.channels() << endl;
	cout << "img3 = " << img3 << endl;

	//转换矩阵类型
	img3.convertTo(img3, CV_32F);
	cout << "img3's depth is " << img3.depth() << endl;
	cout << "img3 = " << img3 << endl;


	//创建全为0的矩阵
	Mat img4 = Mat::zeros(img3.rows, img3.cols, CV_8UC(1));
	cout << "img4 = " << endl << img4 << endl << endl;




	system("pause");
}

或者是对小矩阵,用逗号分割式初始化

Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = \n" << C << endl << endl;

对已经存在的Mat对象,创建新的信息头

Mat RowClone = C.row(1).clone();
cout << "RowClone = \n" << RowClone << endl << endl;

Mat矩阵的创建

Mat E=Mat::eye(4,4,CV_64F);//4X4单位矩阵
Mat O=Mat::ones(2,2,CV_32F);//2X2全为1矩阵
Mat Z=Mat::zeros(3,3,CV_8UC(3));//3X3,3通道全为0矩阵

**

Opencv中的格式化输出方法

**
先初始化一个矩阵
(在使用格式化输出时,在第二个开始出现枚举常量未定义的情况(VS2017中出现红色波浪线),但是复制前一个,再把常量改成需要的枚举常量就没错了,我想这可能是编译器的Profile相关的问题)

Mat R = Mat(3, 2, CV_8UC3);
randu(R, Scalar::all(0), Scalar::all(255));
cout << "R = \n" << R << endl << endl;

Opencv默认风格:

cout << "R (OpenCV默认风格) = \n" << R << "\n\n";
cout << "R (OpenCV默认风格) = \n" << format(R,Formatted::FMT_DEFAULT)<< "\n\n";

在这里插入图片描述
Python风格

cout << "R (Python风格) = \n" << format(R, Formatter::FMT_PYTHON) << "\n\n";

在这里插入图片描述
逗号分隔风格
comma separated values,CSV

cout << "R (逗号分隔风格) = \n" << format(R, Formatter::FMT_CSV) << "\n\n";

在这里插入图片描述
Numpy风格

cout << "R (Numpy风格) = \n" << format(R, Formatter::FMT_NUMPY) << "\n\n";

在这里插入图片描述
C语言风格

cout << "R (C语言风格) = \n" << format(R, Formatter::FMT_C) << "\n\n";

在这里插入图片描述
Matlab风格

	cout << "R (Matlab语言风格) = \n" << format(R, Formatter::FMT_MATLAB) << "\n\n";

在这里插入图片描述

**

常用数据结构

**

	//2D Point
	Point2f p(5, 1);
	cout <<"Point(2D) = \n" << p << "\n\n";
	
	//3D Point
	Point3f p3f(2, 6, 7);
	cout<< "Point(3D) = \n" << p3f << "\n\n";



	//std::vector via cv::Mat
	vector<float> v;
	v.push_back((float)CV_PI);
	v.push_back(2);
	v.push_back(3.01f);
	cout << "Vector of float via Mat = \n" << Mat(v) << "\n\n";


	//std::vector of points
	vector<Point2f> vPoints(20);
	for (size_t E = 0; E < vPoints.size(); ++E) {
		vPoints[E] = Point2f((float)(5 * E), (float)(E % 7));
	}
	cout << "A vector of 2D points = \n" << vPoints<<"\n\n";

点是表示:Point类

Point类数据结构表示二维坐标系下的点,用法如下:

Point p;
p.x=10;
p.y=8;
//或者
Point p=Point(10,8);
//或者
Point p(10,8);

因为Opencv中有如下的定义:

typedef Point_<int> Point2i;
typedef Point_<int64> Point2l;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Point2i Point;

所以,

  • Point_、Point2i、Point等价。
  • Point_、Point2f等价等等。

颜色的表示:Scalar类
Scalar表示具有4个元素的数组,在Opencv中被大量用于传递像素值,如BGR颜色值。Opencv毕竟是C++写的,参数个数的使用很随意。

Scalar(blue,green,red)

Scalar类的源头是Scalar_类,这是Vec4x的变种,常用的Scalar是Scalar_。这就是为什么很多函数的参数可以是Mat,也可以是Scalar。

尺寸的表示:Size类

typedef Size_<int> Size2i;
typedef Size_<int64> Size2l;
typedef Size_<float> Size2f;
typedef Size_<double> Size2d;
typedef Size2i Size;

和Point类类似。Size_是个模板类,上面的意思是

  • Size_、Size2i、Size等价。
  • Size_ 、Size2f等价等等。

template<typename _Tp> class Size_
{
public:
    typedef _Tp value_type;

    //! default constructor
    Size_();
    Size_(_Tp _width, _Tp _height);
    Size_(const Size_& sz);
    Size_(Size_&& sz) CV_NOEXCEPT;
    Size_(const Point_<_Tp>& pt);

    Size_& operator = (const Size_& sz);
    Size_& operator = (Size_&& sz) CV_NOEXCEPT;
    //! the area (width*height)
    _Tp area() const;
    //! aspect ratio (width/height)
    double aspectRatio() const;
    //! true if empty
    bool empty() const;

    //! conversion of another data type.
    template<typename _Tp2> operator Size_<_Tp2>() const;

    _Tp width; //!< the width
    _Tp height; //!< the height
};

上面是Size_模板类的定义,内部重载了一些构造函数,使用频率最高的是:Size_(_Tp _width, _Tp _height);

可以看到代码末尾定义了模板类型的宽高:

  _Tp width; //!< the width
  _Tp height; //!< the height

下面是最后一个常用数据结构
矩阵的表示:Rect类

Rect成员有x,y,width,height,分别左上角坐标和矩形宽高。常用成员函数有:

  • .size()返回size
  • .area()返回面积
  • .contains(Point)判断点是否在矩形内
  • .empty()判断矩形是否为空
  • .tl()返回左上角坐标
  • .br()返回右下角坐标

求两个矩形交集:Rect rect = rect1 & rect2 ;
并集:Rect rect = rect1 | rect2 ;
让矩形平移:Rect rectShift = rect + point ;
缩放:Rect rectScale = rect + size ;

参考:《Opencv3编程入门》

猜你喜欢

转载自blog.csdn.net/weixin_41374099/article/details/86564940
今日推荐