OpenCV数据结构和基本绘图—图像容器Mat, 数据结构,基本图像操作(5)

1.1基础图像容器 Mat

1.1.1 数字图像存储
显示场景中看到的物体是包含一个众多强度值的像素的矩阵,矩阵就是图像在数码设备中的表现形式。
1.1.2 Mat 结构的使用
Mat类:
(1)不必手动为其开辟空间;
(2)不需要时无需手动释放空间;
手动开辟空间并非必须,但它存在,多数OpenCV函数会手动的为输出数据开辟空间。当传递一个已经存在的Mat对象时,开辟好的空间会被重用。也就是我们使用大小正好的内存来完成任务。
Mat 是一个类,它由两部分组成:矩阵头(包含矩阵尺寸,存储方式,存储地址信息)和一个指向存储所有像素值的矩阵(根据所选存储方式的不同,矩阵可以是不同的维度)的指针。矩阵头的尺寸是常数值,但矩阵本身的尺寸会依图像的不同而不同,通常比矩阵头的尺寸大。
在做计算量很大的图像处理算法时,不该进行图像的复制,这会降低程序的运行速度。为了解决此类问题,OpenCV使用了引用记数机制,就是让每个Mat有自己的矩阵头,但共享同一矩阵。这通过让矩阵指针指向同一地址实现。拷贝构造函数则只是复制信息头和矩阵指针,而不复制矩阵。

Mat A,C;  //仅创建矩阵头
A = imread("1.jpg", CV_LOAD_IMAGE_COLOR);  //为矩阵开辟内存空间
Mat B(A);  //拷贝构造
C = A;  //幅值

上述例子,所有的Mat类对象(矩阵头不同)最终都指向同一个矩阵。

我们可以创建只引用部分数据的信息头,感兴趣区域(ROI),只需要创建包含边界的信息头:

Mat D(A, Rect(0, 0, 100, 100));  //使用矩阵定界
Mat E = A(Range:all(), Range(1,3));  //用行和列来定界

如果矩阵属于多个Mat对象,不需要时,谁来清除?
最后一个使用它的对象进行释放 通过引用记数机制来实现,复制一个Mat对象,对一次矩阵的引用。
如果想复制矩阵本身,可以使用clone(), copyTo()函数

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

上述改变T,G不会影响Mat信息头所指向的矩阵(A矩阵指针指向的矩阵).
总上所述:

  • 赋值运算符和拷贝构造(构造函数)只复制信息头(信息头尺寸,存储方式,存储地址);
  • 使用函数clone()或copyTo()来复制一幅图像的矩阵;

1.1.3 像素值存储的方式
存储像素值需要指定颜色空间数据类型
颜色空间:是指针对一个给定的颜色,如何组合颜色元素以对其编码。最简单的颜色空间是灰度级空间,只处理黑色和白色,对他们组合就可产生不同程度的灰色。
颜色系统:

  • RGB,被显示设备采用,红绿蓝为基色,有时为了表示透明也会加入alpha(A);
  • HSV和HLS 把颜色分解为色调,饱和度,明亮。这个是描述颜色最自然的方式。
  • YCrCb 在JPEG图像格式中使用
    -CIE L*a*b 是一种在感知上均匀的颜色空间,它适合度量两个颜色之间的距离;

1.1.4 创建Mat对象的方法
Mat不仅是一个图像容器类,同时也是一个通用的矩阵类。
创建一个Mat对象的多种方式:

  • 使用Mat()构造函数
    最常用的方法就是使用Mat()构造函数。
Mat M(2,2, CV_8UC3, Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;

在这里插入图片描述
CV_8UC3:CV_[位数][带符号与否][类型前缀]C[通道数]
Scalar(0,0,255):能使用指定的定制化值来初始化矩阵,它还可以用于表示颜色。

  • 利用create()函数
    利用Mat类中的create()函数进行Mat类的初始化的操作;
Mat M;
M.create(4,4,CV_8UC2);
cout << "M = " << endl << " " << M << endl << endl;

OpenCV数据结构和基本绘图—图像容器Mat, 数据结构,基本图像操作(5)_第1张图片

  • 对小矩阵使用逗号分割式初始化函数
 Mat M = (Mat_<double>(3,3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);

在这里插入图片描述

1.2 OpenCV中常用的数据结构和函数

1.2.1 点的表示:Point类
Point类数据表示了二维坐标系下的点,即由其图像坐标x和y指定的2D点。
Point point = Point(10,4);
OpenCV中有如下定义:
typedef Point_ Point2f;
typedef Point2i Point;
typedef Point_ point2f;
所以,Point_, Point2i, Point相互等价,Point_, Point2f相互等价。
1.2.2 颜色的表示:Scalar类
Scalar()表示具有4个元素的数组,在OpenCV中被用来大量传递像素值,如RGB颜色值。
例如:Scalar(a, b, c)
定义的RGB颜色值:红色分量为c, 绿色分量为b,蓝色分量为a;
Scalar类的源头时Scalar_类,而Scalar_类是Vec4x的一个变种,我们常用的Scalar就是Scalar_。这也解释了为什么很多函数的输入可以是Mat,也可以是Scalar;
1.2.3 矩形的表示:Rect类
Rect的类成员有x,y,width,height,分别是左上角点的坐标和矩形的宽度和高度。常用的成员函数有:Size()返回值为Size;area()返回矩形的面积;contains(Point)判断点是否在矩形内;inside(Rect)函数判断矩形是否在该矩形内;tl()返回左上角点的坐标; br()返回右下角点的坐标。如果想要求两矩形的交集和并集,可以用如下格式:

Rect rect = rect1 & rect2;
Rect rect = rect | rect2;

如果想让矩形进行平移和缩放操作,可以:

Rect rectShift = rect + point;
Rect rectScale = rect + size;

1.2.4 颜色空间的转换:cvtColor()函数
cvtColor()是OpenCV里的函数颜色空间转换函数,可以实现RGB颜色向HSV,HSI等颜色空间的转换,也可以转换为灰度图像。
函数原型:
void cvColor(InputArray src, OutputArray dst, int code, int dstCn=0);

  • 参数一:输入图像;
  • 参数二:输出图像;
  • 参数三:颜色空间转换的标志符;
  • 参数四:目标图像的通道数,如为0,表示取原图像的通道数;
    例如:
//OpenCV2版本:
cvtColor(srcImage, dstImage, CV_GRAY2BGR);   //原图转灰度图

//OpenCV3版本:
cvtColor(srcImage, dstImage, COLOR_GRAY2BGR);  

随着OpenCV版本的升级,cvtColor()函数对于颜色空间的种类的支持也增多。
OpenCV数据结构和基本绘图—图像容器Mat, 数据结构,基本图像操作(5)_第2张图片
OpenCV数据结构和基本绘图—图像容器Mat, 数据结构,基本图像操作(5)_第3张图片
对于颜色空间转换,OpenCV2的CV_前缀的宏命名规范,被OpenCV3中COLOR_式的宏命名前缀所取代。

1.3 基本图形的绘制

  • 用于绘制直线的line函数;
  • 用于绘制椭圆的ellipse函数;
  • 用于绘制矩形的rectangle函数;
  • 用于绘制原的circle函数;
  • 用于绘制填充的多边形的fillPoly函数;
    1.3.1 DrawEllipse()函数
    —————————————————————————————————————————————
    描述:绘制不同角度,相同尺寸的椭圆
    —————————————————————————————————————————————
#define WINDOW_WIDTH 600
void DrawEllipse(Mat image, double angle)
{
	int thickness = 2;
	int lineType = 8;
	ellipse(imaege, Point(WINDOW_WIDTH/2, WINDOW_WIDTH/2), Size(WINDOW_WHDTH/4, WINDOW_WIDTH/16),angle, 0, 360, Scalar(255, 192, 0), thickness, lineType);
}

函数解析:DrawEllipse()调用OpenCV中的ellipse函数,将椭圆回到image图像上
ellipse()函数的解析:

  • 参数一:将椭圆画到image图像上;
  • 参数二:椭圆中心的点;
  • 参数三:椭圆位于此矩形内;
  • 参数四:椭圆旋转角度为angle;
  • 参数五:可扩展的弧度从0~360;
  • 参数六:图形颜色Scalar(255,192,0)代表蓝色;
  • 参数七:线宽(thickness)为2;
  • 参数八:线形(lineType)为8(8连线型);

1.3.2 DrawFilledCirele()函数
————————————————————————————————————————————————
描述:绘制实心圆
————————————————————————————————————————————————

void DrawFilledCircle(Mat image, Point center)
{
	int thickness = -1;
	int lineType = 8;
	circle(image, center, WINDOW_WIDTH/32, Scalar(250, 192,0), thickness, lineType);
}

函数解析:DrawFilledCircle()函数调用OpenCV中的circle函数,将实心圆绘制到image上
circle()函数解析:

  • 参数一:将实心圆绘制到image图像上;
  • 参数二:圆心坐标;
  • 参数三:实心圆半径;
  • 参数四:圆的颜色;
  • 参数四:thickness=-1,绘制的圆是实心的;

1.3.3 DrawPolygon()函数
————————————————————————————————————————————————
描述:绘制凹多边行
————————————————————————————————————————————————

void DrawPolygon(Mat imaege)
{
	int lineType = 8;
	//创建一些点
        Point rookPoints[1][20];
        rookPoints[0][0]  = Point(    WINDOW_WIDTH/4,   7*WINDOW_WIDTH/8 );
        rookPoints[0][1]  = Point(  3*WINDOW_WIDTH/4,   7*WINDOW_WIDTH/8 );
        rookPoints[0][2]  = Point(  3*WINDOW_WIDTH/4,  13*WINDOW_WIDTH/16 );
        rookPoints[0][3]  = Point( 11*WINDOW_WIDTH/16, 13*WINDOW_WIDTH/16 );
        rookPoints[0][4]  = Point( 19*WINDOW_WIDTH/32,  3*WINDOW_WIDTH/8 );
        rookPoints[0][5]  = Point(  3*WINDOW_WIDTH/4,   3*WINDOW_WIDTH/8 );
        rookPoints[0][6]  = Point(  3*WINDOW_WIDTH/4,     WINDOW_WIDTH/8 );
        rookPoints[0][7]  = Point( 26*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][8]  = Point( 26*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][9]  = Point( 22*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][10] = Point( 22*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][11] = Point( 18*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][12] = Point( 18*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][13] = Point( 14*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][14] = Point( 14*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][15] = Point(    WINDOW_WIDTH/4,     WINDOW_WIDTH/8 );
        rookPoints[0][16] = Point(    WINDOW_WIDTH/4,   3*WINDOW_WIDTH/8 );
        rookPoints[0][17] = Point( 13*WINDOW_WIDTH/32,  3*WINDOW_WIDTH/8 );
        rookPoints[0][18] = Point(  5*WINDOW_WIDTH/16, 13*WINDOW_WIDTH/16 );
        rookPoints[0][19] = Point(    WINDOW_WIDTH/4,  13*WINDOW_WIDTH/16 );
		
		const Point* ppt[1] = {rookPoints[0]};
		int npt[] = {20};
		fillPoly(image, ppt, npt, 1, Scalar(255, 255, 255), lineType);
}

函数解析:DrawPolygon()调用了OpenCV中的fillPoly函数,用于将多边形画到image图像上
fillPoly()函数解析:

  • 参数一:将多边形绘制到image上;
  • 参数二:多边形的顶点集为ppt;
  • 参数三:要绘制的多边形顶点数目npt;
  • 参数四:要绘制的多边形数量为1;
  • 参数五:多边形的颜色为白色;

1.3.4 DrawLine()函数
————————————————————————————————————————————————
描述:绘制线
————————————————————————————————————————————————

void DrawLine(Mat image, Point start, Point end)
{
	int thickness = 1;
	int lineType = 8;
	line(image, start, end, thickness, lineType);
}

函数解析:DrawLine()调用OpenCV中的line()函数,用于将线段画到image图像上
line()函数解析:

  • 参数一:将线画到image图像上;
  • 参数二:线段的起点坐标位置;
  • 参数三:线段的结束坐标位置;
  • 参数四:线宽;
  • 参数五:此线段为8连通线段;

1.3.5 绘制图像

#include 
#include 
#include 
#include 
using namespace std;
using namespace cv;
//定义一些宏
#define WINDOW_NAME1  "[绘制图1]"    
#define WINDOW_NAME2  "[绘制图2]"
#define WINDOW_WIDTH 600  //定义窗口大小;

void DrawEllipse(Mat image, double angle);
void DrawFilledCircle(Mat image, Point center);
void DrawPolygon(Mat image);
void DrawLine(Mat image, Point start, Point end);

int main()
{
    
    //创建空白的Mat图像
    Mat atcoImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
    Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);

    //[1]绘制出椭圆
    DrawEllipse(atcoImage, 90);
    DrawEllipse(atcoImage, -45);
    DrawEllipse(atcoImage, 0);
    DrawEllipse(atcoImage, 45);

    //[2]画实心圆
    DrawFilledCircle(atcoImage, Point(WINDOW_WIDTH/2, WINDOW_WIDTH/2));

    //[3】绘制多边形
    DrawPolygon(rookImage);

    //[4]绘制矩形
    rectangle(rookImage, Point(0, 7 * WINDOW_WIDTH/8), Point(WINDOW_WIDTH, WINDOW_WIDTH), Scalar(0,255,255), -1, 8);

    //[5]绘制一些线段
    DrawLine(rookImage, Point(0, 15 * WINDOW_WIDTH/16), Point(WINDOW_WIDTH, 15*WINDOW_WIDTH/16));


    imshow(WINDOW_NAME1, atcoImage);
    moveWindow(WINDOW_NAME1, 0, 200);  //改变指定窗口的位置
    imshow(WINDOW_NAME2, rookImage);
    moveWindow(WINDOW_NAME2, WINDOW_WIDTH, 200);
    // waitKey(0);
    while(char (waitKey(1)) != 'q') {}
    
    return 0;
}

void DrawEllipse(Mat image, double angle)
{
	int thickness = 2;
	int lineType = 8;
	ellipse(image, Point(WINDOW_WIDTH/2, WINDOW_WIDTH/2), Size(WINDOW_WIDTH/4, WINDOW_WIDTH/16),angle, 0, 360, Scalar(255, 192, 0), thickness, lineType);
}

void DrawFilledCircle(Mat image, Point center)
{
	int thickness = -1;
	int lineType = 8;
	circle(image, center, WINDOW_WIDTH/32, Scalar(250, 192,0), thickness, lineType);
}
void DrawPolygon(Mat image)
{
	int lineType = 8;
	//创建一些点
        Point rookPoints[1][20];
        rookPoints[0][0]  = Point(    WINDOW_WIDTH/4,   7*WINDOW_WIDTH/8 );
        rookPoints[0][1]  = Point(  3*WINDOW_WIDTH/4,   7*WINDOW_WIDTH/8 );
        rookPoints[0][2]  = Point(  3*WINDOW_WIDTH/4,  13*WINDOW_WIDTH/16 );
        rookPoints[0][3]  = Point( 11*WINDOW_WIDTH/16, 13*WINDOW_WIDTH/16 );
        rookPoints[0][4]  = Point( 19*WINDOW_WIDTH/32,  3*WINDOW_WIDTH/8 );
        rookPoints[0][5]  = Point(  3*WINDOW_WIDTH/4,   3*WINDOW_WIDTH/8 );
        rookPoints[0][6]  = Point(  3*WINDOW_WIDTH/4,     WINDOW_WIDTH/8 );
        rookPoints[0][7]  = Point( 26*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][8]  = Point( 26*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][9]  = Point( 22*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][10] = Point( 22*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][11] = Point( 18*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][12] = Point( 18*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][13] = Point( 14*WINDOW_WIDTH/40,    WINDOW_WIDTH/4 );
        rookPoints[0][14] = Point( 14*WINDOW_WIDTH/40,    WINDOW_WIDTH/8 );
        rookPoints[0][15] = Point(    WINDOW_WIDTH/4,     WINDOW_WIDTH/8 );
        rookPoints[0][16] = Point(    WINDOW_WIDTH/4,   3*WINDOW_WIDTH/8 );
        rookPoints[0][17] = Point( 13*WINDOW_WIDTH/32,  3*WINDOW_WIDTH/8 );
        rookPoints[0][18] = Point(  5*WINDOW_WIDTH/16, 13*WINDOW_WIDTH/16 );
        rookPoints[0][19] = Point(    WINDOW_WIDTH/4,  13*WINDOW_WIDTH/16 );
		
		const Point* ppt[1] = {rookPoints[0]};
		int npt[] = {20};
		fillPoly(image, ppt, npt, 1, Scalar(255, 255, 255), lineType);

}

void DrawLine(Mat image, Point start, Point end)
{
	int thickness = 1;
	int lineType = 8;
	line(image, start, end, thickness, lineType);
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.6)
find_package(OpenCV 3.4 REQUIRED)
include_directories(${OpenCV_INCLUDE_DIRS})
add_executable(open  1.cpp)
target_link_libraries(open ${OpenCV_LIBS})

运行结果:
OpenCV数据结构和基本绘图—图像容器Mat, 数据结构,基本图像操作(5)_第4张图片

你可能感兴趣的:(opencv学习,opencv)