首页 >> 大全

OpenCV入门之基本知识

2023-11-21 大全 38 作者:考证青年

一、文件配置

在中需要配置如下三个文件:.json、.json、tasks.json。

1..json

配置文件的关键是包含文件的路径,设置参数:设置后生成的目录下包含文件的目录和并设置gcc编译器路径。

{"configurations": [{"name": "windows-gcc-x64","includePath": ["${workspaceFolder}/**","D:/Data/OpenCV/install/include",  //安装目录的头文件"D:/Data/OpenCV/install/include/opencv2"],"defines": ["_DEBUG","UNICODE","_UNICODE"],"compilerPath": "D:/Data/x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin/gcc.exe", //mingw64下的gcc编译器"cStandard": "c11","cppStandard": "c++17","intelliSenseMode": "windows-gcc-x64"}],"version": 4
}

2..json

配置:配置gdb路径

{"version": "0.2.0","configurations": [{"name": "(gdb) Launch","type": "cppdbg","request": "launch","program": "${fileDirname}/${fileBasenameNoExtension}.exe","args": [],"stopAtEntry": false,"cwd": "${workspaceFolder}","environment": [],"externalConsole": true,"internalConsoleOptions": "neverOpen","MIMode": "gdb","miDebuggerPath": "D:/Data/x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin/gdb.exe","setupCommands": [{"description": "Enable pretty-printing for gdb","text": "-enable-pretty-printing","ignoreFailures": false}],"preLaunchTask": "Compile"}]
}

3.tasks.json

配置参数:g++编译器;配置args:保护头文件路径和库文件路径。也可以根据自己的需要添加合适的库。

// https://code.visualstudio.com/docs/editor/tasks
{"version": "2.0.0","tasks": [{"label": "Compile", // 任务名称,与launch.json的preLaunchTask相对应"command": "D:/Data/x86_64-8.1.0-release-posix-seh-rt_v6-rev0/mingw64/bin/g++.exe",   // 要使用的编译器,C++用g++"args": ["${file}",//当前文件名"-o",    // 指定输出文件名,不加该参数则默认输出a.exe,Linux下默认a.out"${fileDirname}/${fileBasenameNoExtension}.exe","-g",    // 生成和调试有关的信息"-m64", // 不知为何有时会生成16位应用而无法运行,加上此条可强制生成64位的"-Wall", // 开启额外警告"-static-libgcc",     // 静态链接libgcc,一般都会加上"-finput-charset=UTF-8","-fexec-charset=GBK", // 生成的程序使用GBK编码,不加这条会导致Win下输出中文乱码;繁体系统改成BIG5"-std=c++17", // 要用的语言标准,根据自己的需要修改。c++可用c++14// 扩展参数// -I 头文件目录// -L 库文件目录// -l 库文件"-I", "D:/Data/OpenCV/install/include","-I", "D:/Data/OpenCV/install/include/opencv2","-L", "D:/Data/OpenCV/install/x64/mingw/lib/lib*",  // 引入全部的库文件], // 编译的命令,其实相当于VSC帮你在终端中输了这些东西"type": "process", // process是把预定义变量和转义解析后直接全部传给command;shell相当于先打开shell再输入命令,所以args还会经过shell再解析一遍"group": {"kind": "build","isDefault": true // 不为true时ctrl shift B就要手动选择了},"presentation": {"echo": true,"reveal": "always", // 执行任务时是否跳转到终端面板,可以为always,silent,never。具体参见VSC的文档"focus": false,     // 设为true后可以使执行task时焦点聚集在终端,但对编译C/C++来说,设为true没有意义"panel": "shared"   // 不同的文件的编译信息共享一个终端面板},"problemMatcher":"$gcc" // 捕捉编译时终端里的报错信息到问题面板中,修改代码后需要重新编译才会再次触发// 本来有Lint,再开problemMatcher就有双重报错,但MinGW的Lint效果实在太差了;用Clang可以注释掉}]
}

注意:运行程序按F5或者如下图的GDB

参考配置:

搭建的c++环境() 的博客-CSDN博客

Win10系统,编辑器下安装及配置下载安装的博客-CSDN博客

二、示例程序

示例程序位于下载的源码文件中的文件夹中。

在CPP文件夹下的.cpp拷贝到工程下。

在文件中其中help函数可以帮助我们快速了解该示例的用法以及如何运行。

三、Mat容器

图像是以离散的数字进行存储的,每一个数字代表一个像素。

Mat类是里用于存储矩阵数据的类型,与int、等相同。

Mat类用来保存矩阵类型的数据信息,包括向量、矩阵、灰度或彩色图像等数据。Mat类分为矩阵头和指向存储数据的矩阵指针两部分。

矩阵头中包含矩阵的尺寸、存储方法、地址和引用次数等。矩阵头的大小是一个常数,不会随着矩阵尺寸大小而改变。在绝大多数情况下矩阵头大小远小于矩阵中数据量的大小,因此图像复制和传递过程中主要的开销是存放矩阵数据。为了解决这个问题,在中复制和传递图像时,只是复制了矩阵头和指向存储数据的指针,因此在创建Mat类时可以先创建矩阵头后赋值数据。

opencv基础知识_opencv入门要学多久_

Mat类由矩阵头和数据构成,矩阵头包含理论图像的尺寸、行数、列数、数据类型、通道数和引用次数,数据则存储了图像的信息等。

//创建Mat类
cv::Mat a; //创建一个名为a的矩阵头
a = cv::imread(“test.jpg”); //向a中赋值图像数据,矩阵指针指向像素数据
cv::Mat b=a; //复制矩阵头,并命名为b

首先创建了一个名为a的矩阵头,之后读入一张图像并将a中的矩阵指针指向该图像的像素数据,最后将a矩阵头中的内容复制到b矩阵头中。虽然a、b有各自的矩阵头,但是其矩阵指针指向的是同一个矩阵数据,通过任意一个矩阵头修改矩阵中的数据,另一个矩阵头指向的数据也会跟着发生改变。

但是当删除a变量时,b变量并不会指向一个空数据,只有当两个变量都删除后,才会释放矩阵数据。因为矩阵头中引用次数标记了引用某个矩阵数据的次数,只有当矩阵数据引用次数为0的时候才会释放矩阵数据。

Mat类可以存储的数据类型包含、float、uchar、 char以及自定义的模板等。

cv::Mat_:自定义数据类型

cv::Mat_:

cv::Mat_:float

cv::Mat_:uchar

cv::Mat_: char

//声明一个指定类型的Mat类
cv::Mat A = Mat_(3,3);//创建一个3*3的矩阵用于存放double类型数据

由于提出Mat类主要用于存储图像,而像素值的最大值又决定了图像的质量,如果用8位无符号整数去存储16位图像,会造成严重的图像颜色失真或造成数据错误。而由于不同位数的编译器对数据长度定义不同,为了避免在不同环境下因变量位数长度不同而造成程序执行问题,根据数值变量存储位数长度定义了数据类型,中规定的数据类型如下:

仅有数据类型是不够的,还需要定义图像数据的通道()数,例如灰度图像数据是单通道数据,彩色图像数据是3通道或者4通道数据。因此针对这个情况,还定义了通道数标识,C1、C2、C3、C4分别表示单通道、双通道、3通道和4通道。每一种数据类型都存在多个通道的情况,所以将数据类型与通道数表示结合便得到了中对图像数据类型的完整定义,例如表示的就是8位单通道数据,用于表示8位灰度图,而表示的是8位3通道数据,用于表示8位彩色图。

//通过OpenCV数据类型创建Mat类
cv::Mat a(640,480,CV_8UC3) //创建一个640*480的3通道矩阵用于存放彩色图像
cv::Mat a(3,3,CV_8UC1) //创建一个3*3的8位无符号整数的单通道矩阵
cv::Mat a(3,3,CV_8U) //创建单通道矩阵C1标识可以省略

1.Mat类的创建

(1)利用默认构造函数

cv::Mat::Mat();

利用默认构造函数构造了一个Mat类,这种构造方式不需要输入任何的参数,在后续给变量赋值的时候会自动判断矩阵的类型与大小,实现灵活的存储,常用于存储读取的图像数据和某个函数运算输出结果。

(2)利用矩阵宽、高和类型参数创建Mat类

通道数设置:(n),其中的n表示为n通道矩阵,其中n最大可以取到512。

cv::Mat a(3,3,CV_8UC1);

通过输入矩阵的行、列以及存储数据类型实现构造。这种定义方式清晰、直观、易于阅读,常用在明确需要存储数据尺寸和数据类型的情况下,例如相机的内参矩阵、物体的旋转矩阵等。

(3)利用矩阵Size()结构和数据类型参数创建Mat类型

利用输入矩阵尺寸和数据类型构造Mat类的方法存在一种变形,通过将行和列组成一个Size()结构进行赋值。

//用Size()结构构造Mat示例cv::Mat a(Size(480, 640), CV_8UC1); //构造一个行为640,列为480的单通道矩阵cv::Mat b(Size(480, 640), CV_32FC3); //构造一个行为640,列为480的3通道矩

利用这种方式构造Mat类时要格外注意,在Size()结构里矩阵的行和列的顺序与上一种方法相反,使用Size()时,列在前、行在后。如果不注意同样会构造成功Mat类,但是当我们需要查看某个元素时,我们并不知道行与列颠倒,就会出现数组越界的错误。

(4)利用已有Mat类创建新的Mat类

可以从一个较大的Mat中截取一个较小的Mat,类似于抠图。但是,通过这种方式构造的Mat类与已有Mat类享有共同的数据,即如果两个Mat类中有一个数据发生更改,另一个也会随之更改

cv::Mat d = (cv::Mat_(1,5)<<1,2,3,4,5); 
cv::Mat e = cv::Mat::diag(d); 
cv::Mat f = cv::Mat(e,cv::Range(2,4),cv::Range(2,4));

d为第一个数组,e为第二个数组,f为第三个数组,是从第二个数组中截取的第二行到第四行,第2列到第4列。

2.Mat类的赋值

(1)创建时赋值

该种方式是在构造的同时进行赋值,将每个元素想要赋予的值放入结构中即可,这里需要注意的是,用此方法会将图像中的每个元素赋值相同的数值,例如(0, 0, 255)会将每个像素的三个通道值分别赋值0,0,255。我们可以使用如下的形式构造一个已赋值的Mat类。

cv::Mat a(2, 2, CV_8UC3, cv::Scalar(0,0,255));//创建一个3通道矩阵,每个像素都是0,0,255cv::Mat b(2, 2, CV_8UC2, cv::Scalar(0,255));//创建一个2通道矩阵,每个像素都是0,255cv::Mat c(2, 2, CV_8UC1, cv::Scalar(255)); //创建一个单通道矩阵,每个像素都是255

在程序语句之前加上断点进行调试,用Image Watch查看每一个Mat类变量里的数据,结果如图所示,证明我们已成功构造矩阵并赋值。

结构中变量的个数一定要与定义中的通道数相对应,如果结构中变量个数大于通道数,则位置大于通道数之后的数值将不会被读取,例如执行a(2, 2, , (0,0,255))后,每个像素值都将是(0,0),而255不会被读取。如果结构中变量数小于通道数,则会以0补充。

(2)类方法的赋值

以上四个方法是属于Mat类的类成员方法,直接调用即可。diag()需要输入一个矩阵,然后diag将这个拒绝变为对角阵。

cv::Mat d = (cv::Mat_(1,5),在定义时同样需要在括号中声明数据的变量类型。Mat类迭代器的起始是Mat.begin< >(),结束是Mat.end< >(),与其他迭代器用法相同,通过“++”运算实现指针位置向下迭代,数据的读取方式是先读取第一个元素的每一个通道,之后再读取第二个元素的每一个通道,直到最后一个元素的最后一个通道。

(4)通过矩阵元素地址定位方式访问元素

可以通过声明“第x行第x列第x通道”的方式来读取某个通道内的数据。

(int)(*(b.data + b.step[0] * row + b.step[1] * col + channel));

row变量的含义是某个数据所在元素的行数,col变量的含义是某个数据所在元素的列数,变量的含义是某个数据所在元素的通道数。这种方式与我们通过指针读取数据的形式类似,都是通过将首个数据的地址指针移动若干位后指向需要读取的数据,只不过这种方式可以通过直接给出行、列和通道数进行读取,不需要用户再进行计算某个数据在这行数据存储空间中的位置。

结束语

感谢你观看我的文章呐~本次航班到这里就结束啦

希望本篇文章有对你带来帮助 ,有学习到一点知识~

躲起来的星星也在努力发光,你也要努力加油(让我们一起努力叭)。

最后,博主要一下你们的三连呀(点赞、评论、收藏),不要钱的还是可以搞一搞的嘛~

不知道评论啥的,即使扣个666也是对博主的鼓舞吖 感谢

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了