Opencv学习-窗口交互

交互操作能够增加用户对程序流程的控制,使程序可以根据用户需求实现不同的处理结果。有时某一个参数需要反复尝试不同的数值,这时交互操作可以实现在程序运行过程中改变参数数值的作用,避免重复运行程序,节省时间,同时能够增强结果的对比效果。本文将介绍 OpenCV 4 中提供的图像窗口滑动条和鼠标响应两种窗口交互操作。

1. 图像窗口滑动条

图像窗口滑动条,顾名思义,就是在显示图像的窗口中创建能够通过滑动改变数值的滑动条。有时,我们需要动态调节某些参数,以使图像处理的效果更加明显,能够改变参数数值的滑动条可以很好地胜任这项工作。OpenCV 4 通过 createTrackbar() 函数在显示图像的窗口中创建滑动条

1.1 createTrackbar函数原型

int cv::createTrackbar(const String & trackbarname, 
const String & winname, 
int * value, 
int count, 
TrackbarCallback onChange = 0, 
void * userdata = 0 
)

• trackbarname:滑动条的名称。

• winname:创建滑动条窗口的名称。

• value:指向整数变量的指针,该指针指向的值反映滑块的位置,创建后,滑块位置由此变量定义。

• count:滑动条的最大取值。

• onChange:每次滑块更改位置时要调用的函数的指针,其中函数原型为 void Foo(int, void*);,其中第一个参数是轨迹栏位置,第二个参数是用户数据。如果回调是 NULL 指针,则不会调用任何回调,而只是更新数值。

• userdata:传递给回调函数的可选参数。
该函数能够在图像窗口的上方创建一个范围从 0 开始的整数滑动条。由于滑动条默认只能输出整数,因此,如果需要得到小数,就必须进行后续处理,例如输出值除以 10 得到含有一位小数的数据。该函数第一个参数是滑动条的名称。第二个参数是创建滑动条的图像窗口的名称。第三个参数是指向整数变量的指针,该指针指向的值反映滑块的位置,在创建滑动条时,该参数确定了滑块的初始位置,当滑动条创建完成后,该指针指向的整数随着滑块的移动而改变。第四个参数是滑动条的最大取值。第五个参数是每次滑块更改位置时要调用的函数的指针。最后一个参数是传递给回调函数的 void * 类型数据,如果使用的第三个参数是全局变量,则可以不用修改最后一个参数,使用参数的默认值即可。
在示例程序中,滑动条控制图像亮度系数,将图像原始灰度值乘以亮度系数得到最终的图像。为了使图像亮度变化比较平滑,将滑动条参数除以 100 以得到含有两位小数的亮度系数。为了保证每次亮度的改变都是在原始图像的基础上,设置了 img1 、 img2两个表示图像的全局变量,其中 img1 表示原始图像, img2 表示亮度改变后的图像。在该程序中,通过拖曳滑块可以动态地改变图像的亮度。

1.2 示例代码

#include <opencv2/opencv.hpp> 
#include <iostream> 

using namespace std; 
using namespace cv; 

//为了能在被调用函数中使用,设置成全局的
int value; 
void callBack(int, void*); //滑动条回调函数
Mat img1, img2; 

int main() 
{ 
    img1 = imread("../pic/gril.jpg"); 
    if (!img1.data) 
    { 
        cout << "请确认是否输入正确的图像文件" << endl; 
        return -1; 
    } 

    Scalar scalar = mean(img1);
	float imgChannel1 = scalar.val[0];
	float imgChannel2 = scalar.val[1];
	float imgChannel3 = scalar.val[2];
	//float imgChannel4 = scalar.val[3];
	//qDebug() << "--4.method mean picture Light : " << QString::number(picMeanLight, 10, 4);
	float imgLight = (imgChannel1 + imgChannel2 + imgChannel3) / 3;
    
    cout << "111 imgLight == " << imgLight << endl;

    namedWindow("滑动条改变图像亮度"); 
    imshow("滑动条改变图像亮度", img1); 
    value = 100; //滑动条创建时的初始值
    //创建滑动条
    createTrackbar("亮度值百分比", "滑动条改变图像亮度", &value, 600, callBack, 0); 
    waitKey(); 
} 

void callBack(int, void*) 
{ 


    float a = value / 100.0; 
    img2 = img1 * a; 

    Scalar scalar = mean(img2);
	float imgChannel1 = scalar.val[0];
	float imgChannel2 = scalar.val[1];
	float imgChannel3 = scalar.val[2];
	//float imgChannel4 = scalar.val[3];
	//qDebug() << "--4.method mean picture Light : " << QString::number(picMeanLight, 10, 4);
	float imgLight = (imgChannel1 + imgChannel2 + imgChannel3) / 3;
    
    cout << "222 imgLight == " << imgLight << endl;
    imshow("滑动条改变图像亮度", img2); 
}

1.3 运行结果

打印图片亮度时,随着滑动条的值增大而增大

2. 鼠标响应

有时,我们需要在图像中标记出重要的区域,这时通过鼠标可以很好地完成这项任务,因此,OpenCV 4 中也提供了鼠标响应相关函数 setMouseCallback()。

2.1 setMouseCallback()函数原型

void cv::setMouseCallback(const String & winname, 
MouseCallback onMouse, 
void * userdata = 0 
)

• winname :添加鼠标响应的窗口的名字。
• onMouse :鼠标响应的回调函数。
• userdata :传递给回调函数的可选参数。
该函数能够为指定的图像窗口创建鼠标响应。该函数第一个参数是需要创建鼠标响应的图像
窗口的名字。第二个参数为鼠标响应的回调函数,该函数在鼠标状态发生改变时被调用,是一个
MouseCallback 类型的函数。最后一个参数是传递给回调函数的可选参数,一般情况下,使用默认
值 0 即可。

2.2 MouseCallback 类型原型

typedef void(* cv::MouseCallback)(int event, 
int x, 
int y, 
int flags, 
void *userdata 
)

• event :鼠标响应事件,参数为 EVENT_* 形式 。
• x :鼠标指针在图像坐标系中的 x 坐标。
• y :鼠标指针在图像坐标系中的 y 坐标。
• flags :鼠标响应标志,参数为 EVENT_FLAG_* 形式。
• userdata :传递给回调函数的可选参数。
MouseCallback 类型的回调函数是一个无返回值的函数,函数名可以任意设置,有 5 个参数,在鼠标状态发生改变的时候被调用。该函数第一个参数是鼠标响应事件标志。第二个参数和第三个参数分别是鼠标当前位置在图像坐标系中的 x 坐标和 y 坐标。第四个参数是鼠标响应标志。最
后一个参数是传递给回调函数的可选参数,一般情况下,使用 void*默认即可。
鼠标响应事件标志可选参数及含

鼠标响应标志及含义


简单来说,鼠标响应就是当鼠标位于对应的图像窗口内时,时刻检测鼠标状态,当鼠标状态发生改变时,调用回调函数,并根据回调函数中的判断逻辑选择执行相应的操作。例如,回调函数中只处理鼠标左键按下的事件,即判断 event 标志是否为 EVENT_LBUTTONDOWN ,只有当
event==EVENT_ LBUTTONDOWN 时,才有相应的逻辑操作,否则将不会执行任何操作。
为了了解鼠标响应的使用方法,在示例代码 中给出了绘制鼠标移动轨迹的示例程序。在该程序中,如果鼠标右键被按下,就会提示"点击鼠标左键才可以绘制轨迹",若单击左键,就会输出当前鼠标的坐标,并将该点坐标定义为某段轨迹的起始位置。之后按住左键移动鼠标,会进入到第三个逻辑判断,绘制鼠标的移动轨迹。 在该示例程序中,提供了两种绘制轨迹的方法,第一种是每次调用回调函数获得鼠标位置时更改周围的图像像素值,这种方式比较直观,但是,由于回调函数有一定的执行时间,因此,当鼠标移动较快时,绘制的图像轨迹会出现断点;第二种是在前一时刻和当前时刻鼠标位置间绘制直线,这种方式可以避免因鼠标移动过快而带来轨迹出现断点的问题

2.3 示例代码

#include <opencv2/opencv.hpp> 
#include <iostream> 

using namespace std; 
using namespace cv; 

Mat img,imgPoint; //全局的图像
Point prePoint; //前一时刻鼠标的坐标,用于绘制直线
void mouse(int event, int x, int y, int flags, void*); 
 
int main() 
{ 
    img = imread("../pic/gril.jpg"); 
    if (!img.data) 
    { 
        cout << "请确认输入图像名称是否正确!" << endl;
        return -1; 
    } 
    img.copyTo(imgPoint); 
    imshow("图像窗口 1", img); 
    imshow("图像窗口 2", imgPoint); 
    setMouseCallback("图像窗口 1", mouse,0 ); //鼠标响应
    waitKey(0); 
    return 0; 
} 
 
void mouse(int event, int x, int y, int flags, void*) 
{ 
    if (event == EVENT_RBUTTONDOWN) //单击右键
    { 
        cout << "点击鼠标左键才可以绘制轨迹" << endl; 
    } 
    if (event == EVENT_LBUTTONDOWN) //单击左键,输出坐标
    { 
        prePoint = Point(x, y); 
        cout << "轨迹起始坐标" << prePoint << endl;  
    } 
    
    if (event == EVENT_MOUSEMOVE && (flags & EVENT_FLAG_LBUTTON)) //按住鼠标左键移动
    { 
        //通过改变图像像素显示鼠标移动轨迹
        imgPoint.at<Vec3b>(y, x) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y, x-1) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y, x+1) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255); 
        imgPoint.at<Vec3b>(y+1, x) = Vec3b(0, 0, 255); 
        imshow("图像窗口 2", imgPoint); 
        
        //通过绘制直线显示鼠标移动轨迹
        Point pt(x, y); 
        line(img, prePoint, pt, Scalar(0, 0, 255), 2, 5, 0); 
        prePoint = pt; 
        imshow("图像窗口 1", img); 
    } 
}

2.4 运行结果

相关推荐
Charles Ray20 分钟前
C++学习笔记 —— 内存分配 new
c++·笔记·学习
我要吐泡泡了哦1 小时前
GAMES104:15 游戏引擎的玩法系统基础-学习笔记
笔记·学习·游戏引擎
骑鱼过海的猫1231 小时前
【tomcat】tomcat学习笔记
笔记·学习·tomcat
贾saisai3 小时前
Xilinx系FPGA学习笔记(九)DDR3学习
笔记·学习·fpga开发
北岛寒沫3 小时前
JavaScript(JS)学习笔记 1(简单介绍 注释和输入输出语句 变量 数据类型 运算符 流程控制 数组)
javascript·笔记·学习
铁匠匠匠5 小时前
从零开始学数据结构系列之第六章《排序简介》
c语言·数据结构·经验分享·笔记·学习·开源·课程设计
架构文摘JGWZ6 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
小齿轮lsl6 小时前
PFC理论基础与Matlab仿真模型学习笔记(1)--PFC电路概述
笔记·学习·matlab
Aic山鱼7 小时前
【如何高效学习数据结构:构建编程的坚实基石】
数据结构·学习·算法
qq11561487077 小时前
Java学习第八天
学习