Opencv学习-窗口交互

news2024/11/14 15:11:30

        交互操作能够增加用户对程序流程的控制,使程序可以根据用户需求实现不同的处理结果。有时某一个参数需要反复尝试不同的数值,这时交互操作可以实现在程序运行过程中改变参数数值的作用,避免重复运行程序,节省时间,同时能够增强结果的对比效果。本文将介绍 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 运行结果 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1990532.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI智能名片小程序源码在付费媒体与企业营销策略中的创新应用

摘要&#xff1a;在数字化浪潮的推动下&#xff0c;付费媒体已成为企业营销战略的核心组成部分。从视频流媒体平台到社交媒体&#xff0c;企业需不断探索新颖且高效的营销手段以吸引目标受众并促进业务增长。AI智能名片小程序源码&#xff0c;作为融合人工智能、小程序技术与传…

什么是实时数据仓库?它有哪些不可替代之处?

【实时数据仓库】可以分开来理解&#xff1a; ✅【实时数据】&#xff1a;即能够快速处理数据&#xff0c;且几乎无延迟的提供最新的数据的能力。 ✅【仓库管理】&#xff1a;可以理解为对仓库的库存控制、对仓库的存储优化以及协调物流。 那么实时数据仓库就是&#xff1a;…

Windows 找不到音频输入/输出设备

0.具体现象 系统音量为0&#xff0c;且无法通过任何方式调整。音量表示非静音表示&#xff0c;而是下图这个叉。 1. 解决方案 以Windows 11电脑为例&#xff0c;从搜索框输入【设备管理器】&#xff0c;进入界面如下&#xff1a; 发现一个感叹号的设备【Intel High Defini…

对象与类的一些基本定义与例子(C++)

1.例题要求&#xff0c;设计一个学生类&#xff0c;实例化对象&#xff0c;并且进行赋值 我们通过创建学生的类&#xff0c;给予了学生名字和年龄的两个属性&#xff0c;我们在main函数中创建了学生类的对象s1,通过s1调用了学生类的属性并给予赋值&#xff0c;最后调用学生类打…

Nginx统计PV、NV

目录 PV 分析 UV 分析 在 Nginx 的配置文件中设置 access_log 主要涉及到以下几个方面&#xff1a; 以下是一个基本的 access_log 配置示例&#xff1a; 使用命令行工具对日志进行基本分析&#xff1a; 后端记录 对于 nginx 的 access.log 日志&#xff0c;我们可以根据日…

进程的等待(非阻塞轮询+阻塞)和替换控制详解

引言 在Linux系统中&#xff0c;进程管理是核心功能之一。理解进程的创建、执行和终止是系统编程中的基础。本文将深入探讨Linux中的进程控制机制&#xff0c;包括进程的生命周期、父子进程的交互、以及进程状态的管理 1. 进程创建&#xff1a;fork()函数 在Linux操作系统中…

Prompt万能框架与常用评估指标

引言 在人工智能的飞速发展中&#xff0c;大型语言模型&#xff08;LLM&#xff09;已成为研究和应用的热点。LLM以其强大的语言理解和生成能力&#xff0c;在诸如自然语言处理、文本生成、问答系统等多个领域展现出巨大潜力。然而&#xff0c;要充分发挥LLM的能力&#xff0c…

NSSCTF练习记录:[SWPUCTF 2021 新生赛]caidao

题目&#xff1a; 图片上给出了代码&#xff0c;是php的一句话木马 eval($_POST[wllm]);符号 符号表示后面的语句即使执行错误&#xff0c;也不报错。 eval()函数 eval()函数的作用是把括号内的字符串全部当作php代码来执行。 ** P O S T [ ′ w l l m ′ ] ∗ ∗ p o s t …

一起学习LeetCode热题100道(30/100)

30.两两交换链表中的节点(学习) 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 示例 1&#xff1a; 输入&#xff1a;head …

【LVS】调度算法概念

fd相当于静态 ovf相当于动态

界面控件DevExpress WinForms,支持HTML CSS提升用户体验(二)

DevExpress WinForms现在可以利用HTML/CSS强大的功能&#xff0c;帮助受DevExpress驱动的WinForms应用程序引入现代的UI元素和用户体验&#xff01; P.S&#xff1a;DevExpress WinForms拥有180组件和UI库&#xff0c;能为Windows Forms平台创建具有影响力的业务解决方案。Dev…

室内定位导航技术:蓝牙信号强度(RSSI)与三角定位算法应用

在数字化时代&#xff0c;位置服务已成为连接物理世界与数字世界的桥梁。在室内环境中&#xff0c;由于GPS信号受建筑物遮挡而失效&#xff0c;传统的室外定位技术难以满足需求&#xff0c;无法精准指引我们在商场、机场、医院等庞大而复杂的建筑内部寻路。室内定位导航技术不仅…

在 Manim 中,kwargs 传递的关键字参数

在 Manim 中&#xff0c;kwargs 用于传递关键字参数&#xff0c;常用于构造对象时。这里的关键参数有那些。我都要&#xff0c;给我整理一下 Sider Fusion 在 Manim 中&#xff0c;kwargs 允许您传递可选的关键字参数来控制动画、对象的外观和行为。尽管具体可用的参数会因对象…

【开端】通过Java 过滤器灵活配置URL访问权限,并返回403

一、绪论 在JAVA项目系统中&#xff0c;后端给前端提供接口。但是在某些场景我们需要临时控制接口是否能被访问。或关闭某一接口的访问权限。 比如某一接口被攻击了或者某一接口存在漏洞&#xff0c;在系统不关闭的情况下&#xff0c;如何控制系统的访问权限。 二、控制接口访…

CVE-2017-15715~Apache解析漏洞【春秋云境靶场渗透】

Apache解析漏洞 漏洞原理 # Apache HTTPD 支持一个文件拥有多个后缀&#xff0c;并为不同后缀执行不同的指令。比如如下配置文件&#xff1a; AddType text/html .html AddLanguage zh-CN .cn# 其给 .html 后缀增加了 media-type &#xff0c;值为 text/html &#xff1b;给 …

【案例35】销售订单公式问题导致系统宕机

问题现象 经过顾问反馈&#xff0c;发现系统现在出现卡顿&#xff0c;NCC一直在转圈。 问题分析 远程排查&#xff0c;发现在服务器从机上defalut-7发生了内存溢出&#xff0c;宕机。 生成了宕机日志。分析结果如下&#xff1a; 销售订单相关操作&#xff0c;vo太多了导致…

Java stream流支持多字段排序

背景 对于排序而言,比较常见的场景是前端传递所需的排序字段名和排序方向,然后通过stream流或者数据库来实现排序. 为动态接收参数,继承Map来支持多字段传入.另外stream流原生的sorted写起来相对比较繁琐,通过compartor方法封装构建多字段排序的逻辑.具体就是通过反射拿到对应…

怎么在 React Native 应用中处理深度链接?

深度链接是一种技术&#xff0c;其中给定的 URL 或资源用于在移动设备上打开特定页面或屏幕。因此&#xff0c;深度链接可以引导用户到应用程序内的特定屏幕&#xff0c;而不仅仅是启动移动设备上的应用程序&#xff0c;从而提供更好的用户体验。这个特定的屏幕可能位于一系列层…

docker 好用的加速器

cd /etc/docker vi daemon.json { "registry-mirrors":["https://docker.rainbond.cc"] }

SpringIOC整合dbUtil做的增删改查以及转账业务的实现

目录 一、xml方式实现 1.介绍lombok插件 2.功能 3.步骤 3.1 idea安装插件(只做一次) 3.2 添加坐标 3.3 编写注解 4.核心类 4.1 QueryRunner 4.2 query() 查询 4.3 update() 增删改 5.配置文件applicationContext.xml 6.junit测试 6.1使用步骤 6.1.1 坐标 6.1.2…