【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步
文章目录
- 前言
- 3.1 图形的载入、显示和输出到文件
- 3.1.1 OpenCV的命名空间
- 3.1.2 Mat类简析
- 3.1.3 图像的载入与显示概述
- 3.1.4 图像的载入:imread()函数
- 3.1.5 图像的显示:imshow()函数
- 3.1.6 关于InputArray类型
- 3.1.7 创建窗口:namedWindow()函数
- 3.1.8 输出图像到文件:imwrite()函数
- 3.1.9 综合示例程序:图像的载入、显示与输出
- 3.2 滑动条的创建和使用
- 3.2.1 创建滑动条:createTrackbar()函数
- 3.2.2 获取当前轨迹条的位置:getTrackbarPos()函数
- 3.3 鼠标操作
- 3.4 本章小结
前言
笔记系列
参考书籍:OpenCV3编程入门
作者:毛星云
版权方:电子工业出版社
出版日期:2015-02
笔记仅供本人参考使用,不具备共通性
笔记中代码均是OpenCV+Qt的代码,并非用vs开发,请勿混淆
HighGUI模块为高层GUI图形用户界面模块
它的功能包括
- 媒体的输入输出
- 视频捕捉
- 图像和视频的编码解码
- 图形交互界面的接口
- …
3.1 图形的载入、显示和输出到文件
- OpenCV1.0时代的基于C语言接口而建的图像存储格式
IplImage*
十分不好用 - 进入到2.0时代后,OpenCV采用了Mat类作为数据结构进行图像存取
- 此改进使得OpenCV变得容易上手和用于实际开发
3.1.1 OpenCV的命名空间
OpenCV中的C++类和函数都是定义在命名空间cv之内的
我们有两种方法可以访问cv
- 其一:在代码开头的适当位置加上
using namespace cv;
这句代码以规定我们的程序位于词命名空间之内 - 其二:在使用OpenCV的每一个类和函数时,都在前面加上
cv::
- 相较而言,显然第一种方法更加方便,推荐使用
- 其一:在代码开头的适当位置加上
平常在写简单的OpenCV程序时,以下三句可以作为标配
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace cv;
3.1.2 Mat类简析
Mat类
是用于保存图像以及其他矩阵数据的数据结构
默认情况下Mat类的尺寸为0
- 可以用类似于
cv::Mat pic(320,640,cv::Scalar(100));
的代码来定义一个初始带尺寸的Mat类
- 可以用类似于
Mat最基本的用法如下
using namespace cv; Mat srcImage = imread("需要加载的图像路径");//路径可以是绝对路径,也可以是相对路径
上面的代码表示将一张图片载入到Mat类型的srcImage变量中
- (尽量是jpg或png格式的,其他格式我还没试过,不知道可不可行)
代码中的imread()函数,是用于将图片读入Mat类型中,后面会详细讲解
3.1.3 图像的载入与显示概述
图像载入用到的代码
imread()
图像显示用到的代码
imshow()
3.1.4 图像的载入:imread()函数
功能:用于读取文件中的图片到OpenCV中
函数的原型如下:
enum ImreadModes{IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image.}; //上面是截取的部分代码,下面是imread()函数的声明 Mat imread( const String& filename, int flags = IMREAD_COLOR ); //参数一:需要载入的图片路径名 参数二:载入标识符
第一个参数:const String& filename ---- 我们需要载入的图片路径名
在Windows操作系统下,OpenCV的imread函数支持如下类型的图像载入
图片类型 对应的文件后缀名 Windows位图 *.bmp, *.dib JPEG文件 *.jpeg, *.jpg, *.jpe JPEG 2000文件 *.jp2 PNG图片 *.png 便携文件格式 *.pbm, *.pgm, *.ppm Sun rasters光栅文件 *.sr, *.ras TIFF文件 *.tiff *.tif
第二个参数:int flags = IMREAD_COLOR
载入标识符,它指定一个加载图像的颜色类型和图片深度
flags默认值为IMREAD_COLOR,也就是1,所以有些时候这个参数在调用时可以忽略,此时表示载入三通道的彩色图图像
同时通过上面的代码展示可以看出来,此参数可以在OpenCV中标识图像格式的枚举体中取值
让我们转到定义,看看这个枚举体,如下
//! Imread flags enum ImreadModes {IMREAD_UNCHANGED = -1, //!< If set, return the loaded image as is (with alpha channel, otherwise it gets cropped). Ignore EXIF orientation.//如果设置,则按原样返回加载的图像(带有Alpha通道,否则将被裁剪)。 忽略EXIF方向IMREAD_GRAYSCALE = 0, //!< If set, always convert image to the single channel grayscale image (codec internal conversion).//如果设置,则始终将图像转换为单通道灰度图像(编解码器内部转换)。IMREAD_COLOR = 1, //!< If set, always convert image to the 3 channel BGR color image.//如果设置,则始终将图像转换为3通道BGR彩色图像。IMREAD_ANYDEPTH = 2, //!< If set, return 16-bit/32-bit image when the input has the corresponding depth, otherwise convert it to 8-bit.//如果设置,则当输入具有相应的深度时返回16位/ 32位图像,否则将其转换为8位。IMREAD_ANYCOLOR = 4, //!< If set, the image is read in any possible color format.//如果设置,将以任何可能的颜色格式读取图像。IMREAD_LOAD_GDAL = 8, //!< If set, use the gdal driver for loading the image.//如果已设置,则使用gdal驱动程序加载图像。IMREAD_REDUCED_GRAYSCALE_2 = 16, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/2.//如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸减小1/2。IMREAD_REDUCED_COLOR_2 = 17, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/2.//如果设置,则始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/2。IMREAD_REDUCED_GRAYSCALE_4 = 32, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/4.//如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸缩小1/4IMREAD_REDUCED_COLOR_4 = 33, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/4.//如果设置,则始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/4。IMREAD_REDUCED_GRAYSCALE_8 = 64, //!< If set, always convert image to the single channel grayscale image and the image size reduced 1/8.//如果设置,则始终将图像转换为单通道灰度图像,并且图像尺寸减小1/8。IMREAD_REDUCED_COLOR_8 = 65, //!< If set, always convert image to the 3 channel BGR color image and the image size reduced 1/8.//如果设置,则始终将图像转换为3通道BGR彩色图像,并且图像尺寸减小1/8。IMREAD_IGNORE_ORIENTATION = 128 //!< If set, do not rotate the image according to EXIF's orientation flag.//如果已设置,则不要根据EXIF的方向标志旋转图像。};
上述代码选择解释
枚举值 解释说明 IMREAD_UNCHANGED 等价取值为-1,该标识符在新版本中已被废置,忽略 IMREAD_GRAYSCALE 等价取值为0,如果取这个标识的话,会将载入的图像转换成灰度图在返回 IMREAD_COLOR 等价取值为1,若是取这个标识的话,会将图片转换成3通道BGR彩色图像并返回 IMREAD_ANYDEPTH 等价取值为2,若是取这个标识的话,会先检查图像的深度,若图像深度为16位或32位,则返回对应深度的图像,否则,会将图像转换位8位图像再返回 注意:
如果输入有冲突的标志,则采用较小的数字值,例如:- 当
flag = IMREAD_COLOR|IMREAD_ANYCOLOR
时,程序将载入三通道图- 这里的IMREAD_COLOR(等价于1)和IMREAD_ANYCOLOR(等价于4)是相互冲突的,因为二者都是对读取图像的颜色的方法做规定
- 而如果想要载入最真实无损的源图像,可以选择
flag = IMREAD_ANYCOLOR|IMREAD_ANYDEPTH
- 此处的IMREAD_ANYCOLOR(等价于4)和IMREAD_ANYDEPTH(等价于2)是不相互冲突的,因为前者是对读取图片的颜色的方面做规定,后者是对读取图片的深度的方面做规定
- 因此,
flag = IMREAD_ANYCOLOR|IMREAD_ANYDEPTH
标识符所代表的意思是以任何可能的颜色格式,任何可能的图像深度来读取图像
- 当
注意:下面的表格并不全对,这只是书上的描述,在最新的OpenCV3的版本中,有些许出入
因为flags是int型的变量,若我们不在这个枚举体中去固定的值,可以这样进行
flags取值 达到的效果 flags > 0 返回一个3通道的彩色图像,但数值越大,图片就会呗压缩得越厉害! flags = 0 返回灰度图像 flags < 0 返回包含Alpha通道的加载图像 输出的图像默认情况下不返回Alpha通道,所以若想载入Alpha通道,这里就需要取负值
在书上,作者这样写道:通过表可知,flags = 1999也是可以的,它和flags = 1的效果一样,同样标识返回一个3通道的彩色图像
然而,最新的Opencv3的代码已经不太一样了,flag>1时,不一定会返回一个BGR三通道的图像,甚至flag在不同的数字区间中,所返回的图片大小也是不一样的(具体原因看上面的枚举体代码)
imread的一些常见用法
Mat image0 = imread("1.jpg",2|4);//载入无损的源图像 Mat image1 = imread("1.jpg",0);//载入灰度图 Mat image2 = imread("1.jpg",1);//载入3通道的彩色图像
3.1.5 图像的显示:imshow()函数
imshow()函数用于在指定的窗口中显示一副图像
其函数原型如下
void imshow(const String& winname, InputArray mat); //参数一:图像窗口的标识名称 参数二:需要显示的图像
第一个参数:const String& winname
- 填写要显示的窗口标识名称(暂时不能打中文,问题未解决)
第二个参数:InputArray mat
- 填需要显示的图像
imshow()函数的显示规则
- imshow函数用于在指定的窗口中显示图像
- 如果窗口使用CV_WINDOW_AUTOSIZE(默认值)标志创建的,那么显示图像原始大小
- 否则,将图像进行缩放以适合窗口
imshow()函数缩放图像,取决于图像的深度,具体如下
- 如果载入的图像是8位无符号类型(8-bit unsigned),就显示图像本来的样子
- 如果载入的图像是16位无符号类型(16-bit unsigned)或32位整型(32-bit integer),便用像素值除以256,
也就是说,值的范围是[0,255 x 256]映射到[0,255]
- 如果图像时32位浮点型(32-bit floating-point),像素值要乘以255,
也就是说,值的范围是[0,1]映射到[0,255]
此外,若是在窗口创建的时候,程序设定支持OpenGL(WINDOW_OPENGL),那么imshow还支持
olg::Buffer
、olg::Texture2D
和gpu::GpuMat
作为输入,其函数原型如下namespace ogl {class CV_EXPORTS Buffer;class CV_EXPORTS Texture2D;class CV_EXPORTS Arrays; }void imshow(const String& winname, const ogl::Texture2D& tex);
关于imread()和imshow()函数最精简的示例程序,可以参考书本第一章中,1.3.8节的"最终的测试",或者1.4.1中"第一个程序:图像显示"中的代码.
3.1.6 关于InputArray类型
找到InputArray类型的定义,再OpenCV 3.4.13版本中,该类型位于...\opencv2\core\mat.hpp
中
typedef const _InputArray& InputArray;
从此处可以看出:此处是一个类型声明引用,亦即_InputArray
和InputArray
是同一个变量,所以我们接着做定义跳转
跳到_InputArray的定义处
class CV_EXPORTS _InputArray
{public:enum {KIND_SHIFT = 16,FIXED_TYPE = 0x8000 << KIND_SHIFT,FIXED_SIZE = 0x4000 << KIND_SHIFT,KIND_MASK = 31 << KIND_SHIFT,NONE = 0 << KIND_SHIFT,MAT = 1 << KIND_SHIFT,MATX = 2 << KIND_SHIFT,STD_VECTOR = 3 << KIND_SHIFT,STD_VECTOR_VECTOR = 4 << KIND_SHIFT,STD_VECTOR_MAT = 5 << KIND_SHIFT,EXPR = 6 << KIND_SHIFT, //!< removedOPENGL_BUFFER = 7 << KIND_SHIFT,CUDA_HOST_MEM = 8 << KIND_SHIFT,CUDA_GPU_MAT = 9 << KIND_SHIFT,UMAT =10 << KIND_SHIFT,STD_VECTOR_UMAT =11 << KIND_SHIFT,STD_BOOL_VECTOR =12 << KIND_SHIFT,STD_VECTOR_CUDA_GPU_MAT = 13 << KIND_SHIFT,STD_ARRAY =14 << KIND_SHIFT,STD_ARRAY_MAT =15 << KIND_SHIFT};_InputArray();_InputArray(int _flags, void* _obj);_InputArray(const Mat& m);_InputArray(const MatExpr& expr);_InputArray(const std::vector<Mat>& vec);template<typename _Tp> _InputArray(const Mat_<_Tp>& m);template<typename _Tp> _InputArray(const std::vector<_Tp>& vec);_InputArray(const std::vector<bool>& vec);template<typename _Tp> _InputArray(const std::vector<std::vector<_Tp> >& vec);_InputArray(const std::vector<std::vector<bool> >&);template<typename _Tp> _InputArray(const std::vector<Mat_<_Tp> >& vec);template<typename _Tp> _InputArray(const _Tp* vec, int n);template<typename _Tp, int m, int n> _InputArray(const Matx<_Tp, m, n>& matx);_InputArray(const double& val);_InputArray(const cuda::GpuMat& d_mat);_InputArray(const std::vector<cuda::GpuMat>& d_mat_array);_InputArray(const ogl::Buffer& buf);_InputArray(const cuda::HostMem& cuda_mem);template<typename _Tp> _InputArray(const cudev::GpuMat_<_Tp>& m);_InputArray(const UMat& um);_InputArray(const std::vector<UMat>& umv);#ifdef CV_CXX_STD_ARRAYtemplate<typename _Tp, std::size_t _Nm> _InputArray(const std::array<_Tp, _Nm>& arr);template<std::size_t _Nm> _InputArray(const std::array<Mat, _Nm>& arr);
#endiftemplate<typename _Tp> static _InputArray rawIn(const std::vector<_Tp>& vec);
#ifdef CV_CXX_STD_ARRAYtemplate<typename _Tp, std::size_t _Nm> static _InputArray rawIn(const std::array<_Tp, _Nm>& arr);
#endifMat getMat(int idx=-1) const;Mat getMat_(int idx=-1) const;UMat getUMat(int idx=-1) const;void getMatVector(std::vector<Mat>& mv) const;void getUMatVector(std::vector<UMat>& umv) const;void getGpuMatVector(std::vector<cuda::GpuMat>& gpumv) const;cuda::GpuMat getGpuMat() const;ogl::Buffer getOGlBuffer() const;int getFlags() const;void* getObj() const;Size getSz() const;int kind() const;int dims(int i=-1) const;int cols(int i=-1) const;int rows(int i=-1) const;Size size(int i=-1) const;int sizend(int* sz, int i=-1) const;bool sameSize(const _InputArray& arr) const;size_t total(int i=-1) const;int type(int i=-1) const;int depth(int i=-1) const;int channels(int i=-1) const;bool isContinuous(int i=-1) const;bool isSubmatrix(int i=-1) const;bool empty() const;void copyTo(const _OutputArray& arr) const;void copyTo(const _OutputArray& arr, const _InputArray & mask) const;size_t offset(int i=-1) const;size_t step(int i=-1) const;bool isMat() const;bool isUMat() const;bool isMatVector() const;bool isUMatVector() const;bool isMatx() const;bool isVector() const;bool isGpuMat() const;bool isGpuMatVector() const;~_InputArray();protected:int flags;void* obj;Size sz;void init(int _flags, const void* _obj);void init(int _flags, const void* _obj, Size _sz);
};
- _InputArray的源代码相对较长,从大体上,可以看出该类的里面
- 首先定义了一个枚举类型
- 接着是各类的模板类型和一些方法
- 很多时候,遇到函数模型中InputArray/OutputArray类型,我们都可以简单地将之当作Mat类型
3.1.7 创建窗口:namedWindow()函数
namedWindow函数用于创建一个窗口
若是想简单地进行图片显示,可以略去namedWindow()函数的调用
- 此时先调用imread()函数读入图片,然后用imshow()函数直接指定出窗口名进行显示即可
但如果需要在显示窗口之前就用到窗口名时(例如马上就要涉及到的滑动条的使用),就需要调用namedWindow函数先创建出窗口,显式的规定窗口名称了
neamdWindow()函数原型如下
void namedWindow(const String& winname, int flags = WINDOW_AUTOSIZE);
- 第一个参数:const String& winname
- 填写被作用窗口的标识符的窗口名称
- 第二个参数:int flags
- 窗口的标识,默认值为
WINDOW_AUTOSIZE
,其他可选择的值可看下方代码
- 窗口的标识,默认值为
enum WindowFlags {WINDOW_NORMAL = 0x00000000, //!< the user can resize the window (no constraint) / also use to switch a fullscreen window to a normal size.//用户可以调整窗口大小(无限制),也可以将全屏窗口切换为正常大小。WINDOW_AUTOSIZE = 0x00000001, //!< the user cannot resize the window, the size is constrainted by the image displayed.//用户无法调整窗口大小,该大小受所显示图像的限制。WINDOW_OPENGL = 0x00001000, //!< window with opengl support.//opengl支持的窗口。WINDOW_FULLSCREEN = 1, //!< change the window to fullscreen.//将窗口更改为全屏。WINDOW_FREERATIO = 0x00000100, //!< the image expends as much as it can (no ratio constraint).//图片会尽可能多地支出(无比例限制)。WINDOW_KEEPRATIO = 0x00000000, //!< the ratio of the image is respected.//图像的比例得到尊重。WINDOW_GUI_EXPANDED=0x00000000, //!< status bar and tool bar//状态栏和工具栏WINDOW_GUI_NORMAL = 0x00000010, //!< old fashious way//过去的方法};
- namedWindow()函数有默认值WINDOW_AUTOSIZE,所以一般情况下,调用该函数时,我们只需要填入第一个参数即可
- namedWindow()函数的作用时通过指定名字,创建一个可以作为图像和进度条的容器窗口
- 我i们可以调用destroyWindow()或者destroyAllWindows()函数来关闭窗口,并取消与之分配的窗口的相关的所有内存空间
- 对于代码量不大的简单代码来说,上面两个关闭窗口的函数没必要调用,因为在退出时,所有的资源和应用程序的窗口会被操作系统自动关闭
- 反过来说较大的程序,一定要注意窗口资源的申请与回收!!!
3.1.8 输出图像到文件:imwrite()函数
- imwrite()函数原型如下
bool imwrite( const String& filename, InputArray img,const std::vector<int>& params = std::vector<int>());
第一个参数:const String& filename
- 填需要写入的文件名
- 注意带上文件后缀
第二个参数:InputArray img
- 一般填一个Mat类型的图像数据
- OpenCV中将MAT类型的对象作为InputArray类型的对像传递给函数
第三个参数:const std::vector& params = std::vector()
const std::vector& params表示为特定格式保存的参数编码
该参数有默认值
std::vector<int>()
,所以一般情况下不需要填写若是需要填写,需要了解下面的内容
图片格式 参数 const std::vector<int>& params
的含义JPGE 表示从0到100的图片质量(CV_IMWRITE_JPEG_QUALITY),默认值是95 PNG 表示压缩的级别(CV_IMWRITE_PNG_COMPRESSION)从0到9.
较高的值意味着更小的尺寸和更长的压缩时间,其默认值为3PPM,PGM,PBM 表示一个二进制格式标志(CV_IMWRITE_PXM_BINARY),取值为0或1,默认值为1 C++_vector操作
imwrite()函数用于将图片保存到指定的文件
- 其图像格式是基于文件扩展名的,可保存的扩展名和imread()函数可以读取的扩展名一致
示例程序:在OpenCV中生成一副PNG格式图片,并写入到当前工程目录下
前要知识
Mat(int rows, int cols, int type);/** @overload@param size 2D array size: Size(cols, rows) . In the Size() constructor, the number of rows and thenumber of columns go in the reverse order.@param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, orCV_8UC(n), ..., CV_64FC(n) to create multi-channel (up to CV_CN_MAX channels) matrices.*//*/ ** @过载@param size 2D数组大小:Size(cols,rows)。 在Size()构造函数中,行数和列数以相反的顺序排列。@param type数组类型。 使用CV_8UC1,...,CV_64FC4创建1-4通道矩阵,或CV_8UC(n),...,CV_64FC(n)创建多通道(最多CV_CN_MAX通道)矩阵。* /*/
核心代码
//环境:Windows+Qt+OpenCV//文件:main.cpp#include <QCoreApplication> #include<QDebug>#include <opencv2/opencv.hpp> #include <vector>using namespace cv; using namespace std;void creatAlphaMat(Mat &mat);int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);//创建带Alpha通道的MatMat mat(480,640,CV_8UC4);creatAlphaMat(mat);vector<int>compression_params;compression_params.push_back(IMWRITE_PNG_COMPRESSION);compression_params.push_back(9);try{imwrite("Transparent Alpha value map.png",mat,compression_params);imshow("Generated png image",mat);qDebug()<<"PNG图片文件的alpha数据保存完毕\n您可以在工程目录下查看由imwrite()函数生成的图片\n";waitKey(0);}catch(runtime_error& ex){qDebug()<<"图像转换成PNG格式出错:"<<ex.what()<<endl;return 1;}return a.exec(); }void creatAlphaMat(Mat &mat) {for(int i = 0; i < mat.rows; ++i){for (int j = 0;j < mat.cols;++j){Vec4b& rgba = mat.at<Vec4b>(i,j);rgba[0] = UCHAR_MAX;rgba[1] = saturate_cast<uchar>((float (mat.cols - j))/((float)mat.cols) * UCHAR_MAX);rgba[2] = saturate_cast<uchar>((float (mat.rows - i))/((float)mat.rows) * UCHAR_MAX);rgba[3] = saturate_cast<uchar>(0.5 * (rgba[1] + rgba[2]));}} }
代码运行效果图:
3.1.9 综合示例程序:图像的载入、显示与输出
核心代码,文件:main.cpp
#include <QCoreApplication> #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp>using namespace cv;int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);//----------------------------------------【一、图像的载入和显示】----------------------------------------//描述:以下三行代码用于完成图像的载入和显示//----------------------------------------------------------------------------------------------------//【1】载入图片Mat girl = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpg");//【2】创建一个名为[1]Anime的窗口,窗口大小无限制namedWindow("[1]Anime",WINDOW_NORMAL);//【3】显示名为[1]Anime的窗口imshow("[1]Anime",girl);//----------------------------------------【二、初级图像混合】----------------------------------------//描述:初级图像混合//----------------------------------------------------------------------------------------------------//【1】载入图片Mat image = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpeg");Mat logo = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/data/opencv-logo-white.png");//【2】载入后显示图片namedWindow("[2]Original drawing");imshow("[2]Original drawing",image);namedWindow("[3]Logo");imshow("[3]Logo",logo);//【3】定义一个Mat类型,用于存放图像的ROI//机器视觉、图像处理中,从被处理的图像以方框、圆、椭圆、不规则多边形等方式勾勒出需要处理的区域,称为感兴趣区域,ROI。//方法一//imageROI = image(Rect(image.cols-logo.cols,image.rows-logo.rows,logo.cols,logo.rows));//方法二//imageROI =image(Range(image.rows-logo.rows,image.rows),Range(image.cols-logo.cols,image.cols));//方法三:方法二的改版Mat imageROI(image,Range(image.rows-logo.rows,image.rows),Range(image.cols-logo.cols,image.cols));//【4】将logo加到原图上addWeighted(imageROI,0.5,logo,0.3,0.,imageROI);//【5】显示结果namedWindow("[4]OriginalPic+logoPic");imshow("[4]OriginalPic+logoPic",image);//----------------------------------------【三、图像的输出】----------------------------------------//描述:将一个Mat图像输出到图像文件//----------------------------------------------------------------------------------------------------//输出一张jpg图片到工程目录下imwrite("Final.jpg",image);waitKey(0);return a.exec(); }
代码效果如下
代码用到的函数
【1】Rect()函数,对应上述代码第40行
Rect函数参数列表如下
Rect(int _x,int _y,int _width,int _height);
int_x和int_y: 代表左上角点的坐标。
int_width和int_height:代表需要裁剪区域的尺寸 。
追踪Rect的定义,最终如下
template<typename _Tp> inline Rect_<_Tp>::Rect_(const Point_<_Tp>& org, const Size_<_Tp>& sz): x(org.x), y(org.y), width(sz.width), height(sz.height) {}
【2】Range()函数,对应上述代码第42行
Rang()函数参数列表如下
Range::Range(int _start, int _end)
int _start:代表开始的位置
int _end:代表结束的位置
追踪Range的定义,如下
class CV_EXPORTS Range {public:Range();Range(int _start, int _end);int size() const;bool empty() const;static Range all();int start, end; };inline Range::Range(int _start, int _end): start(_start), end(_end) {}
【3】Mat类的两个重载函数
//使用Range时使用的 Mat(const Mat& m, const Range& rowRange, const Range& colRange=Range::all()); //使用Rect时使用的 Mat(const Mat& m, const Rect& roi);
链接:C++的函数重载
3.2 滑动条的创建和使用
- 滑动条(Trackbar)是OpenCV动态调节参数的特别好用的一种工具,其依附于窗口而存在
- OpenCV中没有实现按钮功能的按钮,所以很多时候,我们还可以用仅含0-1的滑动条来实现按钮的按下-弹起效果
3.2.1 创建滑动条:createTrackbar()函数
createTrackbar()函数用于创建一个可以调整数值的滑动条(一般称为轨迹条),并将其附加到指定的窗口上
createTrackbar()函数一般会和一个回调函数配合起来使用
createTrackbar()函数的函数原型如下:
CV_EXPORTS int createTrackbar(const String& trackbarname, //轨迹条的名字const String& winname,//窗口的名子int* value, //滑动块的初始值int count,//滑动块的最大值TrackbarCallback onChange = 0,//指向回调函数的指针void* userdata = 0);//传给回调函数的数据
第一个参数:const String& trackbarname
- 轨迹条的名字,用来代表我们创建的轨迹条
第二个参数:String& winname
- 窗口的名字,用以指定该轨迹条需要依附到哪个窗口上
第三个参数:int* value
- 一个指向整形的指针,用以表示滑块的位置
- 在滑动条创建时,滑块的初始位置就是该滑块当前的值
第四个参数:int count
- 表示滑块可以达到的最大位置的值
- 滑块最小位置的值始终为0
第五个参数:TrackbarCallback onChange = 0
- 只想一个回调函数的指针
- 默认值为零
- 每次滑块改变位置时,回调函数都会进行回调
- 回调函数的原型必须是
void XXXX(int,void*)
- 第一个参数
int
是轨迹条的位置 - 第二个参数
void*
是用户的数据,详看第六个参数
- 第一个参数
- 如果回调指针指向
NULL
- 表示没有回调函数的调用
- 滑块滑动时,仅仅改变第三个数值value
第六个参数:void* userdata = 0
- 默认值为零
- 用户传回给回调函数的数据,用来处理轨迹事件
- 如果第三个参数value实参是全局变量的话,可以不管这个参数
综上
- createTrackbar函数会创建一个具有
特定名称
和范围
的轨迹条(Trackbar,或称滑块范围控制工具) - 它会指定一个和
轨迹条位置同步
的变量,而且要指定回调函数
- 并且,创建的轨迹条会
显示在指定的窗口
上
- createTrackbar函数会创建一个具有
关于回调函数
- 一个通过函数指针调用的函数
定义
:如果我们把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数是,就成为回调函数这句话老夫没看懂
回调函数不由该函数的实现方(这是哪个瓜娃子?)
直接调用,而是在特定的事件或条件发生时由另外的一方(这又是哪个瓜娃子?)
调用,用于对该事件或条件进行响应
createTrackbar()函数使用举例
createTrackbar("Contrast","Effect Image",&g_nContrastValue,300,on_Change);
- g_nContrastValue为全局的整形变量
- on_Change为回调函数的函数名
(在C/C++中,函数名为指向函数地址的指针)
createTrackbar()函数的完整示例
文件:main.cpp
#include <QCoreApplication> #include <opencv2/opencv.hpp> #include <opencv2/highgui/highgui.hpp> #include <QDebug>using namespace cv;#define WINDOW_NAME "[Linear]" //为窗口标题定义的宏//--------------------------【全局变量声明部分】------------------------- // 描述:全局变量声明 //-------------------------------------------------------------------- const int g_nMaxAlphaValue = 100;//Alpha值的最大值 int g_nAlphaValueSlider;//滑动条对应的变量 double g_dAlphaValue; double g_dBetaValue; char TrackbarName[50]; //声明存储图像的变量 Mat g_srcImage1; Mat g_srcImage2; Mat g_srcImage3; Mat g_dstImage;//-------------------------------------【on_Trackbar()函数】---------------------------------- // 描述:响应滑动条的回调函数 //------------------------------------------------------------------------------------------- void on_Trackbar(int,void*) {//求出当前alpha值相对于最大值的比例g_dAlphaValue = (double) g_nAlphaValueSlider/g_nMaxAlphaValue;//则beta值为1减去alpha值g_dBetaValue = (1.0 - g_dAlphaValue);//根据alpha和beta值进行线性混合addWeighted(g_srcImage1,g_dAlphaValue,g_srcImage2,g_dBetaValue,0.0,g_dstImage);qDebug()<<getTrackbarPos(TrackbarName,WINDOW_NAME);//获取轨迹条位置imshow(WINDOW_NAME,g_dstImage); } //---------------------------------------【main()函数】---------------------------------------- // 描述:控制台应用程序的入口函数,我们的程序从这里开始执行 //-------------------------------------------------------------------------------------------- int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);g_srcImage1 = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpeg");//g_srcImage2 = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/Final.jpg");g_srcImage3 = imread("D:/Study/StudyDocuments/Study-Documents-2021/Year-2021/Study_Note/Qt/Code/opencv/test.jpg");//加载的图片大小必须相等g_srcImage2 = g_srcImage3(Rect(100,1000,500,500));if(!g_srcImage1.data){qDebug()<<"读取第一张图片出错,请确定imread指定的目录下有该图片存在!\n";return -1;}if(!g_srcImage2.data){qDebug()<<"读取第二张图片出错,请确定imread指定的目录下有该图片存在!\n";return -1;}//将滑动条的初值设置为70g_nAlphaValueSlider = 70;//创建窗体namedWindow(WINDOW_NAME);//在创建的窗体中创建一个滑动条控件sprintf(TrackbarName,"Transparency value:%d",g_nMaxAlphaValue);createTrackbar(TrackbarName,WINDOW_NAME,&g_nAlphaValueSlider,g_nMaxAlphaValue,on_Trackbar);//结果在回调函数中显示on_Trackbar(g_nAlphaValueSlider, 0 );//按任意键推出waitKey(0);return a.exec(); }
3.2.2 获取当前轨迹条的位置:getTrackbarPos()函数
getTrackbarPos()函数用于获取当前轨迹条的位置并返回,该函数在上面的示例代码中有用到(Line36)
函数原型
CV_EXPORTS_W int getTrackbarPos(const String& trackbarname, const String& winname);
第一个参数:const String& trackbarname
- 表示轨迹条的名字
第二个参数:const String& winname
- 表示轨迹条的父窗口的名称
实现效果(见控制台中刷新的数字)
3.3 鼠标操作
OpenCV中的鼠标操作和滑动条的消息映射方式很类似,都是通过一个中介函数配合一个回调函数来实现
创建和指定鼠标操作回调函数的函数名为SetMouseCallback()
其函数原型如下
CV_EXPORTS void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
第一个参数:const String& winname
- 窗口的名字
第二个参数:MouseCallback onMouse
指定窗口里每次鼠标时间发生的时候,被调用的函数指针
回调函数的原型的大概形式为
void Foo(int event,int x,int y, int flags,void *param)
- 第一个参数:int event
- EVENT_+变量之一
- 第二、三个参数:int x,int y
- 鼠标指针在图像坐标系
(注意不是窗口坐标系)
中的坐标值
- 鼠标指针在图像坐标系
- 第四个参数:int flags
- EVENT_FLAGS的组合
- 第五个参数:void *param
- 用户定义的传递到SetMouseCallback()函数调用的参数
- 如EVENT_MOUSEMOVE----鼠标移动消息,EVENT_LBUTTINDOWN----鼠标左键按下消息
- 第一个参数:int event
第三个参数:void* userdata = 0
- 默认值为零
- 用户定义的传递到回调函数的参数
实际示例
文件:main.cpp
//---------------------------------【预编译内容】---------------------------------- #include <QCoreApplication> #include <opencv2/opencv.hpp> using namespace cv; #define WINDOW_NAME "MOUSE" //---------------------------------【全局函数声明】----------------------------- void on_MouseHandle(int event,int x,int y,int flags,void *param);//鼠标处理函数(回调函数) void DrawRectabgle(cv::Mat& img,cv::Rect box);//用于绘画的函数 //---------------------------------【全局变量声明】-------------------------------- Rect g_rectangle;//声明一个矩形,详情见下方链接 bool g_bDrawingBox = false;//是否进行绘制 RNG g_rng(12345);//用RNG产生随机数,详情见下面链接 //-------------------------------【main函数】------------------------------------- int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);//【1】准备参数g_rectangle = Rect(-1,-1,0,0);//矩形Mat srcImage(600,800,CV_8UC3),tempImage;//画布srcImage.copyTo(tempImage);//把srcImage的内容复制到tempImage里面srcImage = Scalar::all(0);//给srcImage的每个通道都赋值0//【2】设置鼠标操作回调函数namedWindow(WINDOW_NAME);setMouseCallback(WINDOW_NAME,on_MouseHandle,(void*)&srcImage);//【3】程序主循环,当进行绘制的标识符为真时,进行绘制while(1){srcImage.copyTo(tempImage);//复制原图到临时变量if(g_bDrawingBox)DrawRectabgle(tempImage,g_rectangle);//当进行绘制的标识符为真时,进行绘制imshow(WINDOW_NAME,tempImage);//显示临时变量if(waitKey(10) == 27)break;//按下esc键,退出循环(但不会关闭窗口)}return a.exec(); } //-------------------------------【on_MouseHandle函数】--------------------------------------- void on_MouseHandle(int event,int x,int y,int flags,void *param) {Mat &image = *(cv::Mat*) param;//这里创建了一个临时的Mat类switch (event){//鼠标移动事件(其实按照下方的代码定义,可以理解为鼠标左键按下之后,移动时的事件)case EVENT_MOUSEMOVE:{if(g_bDrawingBox)//如果是否进行绘制的标识符为真,则记录下长和宽到Rect型变量中{g_rectangle.width = x-g_rectangle.x;g_rectangle.height = y-g_rectangle.y;}}break;//鼠标左键按下消息case EVENT_LBUTTONDOWN:{g_bDrawingBox = true;g_rectangle = Rect(x,y,0,0);//记录下鼠标此刻的位置}break;//鼠标抬起消息case EVENT_LBUTTONUP:{g_bDrawingBox = false;//对宽和高小于0的处理if(g_rectangle.width < 0){g_rectangle.x += g_rectangle.width;g_rectangle.width *= -1;}if(g_rectangle.height < 0){g_rectangle.y += g_rectangle.height;g_rectangle.height *= -1;}//调用绘制函数DrawRectabgle(image,g_rectangle);}break;} } //-------------------------------------【DrawRectabgle()】------------------------------------------- void DrawRectabgle(cv::Mat& img,cv::Rect box) {rectangle(img,box.tl(),box.br(),Scalar(g_rng.uniform(0,255),g_rng.uniform(0,255),g_rng.uniform(0,255)));//随机颜色 }
代码运行效果
代码分析
opencv - CvType.CV_8UC4的用途是什么
opencv中用RNG产生随机数
opencv Rect类用法
Rect(int _x,int _y,int _width,int _height);
参数意思为:左上角x坐标
左上角y坐标
矩形的宽
矩形的高
一般的用法为Rect g_rectangle;
g_rectangle=Rect(a,b,c,d);
copyTo()函数
copyTo最一般的用法是src.copyTo(dst),将src复制到dst矩阵中。
后面一个参数可以表示复制的部分,如上面的代码src.copyTo( dst, detected_edges);
是将src中detected_edges矩阵对应的非零部分(即边缘检测结果)复制到dst中。
所以最终显示的边缘和原图颜色一样,也可以直接显示detected_edges矩阵(黑白)
Mat() opencv Scalar::all(0)
3.4 本章小结
本章核心函数清单
函数名称 | 用途 | 讲解章节 |
---|---|---|
imread | 用于读取文件中的图片到OpenCV中 | 3.1.4 |
imshow | 在指定的窗口中显示一副图像 | 3.1.5 |
namedWindow | 用于创建一个窗口 | 3.1.7 |
imwrite | 输出图像到文件 | 3.1.8 |
creatTrackbar | 用于创建一个可以调整数值的轨迹条 | 3.2.1 |
getTrackbarPos | 用于获得轨迹条的当前位置 | 3.2.2 |
SetMouseCallback | 为指定的窗口设置鼠标回调函数 | 3.3 |
【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步相关推荐
- Python快速编程入门#学习笔记01# |第一章 :Python基础知识 (Python发展历程、常见的开发工具、import模块导入)
全文目录 ==先导知识== 1 认识Python 1.1.1 Python的发展历程 1.1.2 Python语言的特点 2. Python解释器的安装与Python程序运行 1.2.1 安装Pyth ...
- 原创 OpenCV3编程入门 学习笔记(总)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_36163358/article/ ...
- Python快速编程入门#学习笔记03# |第二章 :Python基础(代码格式、标识符关键字、变量和数据类型、数字类型以及运算符)
全文目录 ==先导知识== 学习目标: 2.1 代码格式 2.1.1 注释 2.1.2 缩进 2.1.3 语句换行 2.2 标识符和关键字 2.2.1 标识符 2.2.2 关键字 2.3 变量和数据类 ...
- OpenCV3编程入门 学习笔记(总)
OpenCV3编程入门 学习笔记 2018.12.12-2018.12.29 此博客为在看过毛星云版<OpenCV3编程入门>后所总结的一本笔记,可供复习使用. 文章目录 OpenCV3编 ...
- Opencv3编程入门学习笔记(五)之通道分离(split)与合并(merge)
若要对Opencv中(BGR)颜色通道进行单一处理,那必然会涉及到通道分离(split)与合并(merge).那么本篇博客笔者记录了两个方法的使用方法和案例.案例来源于<Opencv3编程入门学 ...
- 【OpenCV3编程入门学习笔记】——第1章 邂逅OpenCV
邂逅OpenCV 文章目录 邂逅OpenCV 前言 1.1 OpenCV周边概念认知 1.1.1 图像处理.计算机视觉与OpenCV 1.1.2 OpenCV概述 1.1.3 起源及发展 1.1.4 ...
- Opencv3编程入门学习笔记(三)之访问图像像素的三种方法
访问图像像素的三种方法:指针访问,迭代器访问,动态地址访问.访问最快的为指针访问,以下算法在几毫秒,但指针访问容易造成内存泄漏:其次为迭代器访问:最后为动态地址访问. 以下程序是根据<OpenC ...
- Opencv3编程入门学习笔记(四)之split通道分离Debug过程中0xC0000005内存访问冲突问题
这是笔者学习<Opencv3编程入门>的第四篇博客,这篇博客主要是解决在Windows系统下VS 2013中Debug含有split分离通道色彩函数时报出的0xC0000005内存访问冲突 ...
- Opencv3编程入门学习笔记(二)之显式创建Mat对象
以下总结是基于<Opencv3编程入门>一书4.1节总结的内容进行验证与总结,验证环境均为Windows10 ---VS2013 C++环境,验证Opencv3.0提供的开发包. 1. 方 ...
最新文章
- OpenvSwitch — ovs-db 数据库
- SP 2010: Getting started with Business Connectivity Services (BCS) in SharePoint 2010
- Javascript非构造函数的继承
- Android基础——数据持久化存储
- eclipse maven jetty启动修改默认端口
- php注册页面模板,选项卡式WordPress登陆注册模板
- 《剑指Offer》 二进制中1的个数
- Mysql相关问题收集
- Drcom账户管理Server端解说
- Protues 仿真器件
- Java基础:网络编程的简单总结
- 红米6.0系统如何无root激活xposed框架的教程
- 计蒜客蓝桥杯模拟赛---青出于蓝而胜于蓝
- FindBugs的使用
- N1刷入Armbian(Debian11 bullseye)笔记
- ecshop打开手机端QQ对话窗口
- 什么是单工、半双工和双工通信(最详细)
- APK部署手机上出现闪退现象
- 机器人自动化《RPA国内外平台深度对比》
- 明星直播的品牌效应,这几个关键数据你一定要知道!
热门文章
- 什么是IDP,如何制定个人IDP​计划
- 开源社区的治理模型应当因地制宜
- AE模板 | 数字科技空间穿梭发光文字特效开场视频模板 | Fast Digital Intro
- Python操作MongoDB基本使用
- 14.CPU调度——CPU调度策略
- uniapp(HBuilder X)实现微信小程序转发好友和分享朋友圈(携带多个参数)
- 公有云+5G核心网,狼真的来了吗?
- 阿里云短信平台收费标准价格表
- Java开发微信公众号之整合weixin-java-tools框架开发微信公众号
- c语言输入字符串 Eof,C语言EOF如何使用