《Opencv3编程入门》学习笔记—第四章

news/2024/7/21 5:54:27 标签: opencv, 计算机视觉, 图像处理

《Opencv3编程入门》学习笔记

记录一下在学习《Opencv3编程入门》这本书时遇到的问题或重要的知识点。

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

四、基础图像容器Mat

(一)数字图像存储概述

图像在数码设备中的表现形式为包含众多强度值的像素点矩阵。

(二)Mat结构的使用

  • Mat是一个类,由两个数据部分组成:矩阵头(包含矩阵尺寸、存储方法、存储地址等信息)和一个指向存储所有像素值的矩阵(根据所选存储方法的不同,矩阵可以是不同的维数)的指针。
  • Opencv使用了引用计数机制。通过拷贝构造函数只复制信息头和矩阵指针,矩阵指针指向矩阵地址。
  • opencv函数中输出图像的内存分配是自动完成的,且不需要考虑内存释放问题。
  • 一般不复制矩阵本身,如何非要复制,可以使用函数clone()或者copyTo()来复制一幅图像的矩阵。
Mat F = A.clone();
Mat G;
A.copyTo(G);
  • 一个很棒的功能:可以创建只引用部分数据的信息头
imageROI=image(Rect(500,250,logo.cols,logo.rows));  //使用矩阵界定
imageROI=image(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));  //使用行和列来界定

(三)像素值的存储方法

颜色系统有很多,具体如下:

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

(四)显式创建Mat对象的七种方法

【方法一】使用Mat()构造函数

针对二维多通道图像

Mat M(2,2,CV_8UC3,Scalar(0,0,255));
cout << "M = " << endl << " " << M << endl << endl;

在这里插入图片描述
1、参数1:行数
2、参数2:列数
3、参数3:存储元素的数据类型以及每个矩阵点的通道数

CV_[位数][带符号与否][类型前缀]C[通道数]
CV_8UC3:表示使用8位的unsigned char型,每个像素由三个元素组成三通道

4、参数4:Scalar:short型的向量,能使用指定的定制化值来初始化矩阵,它还可以用于表示颜色。

【方法二】在C\C++中通过构造函数进行初始化

创建超过二维的矩阵

int sz[3] = {2,2,2}  
Mat L(3,sz,CV_8UC,Scalar::all(0));

1、参数1:指定维数
2、参数2:指向一个数组的指针,这个数组包含每个维度的尺寸
3、其他参数与上面一致

【方法三】为已存在的IpIImage指针创建信息头

IpIImage已经被废弃了

【方法四】利用Create()函数

此创建方法不能为矩阵设初值,只是在改变尺寸时重新为矩阵数据开辟内存而已。

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

在这里插入图片描述

【方法五】采用Matlab式的初始化方式
Mat E = Mat::eye(4, 4, CV_64F);
cout << "E = " << endl << " " << E << endl << endl;

Mat O = Mat::ones(2, 2, CV_32F);
cout << "O = " << endl << " " << O << endl << endl;

Mat Z = Mat::zeros(3, 3, CV_8UC1);
cout << "Z = " << endl << " " << Z << endl << endl;

在这里插入图片描述

【方法六】对小矩阵使用都好分隔式初始化函数
Mat C = (Mat_<double>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
cout << "C = " << endl << " " << C << endl << endl;
【方法七】为已存在的对象创建新信息头
Mat RowClone = C.row(1).clone();
cout << "RowClone = " << endl << " " << RowClone << endl << endl;

(五)OpenCV中的格式化输出方法

r矩阵的定义和初始化

Mat r = Mat(10,3,CV_8UC3);
//randu:产生随机值来填充矩阵,需要给定一个上限和下限来确保随机值在期望的范围内。
randu(r,Scalar::all(0),Scalar::all(255));
【风格一】OpenCV默认风格
cout << "r (OpenCV默认风格) = " << r << ";" << endl << endl;

在这里插入图片描述

【风格二】Python风格
//opencv2
cout << "r (Python风格) = " << format(r,"python") << ";" << endl << endl;
//opencv3
cout << "r (Python风格) = " << format(r,Formatter::FMT_PYTHON) << ";" << endl << endl;

在这里插入图片描述

【风格三】逗号分隔风格
//opencv2
cout << "r (逗号分隔风格) = " << format(r,"csv") << ":" << endl << endl;
//opencv3
cout << "r (逗号分隔风格) = " << format(r,"Formatter::FMT_CSV") << ":" << endl << endl;

在这里插入图片描述

【风格四】Numpy风格
//opencv2
cout << "r (Numpy风格) = " << format(r,"numpy") << ":" << endl << endl;
//opencv3
cout << "r (Numpy风格) = " << format(r,"Formatter::FMT_NUMPY") << ":" << endl << endl;

在这里插入图片描述

【风格五】C语言风格
//opencv2
cout << "r (Numpy风格) = " << format(r,"C") << ":" << endl << endl;
//opencv3
cout << "r (Numpy风格) = " << format(r,"Formatter::FMT_C") << ":" << endl << endl;

在这里插入图片描述

(六)输出其他常用数据结构

使用运算符"<<"来打印其他常用的OpenCV数据结构。

1、定义和输出二维点

二维点:point2f(x,y)中的x代表在图像中的列,y代表图像中的行

Point2f p(6,2);
cout << "【二维点】p = " << p << ";\n" <<endl;

在这里插入图片描述

2、定义和输出三维点

二维点:point3f(x,y,z)

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> points(20);
//size_t类型表示C中任何对象所能达到的最大长度,它是无符号整数
for(size_t i = 0;i<points.size();++i)
	points[i] = Point2f((float)(i*5),(float)(i%7));
cout << "【二维点向量】points = " <<points<< ";";

在这里插入图片描述

(七)示例程序:基础图像容器Mat类的使用

书本配套示例程序包中有一个短小的示例程序。

二、常用数据结构和函数

(一)点的表示:point类

Point类数据结构表示了二维坐标系下的点。

Point point;
point.x = 10;
point.y = 8;

Point point = Point(10,8);

(二)颜色的标识:Scalar类

Scalar()表示具有4个元素的数组,在OpenCV中被大量用于传递像素值,如RGB颜色值。第四个参数可不写。

Scalar(a,b,c)

定义的RGB颜色值:红色分量c,绿色分量c,蓝色分量c

(三)尺寸的表示:Size类

Size(5,5);//构造出的Size宽度和高度都为5,即XXX.width和XXX.height都为5

(四)矩形的表示: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;

(五)颜色空间转换:cvtColor()函数

cvtColor()函数是OpenCV里的颜色空间转换函数,可以实现RGB颜色像HSV、HSI等颜色空间的转换,也可以转换为灰度图像。
void cvtColor(InputArray src,OutputArray dst,int code,int dstCn=0)
1、参数1:输入图像
2、参数2:输出图像
3、参数3:颜色空间转换的标识符
4、参数4:目标图像的通道数。若该参数是0,表示目标图像取源图像的通道数。

//opencv2
cvtColor(srcImage,dstImage,CV_GRAY2BGR);  //转换原始图为灰度图
//opencv3
cvtColor(srcImage,dstImage,COLOR_GRAY2BGR);  //转换原始图为灰度图

标识符列举:

在这里插入图片描述
在这里插入图片描述
颜色空间转换的最简化版代码:

#include <opencv2\imgproc\imgproc.hpp>
#include <opencv2\highgui\highgui.hpp>
using namespace cv;

void main(){
	//1、载入图片
	Mat srcImage = imread("D://lili/Desktop/jpg/opencv/1.jpg",1),dstImage;
	//2、转换颜色空间
	cvtColor(srcImage,dstImage,COLOR_BGR2Lab);
	//3、显示效果图
	imshow("效果图",dstImage);
	//4、保持窗口显示
	waitKey();
}

在这里插入图片描述

(六)其他常用的知识点

  • Matx是个轻量级的Mat,必须在使用前规定好大小,比如一个2*3的float型的Matx,可以声明为Matx23f。
  • Vec是Matx的一个派生类,是一个一维的Matx,跟vector很相似。在OpenCV源码中有如下定义。
template<typename _Tpint n> class vec:public Matx<_Tpn1>{…);
typedef Vec<uchar, 2> Vec2b;
  • Range类其实就是为了使OpenCV的使用更像MATLAB而产生的。比如Range:all()其实就是MATLAB里的符号。而Range(a,b)其实就是MATLAB中的a:b,注意这里的a和b都应为整型。
  • OpenCV中防止内存溢出的函数有alignPtr、alignSize、allocate、deallocate、fastMalloc、fastFree等。
  • <math.h>里的一些函数使用起来很方便,有计算向量角度的函数fastAtan2、计算立方根的函数cubeRoot、向上取整函数cvCeil、向下取整函数cvFloor、四舍五入函数cVRound等。还有一些类似MATLAB里面的函数,比如cvlslnf判断自变量是否无穷大,cvlsNaN判断自变量是否不是一个数。
  • 显示文字相关的函数有 getTextSize、cvInitFont、putText。
  • 作图相关的函数有 circle、clipLine、ellipse、ellipse2Poly、line、rectangle、polylines、类Linelterator。
  • 填充相关的函数有fillConvexPoly、fillPoly。
  • OpenCV中RNG()函数的作用为初始化随机数状态的生成器

其他数据结构相关的知识由于使用较少,在需要用到的时候可以配合OpenCV文档,并查阅OpenCV的源码进行学习。

三、基本图形的绘制

绘制函数

  • 用于绘制直线的line函数;
  • 用于绘制椭圆的ellipse函数;
  • 用于绘制矩形的rectangle函数;

void rectangle(InputOutputArray img, Point pt1, Point pt2, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0);
第一个参数:表示要绘制矩形的图像。
第二个参数:表示矩形左上角顶点。
第三个参数:表示矩形右下角顶点。
第四个参数:表示矩形的颜色。
第五个参数:表示线条的粗细程度。 FILLED = -1,  //取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形
第六个参数:表示线条的类型。其中: LINE_4 = 4, LINE_8 = 8, LINE_AA = 16,

  • 用于绘制圆的circle函数;
  • 用于绘制填充的多边形的fillPoly函数。

示例程序:绘制一幅化学原子示例图和一幅组合图

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
 
using namespace cv;
using namespace std;
 
#define WINDOW_WIDTH 600 // 定义窗口大小的宏
#define WINDOW_NAME1 "原子图"        //为窗口标题定义的宏 
#define WINDOW_NAME2 "多边形图以及画线"        //为窗口标题定义的宏 
 
/*绘制椭圆
	void ellipse( CvArr* img, CvPoint center, CvSize axes, double angle, double start_angle, double end_angle, CvScalar color, int thickness=1, int line_type=8, int shift=0 );
	第一个参数:表示要绘制椭圆的图像。
	第二个参数:表示椭圆圆心坐标。(Point)
	第三个参数:表示轴的长度。(Size)
	第四个参数:表示偏转的角度。
	第五个参数:表示圆弧起始角的角度。.
	第六个参数:表示圆弧终结角的角度。
	第七个参数:表示线条的颜色。(Scalar)
	第八个参数:表示线条的粗细程度。
	第九个参数:表示线条的类型。
	第十个参数:表示圆心坐标点和数轴的精度。
*/
void DrawEllipse(Mat img, double angle)
{
    int thickness = 2; //  线宽
    int lineType = 8; // 线性,8 代表联通线性
 
    ellipse(img,
        Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),
        Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16),
        angle, // 角度,0~360 度
        0,
        360,
        Scalar(255, 129, 0),
        thickness,
        lineType);
}
 
/*绘制实心圆
	void circle(InputOutputArray img, Point center, int radius, const Scalar& color, int thickness = 1, int lineType = LINE_8, int shift = 0);
	第一个参数:表示要绘制圆的图像。
	第二个参数:表示圆心坐标。
	第三个参数:表示圆的半径。
	第四个参数:表示线条的颜色。
	第五个参数:表示线条的粗细程度。
	第六个参数:表示线条的类型。
	第七个参数:表示圆心坐标点和数轴的精度。
*/
void DrawFilledCircle(Mat img, Point center)
{
    int thickness = -1; //  线粗-1,代表实心
    int lineType = 8; // 线性,8 代表联通线性
 
    circle(img,
        center,
        WINDOW_WIDTH / 32,
        Scalar(0, 0, 255),
        thickness,
        lineType);
}
 
/*凹多边形绘制
	void fillPoly(Mat& img, const Point** pts, const int* npts, int ncontours, const Scalar& color, int lineType=8, int shift=0, Point offset=Point());
	第一个参数:表示要绘制填充多边形的图像。
	第二个参数:表示指向多边形的指针数组。(const)
	第三个参数:表示多边形的顶点个数。(数组)
	第四个参数:表示多边形的个数。
	第五个参数:表示填充的颜色。
	第六个参数:表示线条的粗细程度。
	第七个参数:顶点坐标的小数点位数。
*/
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 / 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(img, 
        ppt, 
        npt, 
        1, 
        Scalar(255, 255, 255),  // 多边形颜色:白色
        lineType);
}
 
/*线绘制
	void line(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0); 
	第一个参数:表示要绘制线段的图像。
	第二个参数:表示线段的起点。 
	第三个参数:表示线段的终点。 
	第四个参数:表示线段的颜色。 
	第五个参数:表示线段的粗细。
	第六个参数:表示线段的类型。可以取值8,4,和CV_AA,分别代表8邻接连接线,4邻接连接线和反锯齿连接线。默认值为8。
	第七个参数:表示坐标点小数点位数。
*/
void DrawLine(Mat img, Point start, Point end)
{
    int thickness = 2;
    int lineType = 8;
    line(img,
        start,
        end,
        Scalar(0,0,0), // 黑色
        thickness,
        lin	eType);
}
 
 
int main(int argc, char ** argv)
{
    Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
    Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
 
    //---------1、绘制化学中的原子示例图--------------
    // 椭圆绘制
    DrawEllipse(atomImage, 0);
    DrawEllipse(atomImage, 90);
    DrawEllipse(atomImage, 45);
    DrawEllipse(atomImage, -45);
    // 绘制圆心
    DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));
 
    //---------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));
    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));
 
    // ---------------------------<3>显示绘制出的图像------------------------
    imshow(WINDOW_NAME1, atomImage);
    moveWindow(WINDOW_NAME1, 0, 200);
    imshow(WINDOW_NAME2, rookImage);
    moveWindow(WINDOW_NAME2, WINDOW_WIDTH, 200);
 
    waitKey(0);
    return(0);
}

运行效果
在这里插入图片描述


http://www.niftyadmin.cn/n/378562.html

相关文章

Springboot异步执行

异步执行 1.基于Async注解的方式在异步的方法上加 Async注解&#xff0c;调用接口后基于Async注解的方式优缺点: 2.使用 CompletableFuture 实现异步任务在实现类中创建CompletableFuture 类型的方法优缺点: 3.使用 TaskExecutor 实现异步任务优缺点: 1.基于Async注解的方式 As…

基于车站约束的地铁系统协调客流控制模型与算法

1 文章信息 《Model and algorithm of coordinated flow controlling with station-based constraints in a metro system》是2021年发表在Transportation Research Part E上的一篇文章。 2 摘要 随着城市人口的增长和交通需求的快速增长&#xff0c;世界上许多大城市的地铁系统…

《计算机组成原理》唐朔飞 第7章 指令系统 - 学习笔记

写在前面的话&#xff1a;此系列文章为笔者学习计算机组成原理时的个人笔记&#xff0c;分享出来与大家学习交流。使用教材为唐朔飞第3版&#xff0c;笔记目录大体与教材相同。 网课 计算机组成原理&#xff08;哈工大刘宏伟&#xff09;135讲&#xff08;全&#xff09;高清_…

数据分析面试指南,知道以下几点成功率提升50%

目前&#xff0c;数据分析行业大火&#xff0c;相信很多小伙伴都想去这一行业试试水。想要成功进入数据分析行业&#xff0c;就必须得通过数据分析面试&#xff0c;面试应该准备什么&#xff0c;怎么准备&#xff0c;各位小伙伴都知道吗? 简历 大家都知道面试一定要带简历&a…

【SCI邀稿】中科院2区,影响因子即将突破8分,对国人友好

信号处理类SCI&EI ✔ 自引率&#xff1a;1.30% ✔ 国人占比&#xff1a;对国人友好 ✔ 年发文量&#xff1a;50篇左右 ✔ 期刊简介&#xff1a;IF:7.5-8.0&#xff0c;JCR1区&#xff0c;中科院2区 ✔ 检索情况&#xff1a;SCIE&EI 双检&#xff0c;正刊 ✔ 截稿…

冷气机、空调扇、饮水机、液晶驱动VK0256C LQFP52段码LCD液晶显示驱动芯片技术资料

品牌&#xff1a;永嘉微电/VINKA 型号&#xff1a;VK0256C 封装形式&#xff1a;LQFP52 年份&#xff1a;新年份 KPP2649 概述: VK0256C是一个点阵式存储映射的LCD驱动器&#xff0c;可支持最大256点&#xff08;32EGx8COM&#xff09;的LCD屏。单片机可通过3/4线串行接口配…

Spark SQL概述、数据帧与数据集

文章目录 一、准备工作1、准备数据文件2、启动Spark Shell 二、加载数据为Dataset1、读文件得数据集 三、给数据集添加元数据信息1、定义学生样例类2、导入隐式转换3、将数据集转换成学生数据集4、对学生数据集进行操作&#xff08;1&#xff09;显示数据集内容&#xff08;2&a…

ChatGPT赋能Scrum实践

对于Scrum实践者来说&#xff0c;以ChatGPT为代表的的大语言模型是很有效的工具&#xff0c;但要用好这个工具需要一些技巧&#xff0c;本文介绍了60个适用于Scrum实践的提示&#xff0c;可以帮助ChatGPT有效输出适用于Scrum实践的内容。原文: 60 ChatGPT Prompts Plus Prompt …