图像标注版本4-多标注框+标注标签+高亮和删除标签

2023-09-03 21:17:01 浏览数 (2)

4.0版本在3.0版本基础上增加了双击某个位置时,如果这个位置在标注框内,则对选中的标注框进行标识出来,同时提示是否要删除当前选中的标注框,进行删除处理。

在这个版本中首先需要增加双击事件,在双击事件中对当前鼠标位置是否属于某个标注框进行遍历,某个位置可能属于多标注框时,则按照先入后出的原则,进行删除。

在绘制事件中主要是对当前位置所在的标注框进行区分显示。

其他代码基本未变。

一、修改MyLabel代码

增加鼠标双击事件

修改paintevent事件

代码语言:javascript复制
代码语言:javascript复制
from PyQt5.QtWidgets import QWidget, QApplication, QLabel, QMessageBox
from PyQt5.QtCore import QRect, Qt
from PyQt5.QtGui import QPixmap, QPainter, QPen
from ui_labelchoose import DialogChoooseLabelWin
import sys


# 重定义QLabel,实现绘制事件和各类鼠标事件
class MyLabel(QLabel):
    def __init__(self, parent=None):
        '''
        :param parent:
        初始化基本参数
        '''
        super(MyLabel, self).__init__(parent)
        self.x0 = 0
        self.y0 = 0
        self.x1 = 0
        self.y1 = 0
        self.x1RealTime = 0
        self.y1RealTime = 0
        self.rect = QRect()
        self.flag = False
        # 增加一个存储标注框坐标的列表
        self.bboxList = []
        self.labelindex = 0
        self.curChoosedbbox = []
        self.curbboxindex = -1
        self.deleteboxflag = False

    # 鼠标双击事件,选中当前坐标的被标注框
    # 如存在在多个被标注框内,则显示最新标注的那个
    # 再询问是否要删除标注框
    # 如果确定要删除,则删除当前坐标所在的标注框
    def mouseDoubleClickEvent(self, event):
        x = event.pos().x()
        y = event.pos().y()
        self.curChoosedbbox = []
        # 如果尚未做标注框,则不处理
        if self.bboxList == []:
            return
        else:
            # 以此判断当前双击坐标出现在哪个标注框中,最后标注的优先删除
            tempbboxlist = self.bboxList
            for index, bbox in enumerate(tempbboxlist):
                # 判断坐标是否在标注框中
                if bbox[0] <= x <= bbox[2] and bbox[1] <= y <= bbox[3]:
                    # 如果在的话,记录当前选中的标注框和list中的索引号
                    self.curChoosedbbox = bbox
                    self.curbboxindex = index
                    # 第一次绘制,高亮显示被选中的标注框
                    self.update()
                    # 判断是否已有选中的标注框
            if self.curChoosedbbox != []:
                reply = QMessageBox.question(self, "警告!", "是否要删除当前选中的标注框",
                                             QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
                                             QMessageBox.StandardButton.Yes)
                if reply == QMessageBox.Yes:
                    self.deleteboxflag = True
                    self.bboxList.pop(self.curbboxindex)
                    self.update()
                else:
                    return

    # 单击鼠标触发事件
    # 获取鼠标事件的开始位置
    def mousePressEvent(self, event):
        # 将绘制标志设置为True
        self.flag = True
        self.deleteboxflag = False
        # 重新开始点击事件后,取消已选中的标注框
        self.curChoosedbbox = []
        self.x0 = event.pos().x()
        self.y0 = event.pos().y()

    # 鼠标移动事件
    # 绘制鼠标行进过程中的矩形框
    def mouseMoveEvent(self, event):
        if self.flag:
            self.x1RealTime = event.pos().x()
            self.y1RealTime = event.pos().y()
            self.update()

    # 鼠标释放事件
    def mouseReleaseEvent(self, event):
        # 将绘制标志设置为False
        self.flag = False
        self.x1 = event.pos().x()
        self.y1 = event.pos().y()
        # 这样就不用画出实时框了
        self.x1RealTime = self.x0
        self.y1RealTime = self.y0
        # 修正单击鼠标的保存事件bug,当开始坐标等于结束坐标时,或者为一条直线时,均不响应
        if self.x0 == self.x1 or self.y0 == self.y1:
            return
            # 将标注框的四个坐标轴存储到bboxList
        dialogChooseLabel = DialogChoooseLabelWin()
        if dialogChooseLabel.exec_():
            labelname = dialogChooseLabel.getValue()
            self.saveBBbox(self.x0, self.y0, self.x1, self.y1, labelname)
        event.ignore()

    # 绘制事件
    def paintEvent(self, event):
        super().paintEvent(event)
        painter = QPainter()
        # 增加绘制开始和结束时间
        painter.begin(self)
        # 遍历之前存储的标注框坐标列表
        for point in self.bboxList:
            rect = QRect(point[0], point[1], abs(point[0] - point[2]), abs(point[1] - point[3]))
            painter.setPen(QPen(Qt.red, 2, Qt.SolidLine))
            painter.drawRect(rect)
            painter.drawText(point[0], point[1], point[4])
        # 绘制当前标注框的举行
        # 构造矩形框的起始坐标和宽度、高度
        tempx0 = min(self.x0, self.x1RealTime)
        tempy0 = min(self.y0, self.y1RealTime)
        tempx1 = max(self.x0, self.x1RealTime)
        tempy1 = max(self.y0, self.y1RealTime)
        width = tempx1 - tempx0
        height = tempy1 - tempy0
        currect = QRect(tempx0, tempy0, width, height)
        # 构造QPainter,进行矩形框绘制
        painter.setPen(QPen(Qt.blue, 1, Qt.SolidLine))
        painter.drawRect(currect)

        # 判断是否有当前选中窗口,如果有,且未被删除,则高亮显示
        if self.curChoosedbbox != []:
            # 如果当前不是删除标志,则高亮显示
            # 否则就不再绘制该标注框
            if self.deleteboxflag == False:
                point = self.curChoosedbbox
                rect = QRect(point[0], point[1], abs(point[0] - point[2]), abs(point[1] - point[3]))
                painter.setPen(QPen(Qt.green, 4, Qt.SolidLine))
                painter.drawRect(rect)
                painter.drawText(point[0], point[1], point[4])

        painter.end()

    # 保存到bbox列表
    def saveBBbox(self, x0, y0, x1, y1, labelname):
        tempx0 = min(x0, x1)
        tempy0 = min(y0, y1)
        tempx1 = max(x0, x1)
        tempy1 = max(y0, y1)
        bbox = (tempx0, tempy0, tempx1, tempy1, labelname, self.labelindex)
        self.bboxList.append(bbox)
        self.labelindex  = 1

二、窗口类和main函数未做调整

代码语言:javascript复制
# 窗口类
class LabelV1(QWidget):
    def __init__(self):
        super(LabelV1, self).__init__()
        self.initUI()

    def initUI(self):
        self.resize(960, 540)
        self.move(100, 50)
        self.setWindowTitle('Label标注框4.0版本')
        # 加载重定义的label
        self.lbl = MyLabel(self)
        # 构造QPixmap,加载待标注图片
        img = QPixmap('010.png')
        # 在自定义label中显示QImage
        self.lbl.setPixmap(img)
        self.lbl.setCursor(Qt.CrossCursor)
        self.show()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    labelwin = LabelV1()
    sys.exit(app.exec())

0 人点赞