一、基础图像容器Mat
1.1Mat介绍
Mat是用来储存数字图像的一个类。它是IplImage的升级。Mat相对于IplImage最大的优点就是:不用手动开辟和释放图像内存,Mat能够自动管理内存。
Mat是一个类。包含两个数据部分:(1)矩阵头(包含矩阵尺寸,储存方法、存储地址等信息);(2)一个指向储存所有像素值的矩阵(根据所储存方法的不同,矩阵可以是不同的维数)的指针。
为了提高Opencv的运行速度,采用了引用计数机制。其思路是让每个Mat对象有自己的信息头(矩阵头),共享同一个矩阵。在Opencv中,各个函数之间传递图像就是传递信息头,而不是传递矩阵数据,从而提高Opencv运行速度。
注意:一个矩阵被多个Mat类型的对象使用,这个矩阵清理由最后一个使用它的对象负责清理。
1.2Mat的复制:
1,Mat类的拷贝构造函数只复制信息头和矩阵指针,而不复制矩阵。
下面是两种只复制矩阵头的方法例程:
Mat A, C;
A = imread("psb.jpg");
/*使用构造函数进行复制*/
Mat B(A);
/*使用赋值运算符进行复制*/
C = A;
2,复制矩阵数据:
在一些情况下我们必须要复制Mat类的矩阵数据,就可以使用clone()或者是copyTo()。
例1:
Mat F =A.clone();
例2:
Mat G;
A.copyTo(G);
1.3,Mat对象创建的七种办法:
(1)【方法一】使用构造函数(最常用):
Mat M(2, 2, CV_8UC3, Scalar(0, 0, 255)); //创建
cout << "M = " << endl << " " << M << endl << endl; //显示
首先定义其行数和列数,然后指定存储元素的数据类型以及每个矩阵点的通道数,(CV_[位数][带符号与否][类型前缀]C[通道数],例:CV_8UC3,预先定义的通道数最多只能四通道,若需要更多通道数,可以使用大写宏,并把通道数放在括号中,如:CV_8UC(1))
int sz[3] = { 2, 2, 2 };
Mat L(3, sz, CV_8UC(1), Scalar::all(1));
(2)【方法二】使用已存在的IplImage指针创建信息头
IplImage* img = cvLoadImage("G:\\Opencv\\素材\\楪析.jpg", 1);
Mat mtx(img); //转换IplImage*->Mat
(3)【方法三】采用MATLAB式的初始化方式
采用MATLAB初始化矩阵的方式:zeros(),ones(),eyes().然后指定尺寸和数据类型:
Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << O << endl << endl;
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;
Mat Z = Mat::zeros(3, 3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;
(4)【方法四】对小矩阵使用逗号直接初始化:
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
(5)【方法五】为已存在的对象创建新信息头
其实就是先创建一个信息头(Mat RowClone;),然后从一个带有矩阵数据的Mat对象中复制其数据给这个信息头,对其初始化。
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;
1.4、Mat的格式化输出
主要介绍三种:
首先初始化一个Mat对象:
/*初始化一个Mat对象*/
Mat r = Mat(10, 3, CV_8UC3);
randu(r, Scalar::all(0), Scalar::all(255));
(1)opencv默认风格输出:
/*opencv默认风格输出*/
cout << "r(opencv默认风格) = " << endl << r << ";" << endl << endl;
(2)C语言风格输出:
/*C语言风格*/
cout << "r(C语言风格)=" << endl << format(r, "C") << ";" << endl << endl;
(3)逗号分隔的风格:
/*逗号分隔的风格*/
cout << "r(逗号分隔) = " << endl << format(r, "csv") << ";" << endl << endl;
二、其他数据结构
2.1点的定义和输出:Point类
//二维点的定义和输出
Point2f p(6, 2);
cout << "【二维点】 p = " << p << ";\n" << endl;
也可以这样表示:
Point point;
point.x = 10;
point.y = 8;
cout << point << endl;
另外还有三维点:
//三维点的定义和输出
Point3f p3f(8, 2, 0);
cout << "【三维点】p3f = " << p3f << ";\n" << endl;
然后我们查看Point的定义:
typedef Point_<int> Point2i;
typedef Point2i Point;
typedef Size_<int> Size2i;
typedef Size_<double> Size2d;
typedef Size2i Size;
typedef Rect_<int> Rect;
typedef Point_<float> Point2f;
typedef Point_<double> Point2d;
typedef Size_<float> Size2f;
typedef Point3_<int> Point3i;
typedef Point3_<float> Point3f;
typedef Point3_<double> Point3d;
可以发现:Point_,Point2i,Point互相等价。
2.2基于Mat的std:vector定义和输出
//基于Mat的std:vector定义和输出
vector<float> v;
v.push_back(3);
v.push_back(5);
v.push_back(7);
cout << "【基于Mat的vector】shortvec = " << Mat(v) << ";\n" << endl;
2.3定义和输出std::vector点
vector<Point2f> points(20);
for (size_t i = 0; i < points.size(); ++i)
{
points[i] = Point2f((float)(i * 5), (float)(i % 7));
}
cout << "【二维点向量】points =" << points << ";";
2.4颜色的表示:Scalar类
Scalar表示具有4个元素的数组,通常用来传递像素值。例如:RGB的颜色值。对于这个函数可以写三个函数,第四个可以不写。
如:
Scalar(a,b,c)
那么定义RGB颜色值:红色分量为c,绿色为b,蓝色为a;
2.5尺寸的表示:Size类
typedef Size_<int> Size2i;
typedef Size2i Size;
其中Size_是个模板类,在这里Size_表示类体内部的模板所代表的类型为int,这两句代码意思是:首先给已知的数据类型Size_起个新名字,叫Size2i.然后又给已知数据类型Size起个新名字,叫Size。这里主要是typedef的用法。
对于Size_模板类的内部构造函数,我们使用频率最高的是:
Size_(_Tp _width,_Tp_height);
定义的是宽度和高度。
例:
Size(5,5),表示XXX的宽度和高度都是5。
2.6矩阵的表示:Rect类
Rect类的成员变量有x,y,width,hight,分别为左上角的坐标和矩阵的宽和高。常用的成员函数有:size()返回值为size,area()返回矩阵的面积contains(Point)判断点是否在矩形中,inside(Rect)函数判断矩形是否在矩形内。t1()返回左上角点坐标,br()放回右下角点坐标。
Scalar和Point综合运用实例:
Draw.cpp文件:
#include"Draw.h"
/*
*主要功能:实现绘制不同角度,相同尺寸的椭圆
*椭圆绘制函数ellipse()函数参数介绍:
*将图像画到图像img上
*椭圆中心点Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2)
*椭圆旋转角度angle
*扩展的弧度从0度到360度
*图形颜色Scalar(255,129,0)
*线宽(thickness)为2
*线(lineType)为8(联通线型)
*/
void DrawEllipse(Mat img,double angle)
{
int thickness = 2;
int lineType = 8;
ellipse(img,
Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),
Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16),
angle,
0,
360,
Scalar(255, 129, 0),
thickness,
lineType);
}
/*
*功能:实现实心圆的绘制
*圆形绘制circl函数参数介绍:
*将图像画到图像img上
*圆的半径WINDOW_WIDTH/32
*圆的颜色Scalar(0,0,255),为红色
*线宽为 -1,即为实心
*线型(lineType)为8(联通线型)
*/
void DrawFilledCircle(Mat img, Point center)
{
int thickness = -1;
int lineType = 8;
circle(img,
center,
WINDOW_WIDTH / 32,
Scalar(0, 0, 255),
thickness,
lineType
);
}
/*
*功能:实现多边形的绘制
*函数fillPoly():用于将多边形画到图像img上
*多边形顶点集为ppt
*要绘制的多边形顶点数目为npt
*要绘制多边形数量仅为1
*多边形的颜色定义为白色Scalar(255,255,255)
*/
void DrawPolygon(Mat img)
{
int lineType = 8;
//创建一些点
Point rookPoint[1][20];
rookPoint[0][0] = Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
rookPoint[0][1] = Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
rookPoint[0][2] = Point(3 * WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
rookPoint[0][3] = Point(11 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoint[0][4] = Point(19 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoint[0][5] = Point(3 * WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
rookPoint[0][6] = Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoint[0][7] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoint[0][8] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoint[0][9] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoint[0][10] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoint[0][11] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
rookPoint[0][12] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoint[0][13] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoint[0][14] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
rookPoint[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoint[0][16] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
rookPoint[0][17] = Point(13 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
rookPoint[0][18] = Point(5 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
rookPoint[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
const Point* ppt[1] = { rookPoint[0] };
int npt[] = { 20 };
fillPoly(img,
ppt,
npt,
1,
Scalar(255, 255, 255),
lineType);
}
/*
*函数功能:实现线的绘制
*
*/
void DrawLine(Mat img, Point start, Point end)
{
int thickness = 2;
int lineType = 8;
line(img,
start,
end,
Scalar(0, 0, 0),
thickness,
lineType);
}
Draw.h文件:
#ifndef _DRAW_H
#define _DRAW_H
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#define WINDOW_WIDTH 600
#define WINDOW_NAME1 "【绘制图1】"
#define WINDOW_NAME2 "【绘制图2】"
void DrawEllipse(Mat img, double angle);
void DrawFilledCircle(Mat img, Point center);
void DrawPolygon(Mat img);
void DrawLine(Mat img,Point start,Point end);
#endif
main函数:
int main()
{
Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
/*绘制原子示例图*/
//绘制椭圆
DrawEllipse(atomImage, 90);
DrawEllipse(atomImage, 0);
DrawEllipse(atomImage, 45);
DrawEllipse(atomImage, -45);
//绘制圆心
DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));
/*制组合图*/
//绘制多边形
DrawPolygon(rookImage);
//绘制矩形
rectangle(rookImage,
Point(0, 7 * WINDOW_WIDTH / 8),
Point(WINDOW_WIDTH, WINDOW_WIDTH),
Scalar(0, 255, 255),
-1,
8);
DrawLine(rookImage, Point(0, 15 * WINDOW_WIDTH / 16), Point(WINDOW_WIDTH, 15 * WINDOW_WIDTH / 16));
imshow(WINDOW_NAME1, atomImage);
imshow(WINDOW_NAME2, rookImage);
waitKey(0);
return 0;
}
效果: