【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 
#include 
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/4
                 IMREAD_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,  //!< removed
        OPENGL_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_ARRAY
    template<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);
#endif

    template<typename _Tp> static _InputArray rawIn(const std::vector<_Tp>& vec);
#ifdef CV_CXX_STD_ARRAY
    template<typename _Tp, std::size_t _Nm> static _InputArray rawIn(const std::array<_Tp, _Nm>& arr);
#endif

    Mat 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(),所以一般情况下不需要填写

    • 若是需要填写,需要了解下面的内容

    • 图片格式 参数const std::vector& 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 the
            number of columns go in the reverse order.
            @param type Array type. Use CV_8UC1, ..., CV_64FC4 to create 1-4 channel matrices, or
            CV_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 
        #include
        
        #include 
        #include 
        
        using namespace cv;
        using namespace std;
        
        void creatAlphaMat(Mat &mat);
        
        int main(int argc, char *argv[])
        {
            QCoreApplication a(argc, argv);
            //创建带Alpha通道的Mat
            Mat 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]));
                }
            }
        }
        
    • 代码运行效果图:

      • 【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步_第1张图片

3.1.9 综合示例程序:图像的载入、显示与输出

  • 核心代码,文件:main.cpp

    • #include 
      #include 
      #include 
      
      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();
      }
      
    • 代码效果如下

【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步_第2张图片

  • 代码用到的函数

    • 【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 
      #include 
      #include 
      #include 
      
      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;
          }
          //将滑动条的初值设置为70
          g_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();
      }
      
    • 【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步_第3张图片

    • 【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步_第4张图片

    • 【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步_第5张图片

3.2.2 获取当前轨迹条的位置:getTrackbarPos()函数

  • getTrackbarPos()函数用于获取当前轨迹条的位置并返回,该函数在上面的示例代码中有用到(Line36)

  • 函数原型

    • CV_EXPORTS_W int getTrackbarPos(const String& trackbarname, const String& winname);
      
    • 第一个参数:const String& trackbarname

      • 表示轨迹条的名字
    • 第二个参数:const String& winname

      • 表示轨迹条的父窗口的名称
  • 实现效果(见控制台中刷新的数字)

    • 【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步_第6张图片

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 
      #include 
      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)));//随机颜色
      }
      
    • 代码运行效果

      • 【OpenCV3编程入门学习笔记】——第3章 HighGUI图形用户界面初步_第7张图片
    • 代码分析

      • 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

你可能感兴趣的:(Qt+OpenCV基础学习,opencv,qt)