计算机视觉OpenCv学习系列:第四部分、键盘+鼠标响应操作

news2024/11/22 14:45:36

第四部分、键盘+鼠标响应操作

    • 第一节、键盘响应操作
      • 1.键盘响应事件
      • 2.键盘响应
      • 3.代码练习与测试
    • 第二节、鼠标操作与响应
      • 1.鼠标事件与回调
      • 2.鼠标操作
      • 3.代码练习与测试
    • 学习参考

第一节、键盘响应操作

键盘响应中有一个函数叫做waitKey,所有的获取键盘键值都是通过waitKey函数实现的。

1.键盘响应事件


  • cv.waitKey( [, delay] ) --> retval
  • delay如果没有声明或者delay=0,表示一直阻塞
  • delay大于0,表示阻塞指定毫秒数
  • retval返回的对应键盘键值,注意:在不同的操作系统中可能会有差异!
  • 典型的retval = 27是ESC按键(windows环境下)

用户通过敲击键盘,操作系统会返回键值给各个应用程序,当返回键值给OpenCV的时候,如果我们有自己定义的接收返回值的方法,就会按照我们定义的方法对返回值进行处理。

键盘属于外部设备,由操作系统负责监听响应,当键盘发出响应被操作系统接收后,操作系统根据发出注册的应用程序返回接收到的键盘值。

2.键盘响应


  • 检查返回键值,根据不同键值完成不同操作
  • 推荐使用if-elif-else, switch-case方式python3.10支持
# 例如借助if-elif-else来处理我们需要的键值
if <expr>:
    <statement(s)>
elif <expr>:
    <statement(s)>
elif <expr>:
    <statement(s)>
    ...
else:
    <statement(s)>

3.代码练习与测试


例如我们可以按照如下设定:

  • 按ESC退出程序
  • 按1显示HSV图像
  • 按2显示YCrCb图像
  • 按3显示RGB图像
  • 按0恢复原图BGR显示
def keyboard_demo():
    image = cv.imread(r"F:\python\opencv-4.x\samples\data\butterfly.jpg")
    cv.namedWindow("keyboard_demo", cv.WINDOW_AUTOSIZE)
    cv.imshow("keyboard_demo", image)
    while True:
        c = cv.waitKey(10)  # 停顿10ms
        # ESC
        if c == 27:
            break
        # key = 0
        elif c == 48:
            cv.imshow("keyboard_demo", image)
        # key = 1
        elif c == 49:
            hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV)
            cv.imshow("keyboard_demo", hsv)
        # key = 2
        elif c == 50:
            ycrcb = cv.cvtColor(image, cv.COLOR_BGR2YCrCb)
            cv.imshow("keyboard_demo", ycrcb)
        # key = 3
        elif c == 51:
            rgb = cv.cvtColor(image, cv.COLOR_BGR2RGB)
            cv.imshow("keyboard_demo", rgb)
        else:
            if c != -1:
                print("Key: ", c, "is not define.")
    cv.waitKey(0)
    cv.destroyAllWindows()

结果示例:

从左往右依次是BGR/HSV/YCrCb/RGB:

在这里插入图片描述

如果键入了没有定义的键值就会按照else中写的返回一个结果如下图所示:

在这里插入图片描述

第二节、鼠标操作与响应

1.鼠标事件与回调


和键盘一样,鼠标属于外部设备,由操作系统负责监听响应,当鼠标发出响应被操作系统接收后,操作系统根据发出注册的应用程序返回接收到的鼠标值。

注册与回调过程非常类似滚动条的操作。

在这里插入图片描述

鼠标支持的事件:

  • 左键操作:

在这里插入图片描述

  • 右键操作:

在这里插入图片描述

  • 中键+滚轮操作:

在这里插入图片描述

1.EVENT_LBUTTONDOWN
2.EVENT_MOUSEMOVE
3.EVENT_LBUTTONUP
# 以上是三个非常重要的鼠标事件,这三个动作要在一套动作中完成
鼠标左键按下、鼠标移动、鼠标左键弹起

2.鼠标操作


# 鼠标回调函数
1. cv2.setMouseCallback(windowName, onMouse, param=None) 
# windowName:窗口名称
# onMouse:鼠标响应函数
# param:响应函数传递的的参数

# 鼠标响应函数
2. onMouse(event, x, y, flags, param)
# event:鼠标事件,可用参数对应值代替
# x:鼠标x坐标
# y:鼠标y坐标
# flags:鼠标状态,可用参数对应值代替
# param:param是用户定义的传递到setMouseCallback函数调用的参数

3.代码练习与测试


绘制矩形:

在这里插入图片描述

  • 鼠标绘制第一步,reset之前的绘制
  • 记录鼠标按下位置(EVENT_LBUTTONDOWN)
  • 记录鼠标移动位置,并绘制矩形(EVENT_MOUSEMOVE)
  • 抬起,记录位置,绘制最终矩形大小(EVENT_LBUTTONUP)

鼠标绘制矩形:

# 鼠标操作绘制矩形
b1 = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
img = np.copy(b1)
# (x1, y1)表示左上角,(x2, y2)表示右下角点
x1 = -1
x2 = -1
y1 = -1
y2 = -1


# 定义绘制矩形的注册函数
def mouse_drawing_rectangle(event, x, y, flags, parm):
    # 全局参数
    global x1, y1, x2, y2
    # 鼠标放下,赋值左上角点给x1,y1
    if event == cv.EVENT_LBUTTONDOWN:
        x1 = x
        y1 = y
    # 鼠标移动
    if event == cv.EVENT_MOUSEMOVE:
        # x1,y1初始值都是-1,如果移动过程<0说明鼠标没有摁下
        if x1 < 0 or y1 < 0:
            return
        x2 = x
        y2 = y
        dx = x2 - x1
        dy = y2 - y1
        # 移动有一定距离才会绘制
        if dx > 0 and dy > 0:
            # 矩形绘制到b1(读入的图片)上
            # img是原图
            b1[:, :, :] = img[:, :, :]  # 用原图覆盖擦除之前的绘制结果
            cv.putText(b1, "searching...", (x1, y1-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 255), 2)
            cv.rectangle(b1, (x1, y1), (x2, y2), (255, 0, 255), 2, 8, 0)  # 移动过程中用紫色线
    if event == cv.EVENT_LBUTTONUP:
        x2 = x
        y2 = y
        dx = x2 - x1
        dy = y2 - y1
        if dx > 0 and dy > 0:
            # 矩形绘制到b1(读入的图片)上
            # img是原图
            b1[:, :, :] = img[:, :, :]  # 用原图覆盖擦除之前的绘制结果
            cv.putText(b1, "Moon", (x1, y1-5), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
            cv.rectangle(b1, (x1, y1), (x2, y2), (0, 0, 255), 2, 8, 0)  # 鼠标抬起之后用红色线
        # 重新赋值为下一次绘制做准备
        x1 = -1
        y1 = -1
        x2 = -1
        y2 = -1
        
def mouse_demo():
    cv.namedWindow("mouse_demo", cv.WINDOW_AUTOSIZE)
    # 实时关注mouse_demo画布上的响应,如果发生mouse_drawing中定义的事件,就返回响应
    cv.setMouseCallback("mouse_demo", mouse_drawing_rectangle)  # 绘制矩形
    while True:
        cv.imshow("mouse_demo", b1)  # 绘制矩形
        # 每过10ms就获取一次键盘键值,默认是-1,ESC键是27
        c = cv.waitKey(10)
        if c == 27:
            break
    cv.destroyAllWindows()

结果示例:

在这里插入图片描述

鼠标绘制圆形:

# 鼠标操作绘制圆形
b2 = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
img2 = np.copy(b2)
# (c1, c2)表示圆心坐标,r1表示半径
c1 = -1
c2 = -1


# 定义圆形的注册函数
def mouse_drawing_circle(event, x, y, flags, parm):
    # 全局参数
    global c1, c2, r1
    # 鼠标放下,赋值左上角点给x1,y1
    if event == cv.EVENT_LBUTTONDOWN:
        c1 = x
        c2 = y
    # 鼠标移动
    if event == cv.EVENT_MOUSEMOVE:
        # c1,c2初始值都是-1,如果移动过程<0说明鼠标没有摁下
        if c1 < 0 or c2 < 0:
            return
        dr = int(math.sqrt(pow((x-c1), 2) + pow((y-c2), 2)))
        # 移动有一定距离才会绘制
        if dr > 0:
            # 圆形绘制到b1(读入的图片)上
            # img是原图
            b2[:, :, :] = img2[:, :, :]  # 用原图覆盖擦除之前的绘制结果
            cv.circle(b2, (c1, c2), dr, (255, 0, 255), 2, cv.LINE_8)  # 移动过程中用紫色线
    if event == cv.EVENT_LBUTTONUP:
        dr = int(math.sqrt(pow((x - c1), 2) + pow((y - c2), 2)))
        if dr > 0:
            # 圆形绘制到b1(读入的图片)上
            # img是原图
            b2[:, :, :] = img2[:, :, :]  # 用原图覆盖擦除之前的绘制结果
            cv.circle(b2, (c1, c2), dr, (0, 0, 255), 2, cv.LINE_8)  # 移动过程中用红色线
        # 重新赋值为下一次绘制做准备
        c1 = -1
        c2 = -1
        
def mouse_demo():
    cv.namedWindow("mouse_demo", cv.WINDOW_AUTOSIZE)
    # 实时关注mouse_demo画布上的响应,如果发生mouse_drawing中定义的事件,就返回响应
    cv.setMouseCallback("mouse_demo", mouse_drawing_circle)  # 绘制圆形
    while True:
        cv.imshow("mouse_demo", b2)  # 绘制圆形
        # 每过10ms就获取一次键盘键值,默认是-1,ESC键是27
        c = cv.waitKey(10)
        if c == 27:
            break
    cv.destroyAllWindows()

结果示例:

在这里插入图片描述
其他有趣的实现

同时还可以实现一些有意思的功能比如实现实时响应截取以及类似qq截图的功能的,只有截图部分是亮的,其余部分是暗的。

下面代码我是单独写在一个包里面的,如果需要的话可以把他们在上面的类中调用自己试一试也是很有趣的。

import cv2 as cv
import numpy as np


# 图片区域显示
def rectangle_space(img, x1, x2, y1, y2):
    # img = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
    # cv.imshow("img", img)
    # img2 = img[0:256, 256:512, 1:2]  # 第一个0:256表示高度所在位置,第二个0:256表示宽度所在位置,第三个1:2表示输出通道数
    # cv.imshow("rectangle_space", img2)
    cv.imshow("rectangle_space", img[y1:y2, x1:x2, 0:3])

    cv.waitKey(1)


# 除了截图部分其余均变暗
def rectangle_dark(img, x1, x2, y1, y2):
    # img = cv.imread(r"F:\python\opencv-4.x\samples\data\starry_night.jpg")
    # cv.imshow("img", img)
    # img2 = np.zeros_like(img)
    # img2[:, :, :] = (np.uint8(60), np.uint8(60), np.uint8(60))
    # img2[0:256, 256:512, :] = 0
    # result = cv.subtract(img, img2)
    # cv.imshow("result", result)
    img2 = np.zeros_like(img)
    img2[:, :, :] = (np.uint8(60), np.uint8(60), np.uint8(60))
    img2[y1:y2, x1:x2, :] = 0
    result = cv.subtract(img, img2)
    cv.imshow("mouse_demo", result)

    cv.waitKey(1000)


# 当前程序执行部分
if __name__ == '__main__':
    print("Hello world.")
    rectangle_dark()

结果示例:

左图为实时截取,右图为类似qq截图的实现

在这里插入图片描述

学习参考

本系列所有OpenCv相关的代码示例和内容均来自博主学习的网站:opencv_course

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

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

相关文章

【经典笔试题】动态内存管理

test1&#xff1a;void GetMemory(char* p) {p (char*)malloc(100); } void Test(void) {char* str NULL;GetMemory(str);strcpy(str, "hello world");printf(str); }int main() {Test();return 0; }请问执行上面代码&#xff0c;会出现什么结果&#xff1f;解析&a…

7. R语言【独立性检验】:卡方独立性检验、Fisher精确检验 、Cochran-Mantel-Haenszel检验

文章目录1. 卡方检验2. 费希尔精确检验&#xff08;Fisher Exact Test&#xff09;3. Cochran-Mantel-Haenszel检验独立性检验&#xff1a;用来判断变量之间相关性的方法&#xff0c;如果两个变量彼此独立&#xff0c;那么两者统计上就是不相关的 1. 卡方检验 可以使用chisq.…

Java面向对象之多态、内部类、常用API

目录面向对象之三大特性之三&#xff1a;多态多态的概述、多态的形式多态的好处多态下引用数据类型的类型转换多态的综合案例内部类内部类概述内部类之一&#xff1a;静态内部类内部类之二&#xff1a;成员内部类内部类之三&#xff1a;局部内部类内部类之四&#xff1a;匿名内…

JavaSE与网络面试题

大佬的&#xff1a; https://github.com/Snailclimb/JavaGuide https://osjobs.net/topk/all/ 自增自减 要点&#xff1a; 赋值 &#xff0c;最后计算 右边的从左到右加载值&#xff0c;一次压入操作数栈 实际先算哪个看运算符的优先级 自增、自减操作都是直接修改变量…

SpringCloud面试题

为什么需要学习Spring Cloud 不论是商业应用还是用户应用&#xff0c;在业务初期都很简单&#xff0c;我们通常会把它实现为单体结构的应用。但是&#xff0c;随着业务逐渐发展&#xff0c;产品思想会变得越来越复杂&#xff0c;单体结构的应用也会越来越复杂。这就会给应用带…

带你走入虚函数和多态的世界(c++)

1、什么是虚函数 C类中用virtual修饰的函数叫做虚函数&#xff0c;构造函数没有虚构造函数&#xff0c;存在虚析构函数&#xff0c;C所有虚函数都是一个指针去存储的&#xff0c;所以具有虚函数的类&#xff0c;内存会增加一个指针大小的内存 #include<iostream> #includ…

第一章:计算机网络概述

一、计算机网络基本概念 1、什么是计算机网路&#xff1f; 计算机网络是通信技术与计算机技术紧密结合的产物。计算机网络就是一种特殊的通信网络&#xff0c;其特别之处就是&#xff0c;其信源和信宿通常就是我们所说的计算机&#xff0c;发出的信息通常就是数字化的一些信息…

数据分析-深度学习 Pytorch Day5

李宏毅《机器学习》第6讲——梯度下降Review: 梯度下降法在回归问题的第三步中&#xff0c;需要解决下面的最优化问题&#xff1a;我们要找一组参数θ &#xff0c;让损失函数越小越好&#xff0c;这个问题可以用梯度下降法解决。假设θ有里面有两个参数θ1,θ2&#xff0c;随机…

FPGA 20个例程篇:19.OV7725摄像头实时采集送HDMI显示(一)

第七章 实战项目提升&#xff0c;完善简历 19.OV7725摄像头实时采集送HDMI显示&#xff08;一&#xff09; 在例程“OV7725摄像头实时采集送HDMI显示”中&#xff0c;我们将走近FPGA图像处理的世界&#xff0c;图像处理、数字信号、高速接口也一直被业界公认为FPGA应用的三大主…

k8s ingress概念和实践

什么是Ingress Ingress 是对集群中服务的外部访问进行管理的 API 对象&#xff0c;典型的访问方式是 HTTP/HTTPS 该特性从1.19版本开始作为stable状态进行发布 Ingress 公开从集群外部到集群内服务的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。 如下…

Python算法:三种简单排序的方法

目录 前言 1、插入排序 实例 2、选择排序 实例 3、冒泡排序 实例 前言 声明&#xff1a;本文所有动图来源为菜鸟教程 &#x1f340;作者简介&#xff1a;被吉师散养、喜欢前端、学过后端、练过CTF、玩过DOS、不喜欢java的不知名学生。 &#x1f341;个人主页&#xff1a;红…

监督学习、半监督学习、无监督学习、自监督学习、强化学习和对比学习

目录 一、监督学习 二、半监督学习 三、无监督学习 3.1.聚类算法 3.2.降维算法 3.3.异常检测 3.4.自动编码器 3.5.生成模型 3.6.关联规则学习 3.7.自组织映射(SOM) 四、自监督学习 4.1. 基于上下文&#xff08;Context based&#xff09; 4.2. 基于时序&#xff08…

85.【Vue-细刷-01】

Vue(一)、利用Vscode开发Vue1.在桌面创建文件夹code2.使用Vscode进行打开这个文件夹3.在Vscode的右键创建文件夹4.右键创建.html文件(二)、第一个Vue程序1.下载并引入Vue.js2.引入vue.js3.创建视图层4.创建Model层5.获取Vue实列中的数据6.效果展示⭐为什么要使用new 关键字来创…

文件操作(二):学习笔记10

目录 一.概念梳理 1.文件的分类 2.程序运行时&#xff0c;内存和外存数据交流的过程 二.文件缓冲区 三.常用的文件读写函数笔记 1.常用格式化文件读写函数 (1)格式化文件写入函数 用fprintf进行文件写入操作&#xff1a; (2)格式化文件读取函数 用fscanf进行文件读取 2…

Java枚举类与注解

目录 一、枚举类的使用 枚举类的实现 枚举类的属性 自定义枚举类 步骤 举例 使用enum定义枚举类 使用说明 举例 Enum类的主要方法 实现接口的枚举类 二、注解的使用 概述 常见的Annotation示例 自定义Annotation&#xff08;参照SupressWarnings 定义&#xff09…

一不小心,登上支付宝开发者社区热文榜单Top3

大家好&#xff0c;我是小悟 那天中午要午休的时候&#xff0c;看到微信通讯录新朋友有个红色1&#xff0c;像俺这种有强迫症的&#xff0c;那不得去把它点掉。打开一看&#xff0c;加好友的备注是“我是熊二&#xff0c;支付宝开发者社区运营”。 收到支付宝社区的运营添加微…

【ROS】dynamic_reconfigure配置详细说明

文章目录 文章目录 前言 一、编写.cfg文件 二、为节点配置dynamic_reconfigure 总结 前言 dynamic_reconfigure配置是ROS中为了方便用户对程序中的参数进行实时调整而推出的工具&#xff0c;配置好自己的dynamic_reconfigure文件后&#xff0c;可以很方便的使用ROS提供的r…

2022-12-18 网工进阶(三十八)MPLS LDP---LDP基础、工作原理(会话、标签的发布和管理、特性)、配置举例

LDP概述 LDP是MPLS的一种控制协议&#xff0c;相当于传统网络中的信令协议&#xff0c;负责FEC的分类、标签的分配以及LSP的建立和维护等操作。LDP规定了标签分发过程中的各种消息以及相关处理过程。 LDP的工作过程主要分为两部分&#xff1a;LSR之间建立LDP会话&#xff1b;…

一种小型项目的超时机制方案

设计背景 在小型项目中&#xff0c;状态机的跳转往往依赖于某个事件的状态&#xff0c;因此监控某个事件是否处于超时状态就至关重要&#xff1b; 注意事项 超时机制应该能够准确的判断出事件是否真正超时&#xff0c;并预留出设置自定义超时处理的接口&#xff1b;超时机制…

什么是大数?大数有什么用?如何使用大数?

0 什么是大数 如果基本的整数和浮点数精度不能够满足需求&#xff0c;那么可以使用 javamath 包中两个很有用的类:BigInteger和 BiDecimal。 这两个类可以处理包含任意长度数字序列的数值。 BigInteger类实现任意精度的整数运算&#xff0c;BigDecimal实现任意精度的浮点数运算…