OpenCV-PyQT项目实战(7)项目案例03:鼠标框选

这篇具有很好参考价值的文章主要介绍了OpenCV-PyQT项目实战(7)项目案例03:鼠标框选。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

欢迎关注『OpenCV-PyQT项目实战 @ Youcans』系列,持续更新中
OpenCV-PyQT项目实战(1)安装与环境配置
OpenCV-PyQT项目实战(2)QtDesigner 和 PyUIC 快速入门
OpenCV-PyQT项目实战(3)信号与槽机制
OpenCV-PyQT项目实战(4)OpenCV 与PyQt的图像转换
OpenCV-PyQT项目实战(5)项目案例01:图像模糊
OpenCV-PyQT项目实战(6)项目案例02:滚动条应用
OpenCV-PyQT项目实战(7)项目案例03:鼠标框选
OpenCV-PyQT项目实战(8)项目案例04:鼠标定位
OpenCV-PyQT项目实战(9)项目案例04:视频播放
OpenCV-PyQT项目实战(10)项目案例06:键盘事件与视频抓拍
OpenCV-PyQT项目实战(11)项目案例07:摄像头操作与拍摄视频


OpenCV-PyQT项目实战(7)项目案例03:鼠标框选

本节介绍OpenCV和PyQt 实现鼠标框选的方法和案例,通过案例学习PyQt中的鼠标动作。


1. OpenCV实现鼠标框选

OpenCV中的函数 cv.selectROI 可以通过鼠标在图像上选择感兴趣的矩形区域(ROI,region of interest)。

函数原型:

cv.selectROI(windowName, img[, showCrosshair=true, fromCenter=false]) → retval

函数cv.selectROI创建一个显示窗口,允许用户使用鼠标选择ROI,按Space或Enter键完成选择,按c键取消选择。

参数说明:

● img:选择矩形区域的图像

● windowName:图像显示窗口的名称

● showCrosshair:默认值true,显示选择矩形的中心十字线

● fromCenter:默认值false,表示鼠标初始位置作为矩形的角点;true表示鼠标初始位置作为矩形的中心点

● retval:返回值为Rect矩形类,格式为元组 (x , y, w, h)

注意问题:

⒈函数的返回值是Rect矩形类,元组 (x , y, w, h) 分别表示矩形左上角顶点坐标 (x,y)、矩形的宽度w和高度h。

⒉函数创建一个窗口设置自己的鼠标回调,完成后将为使用的窗口设置一个空回调。

例程7-1:OpenCV鼠标框选

    # 1.17 图像的裁剪 (ROI)
    img1 = cv2.imread("../images/imgLena.tif", flags=1)  # flags=1 读取彩色图像(BGR)

    roi = cv2.selectROI(img1, showCrosshair=True, fromCenter=False)
    xmin, ymin, w, h = roi  # 矩形裁剪区域 (ymin:ymin+h, xmin:xmin+w) 的位置参数
    imgROI = img1[ymin:ymin+h, xmin:xmin+w].copy()  # 切片获得裁剪后保留的图像区域

    cv2.imshow("DemoRIO", imgROI)
    cv2.waitKey(0)

pyqt5 绘图和鼠标选择,# OpenCV 实战项目,opencv,pyqt,python,GUI,图像处理


总结:OpenCV实现鼠标框选非常简单,但无法与 PyQt5 的 GUI 集成,只能在 OpenCV GUI 进行简单的操作。



2. PyQt实现鼠标框选

PyQt 中实现鼠标框选,本质上是鼠标动作的响应。

2.1 支持鼠标事件的自定义 Label 类

基本的 QLabel 类并不支持鼠标动作,因此需要自定义一个支持鼠标动作的 Label 类。

PyQt中,每个事件类型都被封装成相应的事件类,如鼠标事件为QMouseEvent,键盘事件为QKeyEvent等。而它们的基类是QEvent。

QMouseEvent 鼠标事件:

mousePressEvent (self, event):鼠标按下事件
mouseReleaseEvent (self, event):鼠标释放事件
mouseDoubieCiickEvent (self, event):双击鼠标事件
mouseMoveEvent(self,event):鼠标移动事件
enterEvent (self, event):鼠标进入控件事件
leaveEvent (self, event):鼠标离开控件事件
wheelEvent (self, event):滚轮滚动事件

QMouseEvent 鼠标方法:

ignore():让父控件继续收到鼠标事件
accept():不让父控件继续收到鼠标事件
x()、y():返回相对于控件空间的鼠标坐标值
pos():返回相对于控件空间的QPoint对象
localPos():返回相对于控件空间的QPointF对象
globalX()、globalY():返回相对于屏幕的x,y 坐标值
globalPos():返回相对于屏幕的QPoint对象
windowPos():返回相对于窗口的QPointF对象
screenPos():返回相对于屏幕的QPointF对象
timestamp():返回事件发生的时间;

QMouseEvent 鼠标事件的具体内容:
按下并释放鼠标按钮时,将调用以下方法:

mousePressEvent (self, event) - 鼠标键按下时调用;
mouseReleaseEvent (self, event) - 鼠标键公开时调用;
mouseDoubieCiickEvent (self, event) - 双击鼠标时调用。必须注意,在双击之前的其他事件。双击时的事件顺序如下:

- MouseButtonPress
- MouseButtonRelease
- MouseButtonDblClick
- MouseButtonPress
- MouseButtonRelease

2.2 例程7-2:支持鼠标事件的自定义 MyLabel 类

class MyLabel(QLabel):
    def __init__(self,parent=None):
        super(MyLabel, self).__init__(parent)
        self.x0 = 0
        self.y0 = 0
        self.x1 = 1
        self.y1 = 1
        self.flag = False

    # 鼠标点击事件
    def mousePressEvent(self, event):
        self.flag = True  # 鼠标点击状态
        self.x0 = event.x()
        self.y0 = event.y()

    # 鼠标释放事件
    def mouseReleaseEvent(self, event):
        self.flag = False  # 鼠标释放状态
        self.x1 = event.x()
        self.y1 = event.y()

    # 鼠标移动事件
    def mouseMoveEvent(self, event):
        if self.flag:
            self.x1 = event.x()
            self.y1 = event.y()
            self.update()

    # 绘制事件
    def paintEvent(self, event):
        super().paintEvent(event)
        rect = QRect(self.x0, self.y0, abs(self.x1 - self.x0), abs(self.y1 - self.y0))
        painter = QPainter(self)
        painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
        painter.drawRect(rect)



3. 项目实战:PyQt 鼠标框选

本项目基于 PyQt5 GUI,使用鼠标框选 ROI 区域,在窗口中显示 ROI 区域,并对 ROI 进行处理。



3.1 使用 QtDesigner 开发 PyQt5 图形界面

本例的 UI 继承自 uiDemo4.ui :


pyqt5 绘图和鼠标选择,# OpenCV 实战项目,opencv,pyqt,python,GUI,图像处理

于是,我们就完成了本项目的图形界面设计,将其保存为 uiDemo8.ui文件。

在 PyCharm中,使用 PyUIC 将选中的 uiDemo8.ui 文件转换为 .py 文件,就得到了 uiDemo8.py 文件。



3.2. 项目主程序的开发

3.2.1 实例化 MyLabel 类

自定义的 MyLabel 类不能在 QtDesigner 中创建,要在主程序中定义如下。

        self.label_1 = MyLabel(self.centralwidget)
        self.label_1.setGeometry(QRect(20, 20, 400, 320))
        self.label_1.setAlignment(Qt.AlignCenter)
        self.label_1.setObjectName("label_1")

3.2.2 框选图像槽函数

click_pushButton槽函数,由 pushButton_3.clicked 按钮信号触发。

    def click_pushButton_3(self):  # 点击 pushButton_3 触发 框选图像
        print("pushButton_3")
        self.label_1.setGeometry(QRect(20, 20, 400, 320))
        hImg, wImg = self.img1.shape[:2]
        wLabel = self.label_1.width()
        hLabel = self.label_1.height()
        x0 = self.label_1.x0 * wImg//wLabel
        y0 = self.label_1.y0 * hImg//hLabel
        x1 = self.label_1.x1 * wImg//wLabel
        y1 = self.label_1.y1 * hImg//hLabel
        print("hImg,wImg=({},{}), x1,y1=({},{})".format(hImg, wImg, hLabel, wLabel))
        print("x0,y0=({},{}), x1,y1=({},{})".format(x0, y0, x1, y1))
        self.img2 = np.zeros((self.img1.shape), np.uint8)
        self.img2[y0:y1, x0:x1, :] = self.img1[y0:y1, x0:x1, :]
        self.refreshShow(self.img2, self.label_2)  # 刷新显示
        return

3.2.3 信号与槽的连接
        # 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButton
        self.pushButton_1.clicked.connect(self.click_pushButton_1)  # 按钮触发:导入图像
        self.pushButton_2.clicked.connect(self.click_pushButton_2)  # # 按钮触发:灰度显示
        self.pushButton_3.clicked.connect(self.click_pushButton_3)  # # 按钮触发:框选图像
        self.pushButton_4.clicked.connect(self.trigger_actHelp)  # # 按钮触发:调整色阶
        self.pushButton_5.clicked.connect(self.close)  # 点击 # 按钮触发:关闭

3.3 完整例程 OpenCVPyqt08.py

# OpenCVPyqt08.py
# Demo07 of GUI by PyQt5
# Copyright 2023 Youcans, XUPT
# Crated:2023-02-12

import sys
import cv2 as cv
import numpy as np
from PyQt5.QtCore import QObject, pyqtSignal, QPoint, QRect, qDebug, Qt
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from uiDemo8 import Ui_MainWindow  # 导入 uiDemo8.py 中的 Ui_MainWindow 界面类


class MyLabel(QLabel):
    def __init__(self,parent=None):
        super(MyLabel, self).__init__(parent)
        self.x0 = 0
        self.y0 = 0
        self.x1 = 1
        self.y1 = 1
        self.flag = False

    # 鼠标点击事件
    def mousePressEvent(self, event):
        self.flag = True  # 鼠标点击状态
        self.x0 = event.x()
        self.y0 = event.y()

    # 鼠标释放事件
    def mouseReleaseEvent(self, event):
        self.flag = False  # 鼠标释放状态
        self.x1 = event.x()
        self.y1 = event.y()

    # 鼠标移动事件
    def mouseMoveEvent(self, event):
        if self.flag:
            self.x1 = event.x()
            self.y1 = event.y()
            self.update()

    # 绘制事件
    def paintEvent(self, event):
        super().paintEvent(event)
        rect = QRect(self.x0, self.y0, abs(self.x1 - self.x0), abs(self.y1 - self.y0))
        painter = QPainter(self)
        painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
        painter.drawRect(rect)


class MyMainWindow(QMainWindow, Ui_MainWindow):  # 继承 QMainWindow 类和 Ui_MainWindow 界面类
    def __init__(self, parent=None):
        super(MyMainWindow, self).__init__(parent)  # 初始化父类
        self.setupUi(self)  # 继承 Ui_MainWindow 界面类
        self.label_1 = MyLabel(self.centralwidget)
        self.label_1.setGeometry(QRect(20, 20, 400, 320))
        self.label_1.setAlignment(Qt.AlignCenter)
        self.label_1.setObjectName("label_1")

        # 菜单栏
        self.actionOpen.triggered.connect(self.openSlot)  # 连接并执行 openSlot 子程序
        self.actionSave.triggered.connect(self.saveSlot)  # 连接并执行 saveSlot 子程序
        self.actionHelp.triggered.connect(self.trigger_actHelp)  # 连接并执行 trigger_actHelp 子程序
        self.actionQuit.triggered.connect(self.close)  # 连接并执行 trigger_actHelp 子程序

        # 通过 connect 建立信号/槽连接,点击按钮事件发射 triggered 信号,执行相应的子程序 click_pushButton
        self.pushButton_1.clicked.connect(self.click_pushButton_1)  # 按钮触发:导入图像
        self.pushButton_2.clicked.connect(self.click_pushButton_2)  # # 按钮触发:灰度显示
        self.pushButton_3.clicked.connect(self.click_pushButton_3)  # # 按钮触发:框选图像
        self.pushButton_4.clicked.connect(self.trigger_actHelp)  # # 按钮触发:调整色阶
        self.pushButton_5.clicked.connect(self.close)  # 点击 # 按钮触发:关闭

        # 初始化
        self.img1 = np.ndarray(())  # 初始化图像 ndarry,用于存储图像
        self.img2 = np.ndarray(())  # 初始化图像 ndarry,用于存储图像
        self.img1 = cv.imread("../images/Lena.tif")  # OpenCV 读取图像
        self.refreshShow(self.img1, self.label_1)
        # self.refreshShow(self.img1, self.label_2)
        return

    def click_pushButton_1(self):  # 点击 pushButton_1 触发
        self.img1 = self.openSlot()  # 读取图像
        self.img2 = self.img1.copy()
        print("click_pushButton_1", self.img1.shape)
        self.refreshShow(self.img1, self.label_1)  # 刷新显示
        return

    def click_pushButton_2(self):  # 点击 pushButton_2 触发
        print("pushButton_2")
        self.img2 = cv.cvtColor(self.img2, cv.COLOR_BGR2GRAY)  # 图片格式转换:BGR -> Gray
        self.refreshShow(self.img2, self.label_2)  # 刷新显示
        return

    def click_pushButton_3(self):  # 点击 pushButton_3 触发 框选图像
        print("pushButton_3")
        self.label_1.setGeometry(QRect(20, 20, 400, 320))
        hImg, wImg = self.img1.shape[:2]
        wLabel = self.label_1.width()
        hLabel = self.label_1.height()
        x0 = self.label_1.x0 * wImg//wLabel
        y0 = self.label_1.y0 * hImg//hLabel
        x1 = self.label_1.x1 * wImg//wLabel
        y1 = self.label_1.y1 * hImg//hLabel
        print("hImg,wImg=({},{}), x1,y1=({},{})".format(hImg, wImg, hLabel, wLabel))
        print("x0,y0=({},{}), x1,y1=({},{})".format(x0, y0, x1, y1))
        self.img2 = np.zeros((self.img1.shape), np.uint8)
        self.img2[y0:y1, x0:x1, :] = self.img1[y0:y1, x0:x1, :]
        print(self.img2.shape)
        # cv.imshow("Demo", self.img2)
        # key = cv.waitKey(0)  # 等待下一个按键命令

        # self.gRightLayout.removeWidget(self.label_2)  # 删除原有 labelCover 控件及显示图表
        # sip.delete(self.labelCover)  # 删除控件 labelCover
        # self.img2 = np.zeros(self.img1.shape, np.int8)
        self.refreshShow(self.img2, self.label_2)  # 刷新显示
        return

    def refreshShow(self, img, label):
        print(img.shape, label)
        qImg = self.cvToQImage(img)  # OpenCV 转为 PyQt 图像格式
        # label.setScaledContents(False)  # 需要在图片显示之前进行设置
        label.setPixmap((QPixmap.fromImage(qImg)))  # 加载 PyQt 图像
        return

    def openSlot(self, flag=1):  # 读取图像文件
        # OpenCV 读取图像文件
        fileName, _ = QFileDialog.getOpenFileName(self, "Open Image", "../images/", "*.png *.jpg *.tif")
        if flag==0 or flag=="gray":
            img = cv.imread(fileName, cv.IMREAD_GRAYSCALE)  # 读取灰度图像
        else:
            img = cv.imread(fileName, cv.IMREAD_COLOR)  # 读取彩色图像
        print(fileName, img.shape)
        return img

    def saveSlot(self):  # 保存图像文件
        # 选择存储文件 dialog
        fileName, tmp = QFileDialog.getSaveFileName(self, "Save Image", "../images/", '*.png; *.jpg; *.tif')
        if self.img1.size == 1:
            return
        # OpenCV 写入图像文件
        ret = cv.imwrite(fileName, self.img1)
        if ret:
            print(fileName, self.img.shape)
        return

    def cvToQImage(self, image):
        # 8-bits unsigned, NO. OF CHANNELS=1
        if image.dtype == np.uint8:
            channels = 1 if len(image.shape) == 2 else image.shape[2]
        if channels == 3:  # CV_8UC3
            # Create QImage with same dimensions as input Mat
            qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_RGB888)
            return qImg.rgbSwapped()
        elif channels == 1:
            # Create QImage with same dimensions as input Mat
            qImg = QImage(image, image.shape[1], image.shape[0], image.strides[0], QImage.Format_Indexed8)
            return qImg
        else:
            qDebug("ERROR: numpy.ndarray could not be converted to QImage. Channels = %d" % image.shape[2])
            return QImage()

    def qPixmapToCV(self, qPixmap):  # PyQt图像 转换为 OpenCV图像
        qImg = qPixmap.toImage()  # QPixmap 转换为 QImage
        shape = (qImg.height(), qImg.bytesPerLine() * 8 // qImg.depth())
        shape += (4,)
        ptr = qImg.bits()
        ptr.setsize(qImg.byteCount())
        image = np.array(ptr, dtype=np.uint8).reshape(shape)  # 定义 OpenCV 图像
        image = image[..., :3]
        return image

    def trigger_actHelp(self):  # 动作 actHelp 触发
        QMessageBox.about(self, "About",
                          """数字图像处理工具箱 v1.0\nCopyright YouCans, XUPT 2023""")
        return

if __name__ == '__main__':
    app = QApplication(sys.argv)  # 在 QApplication 方法中使用,创建应用程序对象
    myWin = MyMainWindow()  # 实例化 MyMainWindow 类,创建主窗口
    myWin.show()  # 在桌面显示控件 myWin
    sys.exit(app.exec_())  # 结束进程,退出程序


运行结果:

pyqt5 绘图和鼠标选择,# OpenCV 实战项目,opencv,pyqt,python,GUI,图像处理


pyqt5 绘图和鼠标选择,# OpenCV 实战项目,opencv,pyqt,python,GUI,图像处理


【本节完】


版权声明:

Copyright 2023 youcans, XUPT

Crated:2023-2-14文章来源地址https://www.toymoban.com/news/detail-807072.html


到了这里,关于OpenCV-PyQT项目实战(7)项目案例03:鼠标框选的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • OpenCV项目开发实战--基于Python/C++实现鼠标注释图像和轨迹栏来控制图像大小

    鼠标指针是图形用户界面 (GUI) 中的关键组件。没有它,您就无法真正考虑与 GUI 进行交互。那么,让我们深入了解 OpenCV 中鼠标和轨迹栏的内置函数。我们将演示如何使用鼠标来注释图像,以及如何使用轨迹栏来控制图像的大小 我们将使用下图来演示 OpenCV 中鼠标指针和轨迹

    2024年02月11日
    浏览(53)
  • opencv进阶03-图像与鼠标的交互示例

    在处理图像时,可能需要与当前正在处理的图像进行交互。OpenCV 提供了鼠标事件,使用户可以通过鼠标与图像交互。鼠标事件能够识别常用的鼠标操作,例如:针对不同按键的单击、双击,鼠标的滑动、拖曳等。 例如,用户单击鼠标,我们就画一个圆。通常的做法是,创建

    2024年02月12日
    浏览(64)
  • osg实现鼠标框选

    目录 1. 需求的提出 2. 具体实现      2.1. 禁止场景跟随鼠标转动      2.2. 矩形框前置绘制 3. 附加说明         3.1. 颜色设置说明         3.2.矩形框显示和隐藏的另一种实现        有时需要在屏幕通过按住键盘上的某个键如Ctrl键且按住鼠标左键,拖出一个矩形,实现框选

    2024年02月08日
    浏览(43)
  • opencv案例03 -基于OpenCV实现二维码生成,发现,定位,识别

    废话不多说,直接上代码 运行效果: 会在当前目前生成一张图片 对生成的二维码识别 opencv从4代之后推出了二维码识别接口.调用方法是这样的.代码如下: 运行结果: 返回值有三个, 第一个result就是解码后的内容,例如我这个二维码的结果是\\\"B0018\\\",当然也可以是个纯数字. 第二

    2024年02月11日
    浏览(47)
  • 全方位讲解:鼠标框选图形功能的技术实现

    如果你正在考虑开发一款图形编辑软件,并希望能够支持常用的框选功能,本文将为你提供一些有用的信息。像下面动画所示那样快速选取单个或多个图形并非易事,尤其是还要考虑到像直线这样的图形有其特别之处。本文提出的解决方案主要是利用包围盒的概念,通过包围

    2024年02月03日
    浏览(51)
  • 学生管理系统-03项目案例(3)

    一、用户列表 1、编写api接口 2、表格渲染 3、分页 4、搜索功能 首先在data中的query对象中添加type和value属性 页面中进行布局 5、注册 略 6、修改用户 在api接口中编写修改方法 为编辑按钮绑定事件 在data中定义 在methods中定义一个修改方法 使用深浅拷贝解决修改中的一个问题

    2024年02月15日
    浏览(43)
  • OpenCvSharp-鼠标框选截取感兴趣区域(ROI)-附源代码

     前言:ROI(Region of Interest)是图像处理中的一个重要概念,指的是图像中感兴趣的区域。在这个区域内,我们通常希望执行某种特定的操作、获取特定信息,或者进行进一步的分析。ROI 可以是图像的一个矩形、圆形、多边形或者其他各种形状。 目录 一、核心函数: 委托

    2024年02月04日
    浏览(52)
  • vue中实现el-table点选和鼠标框选功能

      实现思路: 项目有两个需求,既能在el-table实现点选又能实现鼠标框选 一. 点选实现思路: 使用el-table的cellClick方法,         1.直接给点击的cell添加类名,cell.classList.add(\\\"blue-cell\\\");然后把获取的数据存入数组,            设置样式:      方法2.如果不添加类名,可以在ce

    2024年02月16日
    浏览(67)
  • React 入门:实战案例 TodoList Item鼠标悬浮效果

    本文实现鼠标悬浮到 Todo 记录上时的样式,以及悬浮到 Item 组件 UI 上时,显示【删除】按钮,鼠标离开时隐藏【删除】按钮。 本文通过给事件处理来实现。 (如果你对 React 的事件处理不了解,请阅读本专栏前面的【事件处理】章节) onMouseEnter:当鼠标悬浮到某个元素上时

    2024年02月12日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包