《OpenCV3编程入门》学习笔记4 OpenCV数据结构与基本绘图

本文深入探讨了OpenCV中图像处理的基本概念,包括Mat数据结构的详细解析,图像的存储和处理方法,以及如何使用OpenCV进行基本图形绘制。文章还介绍了颜色空间的概念和转换方法,为读者提供了丰富的实践指导。

第4章 OpenCV数据结构与基本绘图

4.1 基础图像容器Mat

4.1.1 数字图像存储概述

  图像在数码设备中的表现形式:像素点矩阵

4.1.2 Mat结构的使用

1.OpenCV1.x时代
  基于C语言接口而建的图像存储格式IplImage*
  缺点:退出时忘记relese掉的话,会造成内存泄漏,手动释放内存耗费时间
2.C++出现后
  自动的内存管理
  不足:许多嵌入式开发系统只支持C语言
3.OpenCV2.0时代
  Mat类数据结构
  优点:不必手动开辟空间、不必再在不需要时立即释放空间
4.Mat类数据部分组成
(1)矩阵头:矩阵尺寸、存储方法、存储地址等
(2)指向存储所有像素值的矩阵的指针
5.计数机制
  矩阵本身尺寸数量级很大,传递图像时造成比较大的开销,为解决此问题,引入计数机制。
  计数机制:让每个Mat对象有自己的信息头,但共享同一个矩阵。通过让矩阵指针指向同一个地址而实现,拷贝构造函数则只复制信息头和矩阵指针,而不复制矩阵。这时,通过任何一个对象所做的改变会影响其他图像。
(1)创建信息头:

Mat A,C; //仅创建信息头
Mat D(A,Rect(10,10,100,100)); //使用矩形界定信息头
Mat E=A(Range:all(),Range(1,3)); //用行和列界定信息头

(2)矩阵清理:复制一个信息头会增加矩阵的引用次数,反之,当一个头被释放后,计数会减一,当计数值为0,矩阵会被清理。
(3)复制矩阵本身函数:clone()或copyTo(),改变一个对象不会影响其他对象,例如:

Mat F=A.clone();
Mat G;
A.copyTo(G);
4.1.3 像素值的存储方法

1.存储像素值需要指定颜色空间和数据类型,颜色空间
(1)灰度级空间:黑白组合
(2)其他颜色空间:
  1)RGB颜色空间最常用;
  2)HSV和HLS把颜色分解成色调、饱和度和亮度/明度,抛弃最后一个元素算法对输入图像的光照条件不敏感
  3)YCrCb在JPEG图像格式中广泛使用
  4)CIE L*a*b*是一种在感知上均匀的颜色空间,它适合用来度量两个颜色之间的距离
2.每个组成元素都有自己的定义域,定义域取决于其数据类型
(1)char:最小数据类型,1字节(8位),RGB颜色空间使用三个char,可表示1600万种
(2)有符号型:0到255
(3)无符号型:-127到+127
(4)float或double:能给出更加精细的颜色分辨能力

4.1.4 显式创建Mat对象的7种方法

  (Mat不但是图像容器类,也是通用矩阵类,可以用来创建多维矩阵)

1.Mat()构造函数
  二维多通道图像定义:行数,列数,存储元素数据类型和矩阵点通道数(CV_[位数][带符号与否][类型前缀]C[通道数]),定制化值初始化矩阵,如:

Mat M(2,2,CV_8UC3,Scalar(0,0,255));//CV_8UC3:8位的unsigned char,每个像素由3个元素组成三通道
cout<<”M=<<endl<<” “<<M<<endl<<endl;

2.C\C++中通过构造函数进行初始化

	int sz[3] = { 2,2,2 };
	Mat L(3, sz, CV_8UC3, Scalar::all(0));//创建超过2维的矩阵,指定维度,传递一个指向一个数组的指针,数组包含每个维度的尺寸

3.唯一存在的IplImage指针创建信息头,转换IplImage*->Mat

	//IplImage* img = cvLoadImage("1.jpg", 1);
	//Mat mtx(img); //报错,"Mat没有匹配IplImage*参数的构造函数"

4.利用Create()函数,此方法不能为矩阵设初值,只是在改变尺寸时重新为矩阵数据开辟内存

    M.create(4, 4, CV_8UC(2));
	cout << "M= " << endl << " " << M << endl << endl;

5.采用Matlab式的初始化方式

	Mat E = Mat::eye(4, 4, CV_64F);//单位矩阵
	cout << "E= " << endl << " " << E << endl << endl;

	Mat O = Mat::ones(2, 2, CV_32F);//全1矩阵
	cout << "O= " << endl << " " << O << endl << endl;

	Mat Z = Mat::zeros(3, 3, CV_8UC1);//全0矩阵
	cout << "Z= " << endl << " " << Z << endl << endl;

6.对小矩阵使用逗号分隔式初始化函数

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

7.为已存在的对象创建新信息头

	Mat RowClone = C.row(1).clone();
	cout << "RowClone= " << endl << " " << RowClone << endl << endl;
4.1.5 OpenCV中格式化输出方法

  r矩阵定义:通过randu()函数产生的随机值来填充矩阵,需要给定一个上限和下限确保随机值在期望的范围内。

Mat r=Mat(10,3,CV_8UC3);
randu(r,Scalar::all(0),Scalar::all(255));
//1.OpenCV默认风格
	cout << "r (OpenCV默认风格)= " << r << ";" << endl << endl;
//2.Python风格
	cout << "r (Python风格)= " << format(r, Formatter::FMT_PYTHON) << ";" << endl << endl;
//3.逗号分隔风格
	cout << "r (逗号分隔风格)= " << format(r, Formatter::FMT_CSV) << ";" << endl << endl;
//4.Numpy风格
	cout << "r (Numpy风格)= " << format(r, Formatter::FMT_NUMPY) << ";" << endl << endl;
//5.C语言风格
	cout << "r (C语言风格)= " << format(r, Formatter::FMT_C) << ";" << endl << endl;
4.1.6 输出其他常用数据结构

1.二维点

	Point2f p2f(6, 2);
	cout << "[二维点]p2f = " << p2f << ";\n" << endl;

2.三维点

	Point3f p3f(8, 2, 0);
	cout << "[二维点]p3f = " << p3f << ";\n" << endl;

3.基于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;

4.std::vector点,定义和输出存放点的vector容器,以存放二维点Point2f为例

	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 << ";";

4.2 常用数据结构和函数

4.2.1 点的表示:Point类(OpenCV中Point_<int>、Point2i、Point等价,Point_<float>、Point2f等价)

	Point point;
	point.x = 10;
	point.y = 8;
	//Point point = Point(10, 8);

4.2.2 颜色的表示:Scalar类

  Scalar(a, b, c); 红c,绿b,蓝a

4.2.3 尺寸的表示:Size类(OpenCV中Size_、Size2i、Size等价)

	Size(5, 5);

4.2.4 矩形的表示:Rect类
(1)成员变量:x,y,width,height
(2)成员函数:Size(),area(),contains(Point),inside(Rect),tl()返回左上角点坐标,br()返回右下角点坐标
(3)求两矩形交并集 Rect rect = rect1 & rect2; Rect rext = rext1 | rext2;
(4)矩形平移和缩放 Rect rextShift = rect + point; Rect rectScale = rect + size;

4.2.5 颜色空间转换:cvtColor()函数
(1)函数原型:void cvtColor(InputArray src,OutputArray dst,int code,int dstCn=0)
(2)参数说明:输入图像,输出图像,颜色空间转换标识符,目标图像通道数
(3)示例: cvtColor(srcImage, dstImage, COLOR_GRAY2BGR);
        在这里插入图片描述
4.2.6 Core模块中的常用函数
1.<math.h>中,计算向量角度fastAtan2、计算立方根cubeRoot、向上取整cvCeil、向下取整cvFloor、四舍五入cvRound、判断自变量是否无穷大cvIsInf、判断自变量是否不是一个数cvIsNaN
2.显示文字相关:getTextSize、cvInitFont、putText
3.作图相关:circle、clipLine、ellipse、ellipse2Poly、line、rectangle、polylines、LineIterator
4.填充相关:fillConvexPoly、fillPoly

4.3 基本图形绘制

  涉及绘制函数:line:直线;ellipse:椭圆;rectangle:矩形;circle:圆形;fillPoly:填充的多边形

#include<opencv2/opencv.hpp>
#include<opencv2/highgui.hpp>
#include<opencv2/imgproc/imgproc.hpp>
#define WINDOW_NAME1 "[绘制图1]"
#define WINDOW_NAME2 "[绘制图2]"
#define WINDOW_WIDTH 600      //定义窗口大小的宏
using namespace cv;
//4.3.1 DrawEllipse()函数
//自定义的绘制函数,实现绘制不同角度、相同尺寸的椭圆
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);
}

//4.3.2 DrawFilledCircle()函数
//自定义的绘制函数,实现实心圆的绘制
void DrawFilledCircle(Mat img, Point center)
{
	int thickness = -1;
	int lineType = 8;
	circle(img, center, WINDOW_WIDTH / 32, Scalar(0, 0, 255), thickness, lineType);//图像,圆心,半径,颜色,线粗,线形
}
//4.3.3 DrawPolygon()函数
//自定义的绘制函数,实现凹多边形的绘制
void DrawPolygon(Mat img)
{
	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 / 4, WINDOW_WIDTH / 8);
	rookPoints[0][8] = Point(26 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 4);
	rookPoints[0][9] = Point(22 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 4);
	rookPoints[0][10] = Point(22 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][11] = Point(18 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][12] = Point(18 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 4);
	rookPoints[0][13] = Point(14 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 4);
	rookPoints[0][14] = Point(14 * WINDOW_WIDTH / 4, 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 / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][18] = Point(5 * WINDOW_WIDTH / 4, 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(img, ppt, npt, 1, Scalar(255, 255, 255), lineType);//图像,顶点集,顶点数目,要绘制的多边形数量,颜色,线形
}
//4.3.4 DrawLine()函数
//自定义绘制函数,实现线的绘制
void DrawLine(Mat img, Point start, Point end)
{
	int thickness = 2;
	int lineType = 8;
	line(img, start, end, Scalar(0, 0, 0), thickness, lineType);
}
int main()
{
	//创建空白的Mat图像
	Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
	Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);

	//-------------------<1>绘制化学中的原子示例图--------------------------
	//1.绘制椭圆
	DrawEllipse(atomImage, 90);
	DrawEllipse(atomImage, 0);
	DrawEllipse(atomImage, 45);
	DrawEllipse(atomImage, -45);
	//2.绘制圆心
	DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));
	
	//-------------------------<2>绘制组合图---------------------------
	//1.绘制椭圆
	DrawPolygon(rookImage);
	//2.绘制矩形
	rectangle(rookImage, Point(0, 7 * WINDOW_WIDTH / 8), Point(WINDOW_WIDTH, WINDOW_WIDTH), Scalar(0, 255, 255), -1, 8);
	//3.绘制一些线段
	DrawLine(rookImage, Point(0, 15 * WINDOW_WIDTH / 16), Point(WINDOW_WIDTH, 15 * WINDOW_WIDTH / 16));
	DrawLine(rookImage, Point(WINDOW_WIDTH/4, 7 * WINDOW_WIDTH / 8), Point(WINDOW_WIDTH/4, WINDOW_WIDTH));
	DrawLine(rookImage, Point(WINDOW_WIDTH / 2,7 * WINDOW_WIDTH/8), Point(WINDOW_WIDTH/2, WINDOW_WIDTH));
	DrawLine(rookImage, Point(3 * WINDOW_WIDTH/4, 7 * WINDOW_WIDTH / 8), Point(3 * WINDOW_WIDTH/4, WINDOW_WIDTH));

	//显示绘制图像
	imshow(WINDOW_NAME1, atomImage);
	cvMoveWindow(WINDOW_NAME1, 0, 200);
	imshow(WINDOW_NAME2, rookImage);
	cvMoveWindow(WINDOW_NAME2, WINDOW_WIDTH, 200);

	waitKey(0);
	return 0;
}

4.4 颜色空间拓展

  图像彩色模型包括RGB模型、CMY模型、HSI模型、YIQ模型、YUV模型、YCbCr模型等

4.4.1 RGB彩色模型

  RGB彩色模型基于笛卡尔坐标系统,红、绿、蓝、青、深红、黄、黑和白分别位于立方体各个顶点,灰度级沿黑和白两点的连线分布。不同的颜色可以用从原点分布的向量来定义。归一化彩色子空间立方体如图所示:
                在这里插入图片描述

4.4.2 HSI彩色模型

  用色调、饱和度和强度描述彩色图像。饱和度是纯彩色被白光冲淡程度的度量,色调是用来描述纯色的属性,强度(灰度)是单色图像的描述算子。该模型可从彩色信息中消除强度分量的影响,更符合人类的视觉特性,是一种理想的彩色图像处理工具。

4.4.3 RGB与HSI彩色模型之间的转换

(1)RGB转换为HSI

  色调分量转换公式:
                  在这里插入图片描述
  其中,
              在这里插入图片描述
  饱和度分量转换公式:
                  在这里插入图片描述
  亮度分量转换公式:
                    在这里插入图片描述

(2)HSI转换为RGB

  RG区(在这里插入图片描述):若H在这个区域内,RGB分量公式为:
                   在这里插入图片描述
  GB区(在这里插入图片描述):若H在这个区域内,RGB分量公式为:
                   在这里插入图片描述
  BR区(在这里插入图片描述):若H在这个区域内,RGB分量公式为:
                   在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值