OpenCV 您所在的位置:网站首页 选框工具案例 OpenCV

OpenCV

2024-07-16 07:41| 来源: 网络整理| 查看: 265

欢迎关注『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:鼠标框选1. OpenCV实现鼠标框选2. PyQt实现鼠标框选2.1 支持鼠标事件的自定义 Label 类2.2 例程7-2:支持鼠标事件的自定义 MyLabel 类 3. 项目实战:PyQt 鼠标框选3.1 使用 QtDesigner 开发 PyQt5 图形界面3.2. 项目主程序的开发3.2.1 实例化 MyLabel 类3.2.2 框选图像槽函数3.2.3 信号与槽的连接 3.3 完整例程 OpenCVPyqt08.py

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)

在这里插入图片描述

总结: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 :

在这里插入图片描述

于是,我们就完成了本项目的图形界面设计,将其保存为 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_()) # 结束进程,退出程序

运行结果:

在这里插入图片描述

在这里插入图片描述

【本节完】

版权声明:

Copyright 2023 youcans, XUPT

Crated:2023-2-14



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有