【学习记录】OpenCV鼠标事件

写在前面:本博客仅作记录学习之用,部分图片来自网络,如需使用请注明出处,同时如有侵犯您的权益,请联系删除!

前言

本博客仅为学习记录之用,目的在于后续若需要相关的有资可查。如有错误,欢迎指出交流学习!本博客包含鼠标事件的应用: 鼠标事件的应用;图片动态局部放大;动态绘制矩形框用以放大;cv2多行文本的显示。


本文动机

对于为什么要局部放大?可参考【链接】,在实际的实现上往往是需要期望放大位置的具体坐标,然而对于使用者而言,需要根据大概的位置不断调整坐标才能达到期望的位置,太繁琐了(切身体会过,害)。因此通过动态的框选位置以及动态的放大,结合上述链接方法,可极大的提高效率。


鼠标事件

函数

如何获取鼠标事件,cv2采取一下函数,针对不同的窗口,传入对应参数在回调函数中实现对鼠标事件的响应。
cv2.setMouseCallback(windowName, onMouse, param=None) 第一个参数是窗口名,第二个参数是回调函数(event, x, y, flags, param),以及传入回调函数的参数。

事件

指定event=='下列事件’或者等于事件的标号即可,获得该事件发生的信号后,可对窗口进行响应。

EVENT_LBUTTONDBLCLK = 7 左键双击
EVENT_LBUTTONDOWN = 1 左键击下
EVENT_LBUTTONUP = 4 左键弹起
EVENT_MBUTTONDBLCLK = 9 中键双击
EVENT_MBUTTONDOWN = 3 中键击下
EVENT_MBUTTONUP = 6 中键弹起
EVENT_MOUSEMOVE = 0 鼠标移动
EVENT_MOUSEWHEEL = 10 滚动条向上
EVENT_RBUTTONDBLCLK = 8 右键双击
EVENT_RBUTTONDOWN = 2 右键击下
EVENT_RBUTTONUP = 5 右键弹起

Flags

指定flags=='下列标志’或者等于标志的标号即可,获得该事件发生的信号后,可对窗口进行响应。
EVENT_FLAG_ALTKEY = 32 摁住Alt
EVENT_FLAG_CTRLKEY = 8 摁住Ctrl
EVENT_FLAG_LBUTTON = 1 摁住左键
EVENT_FLAG_MBUTTON = 4 摁住中键
EVENT_FLAG_RBUTTON = 2 摁住右键
EVENT_FLAG_SHIFTKEY = 16 摁住Shift

动态显示

利用cv2显示的关键在于如何刷新窗口,显示的图片后,对其操作之后,使得该图片无法后续使用。简单的方法就是重载图像进行初始化,在继续对其操作。亦可以将操作产生的影响进行消除,如框文本之类的。
image_new = image_origin
imshow(‘窗口名’,image_new )
直接将原图(image_origin)进行显示,这样操作会污染image_origin,导致后续无法继续使用image_origin。

added = image_now-image_origin
image_new = image_now - add
本质是显示image_origin,但是这样操作不会污染image_origin,后续仍旧可以对image_origin进行操作。

回调函数

函数名字(event, x, y, flags, param),依次表示事件,鼠标坐标,标志,回调参数。
需要注意的是事件的并发,如双击事件发生同时单击事件也发生,鼠标移动和其他事件同时发生,如果这些事件都需要响应,为避免事件的干扰,建议使用标志位,进行限制。

回调参数和寻常函数的参数一样,在刷新窗口时,可传入需要处理的图像或者原图,标志位,进行适当组合进行响应

多行文本显示

cv2.putText(image, text, 起始坐标, 字体,大小, 颜色(0, 0, 255), 粗细)

对于太长的文本,很可能超出窗口大小,进而显示不全。在进行提示或者写文本内容时,有必要实现多行显示文本。
使用retval, baseLine = cv2.getTextSize(text, fontFace, fontScale, thickness)得到字体的宽高 (width, height),基线是相对于最底端文本的 y 坐标,文本的高是从baseLine到文本最顶端,结合换行符进行换行。

	note = 'Please frame the area from\nleft to right and\nfrom top to bottom.'
                text_line = note.split("\n")
                fontScale = 0.4
                thickness = 1
                fontFace = FONT_HERSHEY_COMPLEX_SMALL
                text_size, baseline = getTextSize(str(text_line), fontFace, fontScale, thickness)
                for i, text in enumerate(text_line):
                    if text:
                        draw_point = [img.shape[0]//8, img.shape[0]//4 + (text_size[1] + 10 + baseline) * i]
                        putText(img, text, draw_point, fontFace, 1.0, (255, 0, 0), thickness=thickness)


程序代码

思路简单,首先框选需要关注的部分,确定起点和终点坐标绘制矩形;获取鼠标的坐标,在其周围绘制设定大小的范围,获取该区域进行插值放大指定倍数,使用新窗口进行展示。根据框的坐标将指定区域放大后置于图像内部并保存。


#!/usr/bin/env python
# -*- coding: UTF-8 -*-
'''
@File    :bb.py
@Author  :Xiaodong
@Function: 左键实时绘制矩形框及坐标;右键撤销上一个框;中键滚动后,移动鼠标动态放大光标处;点击中键退出
@Date    :2022/10/23 16:49
'''

from cv2 import EVENT_LBUTTONDOWN, EVENT_FLAG_LBUTTON, EVENT_LBUTTONUP, EVENT_RBUTTONDOWN, EVENT_MOUSEWHEEL, EVENT_MOUSEMOVE, EVENT_MBUTTONDOWN
from cv2 import namedWindow, destroyWindow, destroyAllWindows, setMouseCallback, FONT_HERSHEY_COMPLEX_SMALL
from cv2 import imread, imshow, waitKey, resize, rectangle, circle, putText, getTextSize, INTER_CUBIC, cvtColor, COLOR_BGR2RGB

from copy import deepcopy
from os import listdir, makedirs
from os.path import join as opjoin

from matplotlib.pyplot import figure, axis, xticks, yticks, savefig
from matplotlib.pyplot import imshow as pimshow

from tkinter import Tk
from tkinter.filedialog import askdirectory


def Partial_magnification(pic, target, location='lower_right', ratio=1):
    '''
    :param pic: input pic
    :param target: Intercept area, for example [target_x, target_y, target_w, target_h]
    :param location: lower_right,lower_left,top_right,top_left,center
    :param ratio: gain
    :return: oringal pic, pic
    '''

    w, h = pic.shape[1], pic.shape[0],

    target_x, target_y = target[0], target[1]
    target_w, target_h = target[2], target[3]
    rectangle(pic, (target_x, target_y), (target_x + target_w, target_y + target_h), (0, 255, 0), 2)
    new_pic = pic[target_y:target_y + target_h, target_x:target_x + target_w]
    new_pic = resize(new_pic, (target_w*ratio, target_h*ratio), interpolation=INTER_CUBIC)
    if location == 'lower_right':
        pic[h-1-target_h*ratio:h-1, w-1-target_w*ratio:w-1] = new_pic
        # line(pic, (target_x + target_w, target_y + target_h), (w-1-target_w*ratio, h-1-target_h*ratio), (255, 0, 0),2)
    elif location == 'lower_left':
        pic[h-1-target_h*ratio:h-1, 0:target_w*ratio] = new_pic
    elif location == 'top_right':
        pic[0:target_h*ratio, w-1-target_w*ratio:w-1] = new_pic
    elif location == 'top_left':
        pic[0:target_h*ratio, 0:target_w*ratio] = new_pic
    elif location == 'center':
        pic[int(h/2-target_h*ratio/2):int(h/2+target_h*ratio/2),
            int(w/2-target_w*ratio/2):int(w/2+target_w*ratio/2)] = new_pic
    return img, pic


def on_EVENT_LBUTTONDOWN(event, x, y, flags, param):  # 鼠标事件函数
    img = param[0]
    flag = param[1]-1
    if event == EVENT_LBUTTONDOWN:  # 左键按下,获得起始点坐标
        try:
            destroyWindow('bpart')
        except:
            pass
        setMouseCallback("image", on_EVENT_LBUTTONDOWN, param=[img, 1])
        point.append((x, y))
        target.append((x, y))
    elif flags == EVENT_FLAG_LBUTTON:  # 左键长按,实时显示框与坐标
        point.append((x, y))
        for i in range(0, len(point), 2):
            rectangle(img, point[i], point[i+1], (0, 255, 0), thickness=1, lineType=8, shift=0)
            circle(img, target[i], 1, (255, 0, 0), thickness=-1)
            circle(img, point[i], 1, (255, 0, 0), thickness=-1)
            putText(img, str(point[i]), (point[i][0], point[i][1]), FONT_HERSHEY_COMPLEX_SMALL, 0.8, (0, 0, 0), thickness=1)
            putText(img, str(point[i+1]), point[i+1], FONT_HERSHEY_COMPLEX_SMALL, 0.8, (255, 0, 0), thickness=1)
        imshow("image", img)
        point.pop(-1)
        added = img - copyImg
        setMouseCallback("image", on_EVENT_LBUTTONDOWN, param=[img - added, 1])
    elif event == EVENT_LBUTTONUP:  # 左键弹起,保存坐标
        point.append((x, y))
        target.append((x, y))
        for i in range(0, len(target), 2):
            rectangle(img, point[i], point[i+1], (0, 255, 0), thickness=1, lineType=8, shift=0)
            circle(img, point[i], 1, (255, 0, 0), thickness=-1)
            putText(img, str(point[i]), (point[i][0], point[i][1]), FONT_HERSHEY_COMPLEX_SMALL, 0.8, (0, 0, 0), thickness=1)
            putText(img, str(point[i+1]), point[i+1], FONT_HERSHEY_COMPLEX_SMALL, 0.8, (255, 0, 0), thickness=1)
        imshow("image", img)
    elif event == EVENT_RBUTTONDOWN:  # 右键按下,撤销前一个框的数据
        try:
            destroyWindow('bpart')
        except:
            pass
        added = img - copyImg
        img = img - added
        if len(target) > 0:
            for _ in range(2):
                target.pop(-1)
                point.pop(-1)
        for i in range(0, len(target), 2):
            rectangle(img, point[i], point[i+1], (0, 255, 0), thickness=1, lineType=8, shift=0)
            circle(img, point[i], 1, (255, 0, 0), thickness=-1)
            putText(img, str(point[i]), (point[i][0], point[i][1]), FONT_HERSHEY_COMPLEX_SMALL, 0.8, (0, 0, 0), thickness=1)
            putText(img, str(point[i+1]), point[i+1], FONT_HERSHEY_COMPLEX_SMALL, 0.8, (255, 0, 0), thickness=1)
        imshow("image", img)
        setMouseCallback("image", on_EVENT_LBUTTONDOWN, param=[img - added, 1])
    elif event == EVENT_MOUSEWHEEL:  # 中键前滚,标志位置1
        added = img - copyImg
        imshow("image", img - added)
        setMouseCallback("image", on_EVENT_LBUTTONDOWN, param=[img - added, 0])
    elif event == EVENT_MOUSEMOVE and flag:  # 标志位置为1时候,动态放大
        cop = deepcopy(copyImg)
        shape = cop.shape
        w = 30
        x1 = 0 if x-w < 0 else x-w
        y1 = 0 if y-w < 0 else y-w
        x2 = x + w if x + w < shape[1] else shape[1]
        y2 = y + w if y + w < shape[0] else shape[0]
        if x1 == 0: x2 = 2*w
        if y1 == 0: y2 = 2*w
        if x2 == shape[1]: x1 = x2-2*w
        if y2 == shape[0]: y1 = y2-2*w
        rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), thickness=1, lineType=8, shift=0)
        part = cop[y1:y2, x1:x2]
        bpart = resize(part, (10*w, 10*w))
        imshow("image", img)
        imshow("bpart", bpart)
        added = img - copyImg
        point.clear()
        target.clear()
        setMouseCallback("image", on_EVENT_LBUTTONDOWN, param=[img - added, 0])
    elif event == EVENT_MBUTTONDOWN:  # 中键按下,退出
        destroyAllWindows()


if __name__ == '__main__':
    root = Tk()
    root.withdraw()
    pics_path = askdirectory()  # 获取所选文件夹路径
    save_path = './'  # 保存在当前目录下

    if pics_path is not None:
        pics = listdir(pics_path)
        for idx, ppic in enumerate(pics):
            point = []
            target = []
            path = opjoin(pics_path, ppic)
            img = imread(path)
            if max(img.shape[0], img.shape[1]) > 800:
                img = resize(img, (800, 800*img.shape[0]//img.shape[1]))
            if img is not None:
                copyImg = deepcopy(img)
                namedWindow("image")
                if idx == 0:
                    note = 'Please frame the area from\nleft to right and\nfrom top to bottom.'
                    text_line = note.split("\n")
                    fontScale = 0.4
                    thickness = 1
                    fontFace = FONT_HERSHEY_COMPLEX_SMALL
                    text_size, baseline = getTextSize(str(text_line), fontFace, fontScale, thickness)
                    for i, text in enumerate(text_line):
                        if text:
                            draw_point = [img.shape[0]//8, img.shape[0]//4 + (text_size[1] + 10 + baseline) * i]
                            putText(img, text, tuple(draw_point), fontFace, 1.0, (255, 0, 0), thickness=thickness)
                            imshow("image", img)
                            waitKey(666)

                setMouseCallback("image", on_EVENT_LBUTTONDOWN, param=[img-(img-copyImg), 1])
                imshow("image", img)
                waitKey(0)
                destroyAllWindows()

                location = ['top_left', 'top_right', 'lower_right', 'lower_left', 'center']
                img = imread(path)
                if max(img.shape[0], img.shape[1]) > 800:
                    img = resize(img, (800, 800 * img.shape[0] // img.shape[1]))

                for i in range(len(target)//2):
                    if target[2*i+1][0]-target[2*i][0] > 0:
                        target1 = [target[2*i][0], target[2*i][1], target[2*i+1][0]-target[2*i][0], target[2*i+1][1]-target[2*i][1]]
                    else:
                        raise ValueError('请从左往右,从下往上 框取区域')
                    if i == 0:
                        pic, pic1 = Partial_magnification(img, target1, location=location[i], ratio=2)
                    else:
                        pic, pic1 = Partial_magnification(pic, target1, location=location[i], ratio=2)
                try:
                    fig = figure(figsize=(5, 5))  # figsize 尺寸
                    axis('off')  # 去坐标轴
                    xticks([])  # 去 x 轴刻度
                    yticks([])  # 去 y 轴刻度
                    pimshow(cvtColor(pic, COLOR_BGR2RGB))
                    makedirs(opjoin(save_path, 'save'), exist_ok=True)
                    savefig(opjoin(save_path, 'save', ppic), dpi=300, bbox_inches='tight')  # dpi 分辨率
                    del pic
                except Exception as e:
                    pass

实际效果

在这里插入图片描述
在这里插入图片描述


修改记录

2023.4.20: 添加选择图片路径,以便打包exe. 网盘

总结


以上就是本文的主要内容,主要是OpenCV鼠标事件的基本使用。


致谢

欲尽善本文,因所视短浅,怎奈所书皆是瞽言蒭议。行文至此,诚向予助与余者致以谢意

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值