文章目录

  • 前言
  • 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::Bufferolg::Texture2Dgpu::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;

从此处可以看出:此处是一个类型声明引用,亦即_InputArrayInputArray是同一个变量,所以我们接着做定义跳转

跳到_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.
      较高的值意味着更小的尺寸和更长的压缩时间,其默认值为3
      PPM,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("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----鼠标左键按下消息
      • 第三个参数: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图形用户界面初步相关推荐

  1. Python快速编程入门#学习笔记01# |第一章 :Python基础知识 (Python发展历程、常见的开发工具、import模块导入)

    全文目录 ==先导知识== 1 认识Python 1.1.1 Python的发展历程 1.1.2 Python语言的特点 2. Python解释器的安装与Python程序运行 1.2.1 安装Pyth ...

  2. 原创 OpenCV3编程入门 学习笔记(总)

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/qq_36163358/article/ ...

  3. Python快速编程入门#学习笔记03# |第二章 :Python基础(代码格式、标识符关键字、变量和数据类型、数字类型以及运算符)

    全文目录 ==先导知识== 学习目标: 2.1 代码格式 2.1.1 注释 2.1.2 缩进 2.1.3 语句换行 2.2 标识符和关键字 2.2.1 标识符 2.2.2 关键字 2.3 变量和数据类 ...

  4. OpenCV3编程入门 学习笔记(总)

    OpenCV3编程入门 学习笔记 2018.12.12-2018.12.29 此博客为在看过毛星云版<OpenCV3编程入门>后所总结的一本笔记,可供复习使用. 文章目录 OpenCV3编 ...

  5. Opencv3编程入门学习笔记(五)之通道分离(split)与合并(merge)

    若要对Opencv中(BGR)颜色通道进行单一处理,那必然会涉及到通道分离(split)与合并(merge).那么本篇博客笔者记录了两个方法的使用方法和案例.案例来源于<Opencv3编程入门学 ...

  6. 【OpenCV3编程入门学习笔记】——第1章 邂逅OpenCV

    邂逅OpenCV 文章目录 邂逅OpenCV 前言 1.1 OpenCV周边概念认知 1.1.1 图像处理.计算机视觉与OpenCV 1.1.2 OpenCV概述 1.1.3 起源及发展 1.1.4 ...

  7. Opencv3编程入门学习笔记(三)之访问图像像素的三种方法

    访问图像像素的三种方法:指针访问,迭代器访问,动态地址访问.访问最快的为指针访问,以下算法在几毫秒,但指针访问容易造成内存泄漏:其次为迭代器访问:最后为动态地址访问. 以下程序是根据<OpenC ...

  8. Opencv3编程入门学习笔记(四)之split通道分离Debug过程中0xC0000005内存访问冲突问题

    这是笔者学习<Opencv3编程入门>的第四篇博客,这篇博客主要是解决在Windows系统下VS 2013中Debug含有split分离通道色彩函数时报出的0xC0000005内存访问冲突 ...

  9. Opencv3编程入门学习笔记(二)之显式创建Mat对象

    以下总结是基于<Opencv3编程入门>一书4.1节总结的内容进行验证与总结,验证环境均为Windows10 ---VS2013 C++环境,验证Opencv3.0提供的开发包. 1. 方 ...

最新文章

  1. OpenvSwitch — ovs-db 数据库
  2. SP 2010: Getting started with Business Connectivity Services (BCS) in SharePoint 2010
  3. Javascript非构造函数的继承
  4. Android基础——数据持久化存储
  5. eclipse maven jetty启动修改默认端口
  6. php注册页面模板,选项卡式WordPress登陆注册模板
  7. 《剑指Offer》 二进制中1的个数
  8. Mysql相关问题收集
  9. Drcom账户管理Server端解说
  10. Protues 仿真器件
  11. Java基础:网络编程的简单总结
  12. 红米6.0系统如何无root激活xposed框架的教程
  13. 计蒜客蓝桥杯模拟赛---青出于蓝而胜于蓝
  14. FindBugs的使用
  15. N1刷入Armbian(Debian11 bullseye)笔记
  16. ecshop打开手机端QQ对话窗口
  17. 什么是单工、半双工和双工通信(最详细)
  18. APK部署手机上出现闪退现象
  19. 机器人自动化《RPA国内外平台深度对比》
  20. 明星直播的品牌效应,这几个关键数据你一定要知道!

热门文章

  1. 什么是IDP,如何制定个人IDP​计划
  2. 开源社区的治理模型应当因地制宜
  3. AE模板 | 数字科技空间穿梭发光文字特效开场视频模板 | Fast Digital Intro
  4. Python操作MongoDB基本使用
  5. 14.CPU调度——CPU调度策略
  6. uniapp(HBuilder X)实现微信小程序转发好友和分享朋友圈(携带多个参数)
  7. 公有云+5G核心网,狼真的来了吗?
  8. 阿里云短信平台收费标准价格表
  9. Java开发微信公众号之整合weixin-java-tools框架开发微信公众号
  10. c语言输入字符串 Eof,C语言EOF如何使用