与OpenCV的第三天

第一件事:HighGUI 模块学习

1. OpenCV 命名空间

之前的代码每一个 OpenCV 类和函数之前都会加上 cv:: ,这是因为 OpenCV 的代码都是定义在命名空间 cv 之内的,需要通过在类和函数之前加上 cv::cv 命名空间内的类和函数进行调用。但是还有一种更方便的用法,是在代码开头的适当位置加上 using namespace cv; 这句代码。规定之后的程序都位于此命名空间之内。
比如:

#include <opencv2/opencv.cpp>
using namespace cv;

这样,之后的程序都不需要再加 cv:: 前缀。

2. 图像载入: imread 函数

imread 函数用于读取文件中的图片到 OpenCV 中。原型如下:

Mat imread(const string& filename, int flags = 1);
  • 第一个参数,const string& 类型的变量,需要填入图片的路径名。
    支持如下类型的图像输入:
类型 后缀
位图 *.bmp,*.dib
JPEG文件 *.jpeg,*.jpg,*.jpe
JPEG 2000 文件 *.jp2
PNG图片 *.png
便携文件格式 *.pbm,*.pgm,*ppm
Sun rasters 光栅文件 *.sr,*.ras
TIFF 文件 *.tiff,*.tif
  • 第二个参数,int 类型的变量,指定一个加载图像的显示颜色类型。默认值为 1,表示载入三通道的彩色图。我们可以在 imgcodecs_c.hOpenCV2 中,被定义在 higui_c.h 文件中) 文件中找到这个枚举的定义:
enum
{
/* 8bit, color or not */
    CV_LOAD_IMAGE_UNCHANGED  =-1,
/* 8bit, gray */
    CV_LOAD_IMAGE_GRAYSCALE  =0,
/* ?, color */
    CV_LOAD_IMAGE_COLOR      =1,
/* any depth, ? */
    CV_LOAD_IMAGE_ANYDEPTH   =2,
/* ?, any color */
    CV_LOAD_IMAGE_ANYCOLOR   =4,
/* ?, no rotate */
    CV_LOAD_IMAGE_IGNORE_ORIENTATION  =128
};
参数名 介绍
CV_LOAD_IMAGE_UNCHANGED CV_LOAD_IMAGE_ANYCOLOR 相比,除了会载入Alpha通道外基本是同等效果的,所以这个参数渐渐使用的很少了。
CV_LOAD_IMAGE_GRAYSCALE 将图像转为灰度再返回。
CV_LOAD_IMAGE_COLOR 将图像转换为3通道彩色再返回。
CV_LOAD_IMAGE_ANYDEPTH 若载入图像深度为 16 位 或 32 位,就返回对应深度的图像,否则将图像深度转为8位再返回。
CV_LOAD_IMAGE_ANYCOLOR 保持颜色通道不变
CV_LOAD_IMAGE_IGNORE_ORIENTATION 忽略图片文件中的 orientation 旋转参数载入

注意:如果输入的参数之间有冲突,则会采用较小的值。如 CV_LOAD_IMAGE_COLOR | CV_LOAD_IMAGE_ANYCOLOR 将按照 CV_LOAD_IMAGE_COLOR 载入三通道图。

若这个参数不取这个枚举值,那么将会按照:

  • flags > 0 返回 3 通道彩色图(CV_LOAD_IMAGE_COLOR);
  • flags = 0 返回灰度图(CV_LOAD_IMAGE_GRAYSCALE);
  • flags < 0 返回包含Alpha通道的彩色图(CV_LOAD_IMAGE_UNCHANGED);

这个规则载入图像。

3.图像显示: imshow 函数

imshow 用于在指定窗口显示图像,原型如下:

void imshow(const string& winname, InputArray Mat);
  • 第一个参数:const string& 类型的变量,需要显示的窗口名。
  • 第二个参数:InputArray 类型的变量,需要显示的图像。如果窗口是用 CV_WINDOW_AUTOSIZE(默认值) 创建的,那么就按图像原始大小显示(窗口会自动适应图片大小)。否则缩放图像适应窗口大小。而 imshow 函数对图像深度的缩放会规范到[0,255] 区间,即 8 位深度。
InputArray 类型:

opencv_core 模块中的 mat.hpp 头文件中可以找到该类型定义:

typedef const _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,
        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
    };

    _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);
    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);

    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 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);
};

可以看出这是一个代理类并通过 c++ 的转换构造函数机制,实现传入 Mat 类型并转为 InputArray 的相应构造函数 _InputArray(const Mat& m);,构造出一个 InputArray 类型的对象。
代理类:目标类(可多个)为其内部的 private、protected 类型成员变量,并通过创建同名方法以及对目标类相应方法的调用,完成对目标类的代理行为。
简而言之,就是替目标类传话的一个类。

4. 创建窗口:namedWindow 函数

namedWindow 的函数原型如下:

void namedWindow(const string& winname, int flags=WINDOW_AUTOSIZE);
  • 第一个参数,const string& 类型的变量,窗口标识符。
  • 第二个参数,int 类型的变量,窗口类型。
类型 介绍
WINDOW_NORMAL 用户可以改变窗口的大小
WINDOW_AUTOSIZE 窗口自动调整,以显示所显示的图像。用户不可以手动设置。
WINDOW_OPENGL 使创建的窗口支持OpenGL

5. 输出图像到文件:imwrite 函数

imwrite 函数原型如下:

bool imwrite(const string& filename,InputArray img, vector<int> params=vector<int>());
  • 第一个参数:const string& 类型的变量,文件名(需要后缀)。
  • 第二个参数:InputArray 类型的数据,一般是 Mat 类型的图像数据。
  • 第三个参数:vector<int> 类型的变量,特定格式保存的参数编码。它有默认值。
图片格式 介绍
JPEG 这个参数表示从 0 到 100 的图片质量,默认值95。
PNG 这个参数表示压缩级别(CV_IMWRITE_PNG)从 0 到 9。值越高意味着,尺寸越小,压缩时间越长。 默认值是 3
PPM,PGM,PBM 表示二进制格式标志(CV_IMWRITE_PXM_BINARY),取值为 0 或者 1。

支持保存的几种图片格式与支持读取的几种图片格式一致。

6. 图像载入、显示与输出的综合程序

#include <iostream>
#include <opencv2/opencv.hpp>
int main(int argc, const char * argv[]) {
//载入图像
    cv::Mat image = cv::imread("1.jpg");
    cv::Mat logo = cv::imread("Code_r_Wang.jpg");
    
    //显示原图像
    cv::namedWindow("1.jpg");
    cv::imshow("1.jpg", image);
    
    //显示logo图像
    cv::namedWindow("Code_r_Wang.jpg");
    cv::imshow("Code_r_Wang.jpg", logo);
    
    //选取部分图像
    cv::Mat imageROI;
    imageROI = image(cv::Rect(image.cols - logo.cols - 10, image.rows - logo.rows - 10, logo.cols, logo.rows));

    //混合图像(这个函数放到后面学)
    cv::addWeighted(imageROI, 0.5, logo, 0.5, 0.0, imageROI);
    
    //展示结果图
    cv::namedWindow("result");
    cv::imshow("result", image);
    
    //图像输出到文件
    cv::imwrite("叠加的结果图像.jpg", image);

    cv::waitKey();

return 0;
}
运行效果图【原图】
运行效果图【logo图】
运行效果图【原图+logo图】
右击 **Products** 目录下生成的可运行的程序点击 **show in finder** 即可查看生成的图片
生成的图像

7. 滑动条

  • 创建滑动条
    createTrackbar 函数,用于创建一个可以调整数值的滑动条,并将滑动条依附至指定窗口。函数原型如下:
int createTrackbar(const String& trackbarname, const String& winname, int *value, int count, TrackbarCallback onChange = 0, void* userdata = 0);

第一个参数:const String& 类型变量,滑动条的标识符。
第二个参数:const String& 类型变量,要依附的窗口的标识符。
第三个参数:int* 类型变量,一个指向整型的指针,表示滑块的位置。
第四个参数:int 类型变量,表示滑块滑块可以到达的最大位置。滑块最小位置为 0。
第五个参数:TrackbarCallback 类型的 onChange,默认值为 0。一个指向回调函数的指针。每次滑动条位置改变,都会回调这个函数。可以在 highgui.hpp 头文件中找到定义 typedef void (*TrackbarCallback)(int pos, void* userdata); 指向的指针必须为 void (int ,void*)类型。第一个参数为滑块的位置,第二个参数为用户想要传递的数据。
第六个参数:void* 类型的变量,默认值为 0。这个参数会作为回调函数的第二个参数。

  • 测试程序
#include <iostream>
#include <opencv2/opencv.hpp>
//窗口宏
#define WINDOW_NAME "Trackbar Demo"

//滑动条最大值
const int g_nMaxAlphaValue = 100;

//滑动条对应变量
int g_nAlphaValueSlider;
double g_dAlphaValue;
double g_dBetaValue;

//图像
cv::Mat g_srcImage1;
cv::Mat g_srcImage2;
cv::Mat g_dstImage;

void on_Trackbar(int count, void* userdata){
    //计算 alpha 值
    g_dAlphaValue = (double) g_nAlphaValueSlider/g_nMaxAlphaValue;

    //计算 beta 值
    g_dBetaValue = 1 - g_dAlphaValue;
    
    //线性混合(这个函数放到后面学)
    cv::addWeighted(g_srcImage1, g_dAlphaValue, g_srcImage2, g_dBetaValue, 0.0, g_dstImage);
    
    //显示效果图
    cv::imshow(WINDOW_NAME, g_dstImage);
}

int main(int argc, const char * argv[]) {

    //载入图像
    g_srcImage1 = cv::imread("1.jpg");
    g_srcImage2 = cv::imread("2.jpg");
    
    //判断是否载入成功,失败返回 -1
    if ( !g_srcImage1.data || !g_srcImage2.data) {
        return -1;
    }
    
    //赋最大参数值
    g_nAlphaValueSlider = 70;
    
    //创建窗口
    cv::namedWindow(WINDOW_NAME);
    
    //创建滑动条
    cv::createTrackbar("TrackBar", WINDOW_NAME, &g_nAlphaValueSlider, g_nMaxAlphaValue, on_Trackbar);
    
    //初次混合
    on_Trackbar(g_nAlphaValueSlider, NULL);
    
    cv::waitKey();
    
    return 0;
}
透明值 70 效果图
透明值 100 效果图
透明值 0 效果图

8. 鼠标操作:setMouseCallback 函数

鼠标操作的消息传递和滑动条的消息传递方式很类似。setMouseCallback 函数原型如下:

void setMouseCallback(const String &winname, MouseCallBack onMouse, void* userdata);
  • 第一个参数窗口标识符。
  • 第二个参数 MouseCallback 类型函数指针
//highgui.hpp 头文件中
/** @brief Callback function for mouse events. see cv::setMouseCallback
@param event one of the cv::MouseEventTypes constants.
@param x The x-coordinate of the mouse event.
@param y The y-coordinate of the mouse event.
@param flags one of the cv::MouseEventFlags constants.
@param userdata The optional parameter.
 */
typedef void (*MouseCallback)(int event, int x, int y, int flags, void* userdata);

第一个参数:int 类型变量对应枚举值

enum MouseEventTypes {
       EVENT_MOUSEMOVE      = 0, //!< indicates that the mouse pointer has moved over the window.
       EVENT_LBUTTONDOWN    = 1, //!< indicates that the left mouse button is pressed.
       EVENT_RBUTTONDOWN    = 2, //!< indicates that the right mouse button is pressed.
       EVENT_MBUTTONDOWN    = 3, //!< indicates that the middle mouse button is pressed.
       EVENT_LBUTTONUP      = 4, //!< indicates that left mouse button is released.
       EVENT_RBUTTONUP      = 5, //!< indicates that right mouse button is released.
       EVENT_MBUTTONUP      = 6, //!< indicates that middle mouse button is released.
       EVENT_LBUTTONDBLCLK  = 7, //!< indicates that left mouse button is double clicked.
       EVENT_RBUTTONDBLCLK  = 8, //!< indicates that right mouse button is double clicked.
       EVENT_MBUTTONDBLCLK  = 9, //!< indicates that middle mouse button is double clicked.
       EVENT_MOUSEWHEEL     = 10,//!< positive and negative values mean forward and backward scrolling, respectively.
       EVENT_MOUSEHWHEEL    = 11 //!< positive and negative values mean right and left scrolling, respectively.
     };

从枚举的名称不难看出其所代表的意义。

int x, int y 参数代表鼠标指针在图像坐标系中的坐标。
int flags 参数代表物理按键类型。对应枚举值如下:

//! Mouse Event Flags see cv::MouseCallback
enum MouseEventFlags {
       EVENT_FLAG_LBUTTON   = 1, //!< indicates that the left mouse button is down.
       EVENT_FLAG_RBUTTON   = 2, //!< indicates that the right mouse button is down.
       EVENT_FLAG_MBUTTON   = 4, //!< indicates that the middle mouse button is down.
       EVENT_FLAG_CTRLKEY   = 8, //!< indicates that CTRL Key is pressed.
       EVENT_FLAG_SHIFTKEY  = 16,//!< indicates that SHIFT Key is pressed.
       EVENT_FLAG_ALTKEY    = 32 //!< indicates that ALT Key is pressed.
     };

*void userdata 参数代表自定义参数。

  • 第三个参数:*void userdata 参数代表自定义参数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容