未验证 提交 034e628d 编写于 作者: E Evezerest 提交者: GitHub

Merge pull request #6829 from whjdark/dygraph

为表格标注添加BBOX排序功能和序号显示
...@@ -28,7 +28,7 @@ from PyQt5.QtCore import QSize, Qt, QPoint, QByteArray, QTimer, QFileInfo, QPoin ...@@ -28,7 +28,7 @@ from PyQt5.QtCore import QSize, Qt, QPoint, QByteArray, QTimer, QFileInfo, QPoin
from PyQt5.QtGui import QImage, QCursor, QPixmap, QImageReader from PyQt5.QtGui import QImage, QCursor, QPixmap, QImageReader
from PyQt5.QtWidgets import QMainWindow, QListWidget, QVBoxLayout, QToolButton, QHBoxLayout, QDockWidget, QWidget, \ from PyQt5.QtWidgets import QMainWindow, QListWidget, QVBoxLayout, QToolButton, QHBoxLayout, QDockWidget, QWidget, \
QSlider, QGraphicsOpacityEffect, QMessageBox, QListView, QScrollArea, QWidgetAction, QApplication, QLabel, QGridLayout, \ QSlider, QGraphicsOpacityEffect, QMessageBox, QListView, QScrollArea, QWidgetAction, QApplication, QLabel, QGridLayout, \
QFileDialog, QListWidgetItem, QComboBox, QDialog QFileDialog, QListWidgetItem, QComboBox, QDialog, QAbstractItemView
__dir__ = os.path.dirname(os.path.abspath(__file__)) __dir__ = os.path.dirname(os.path.abspath(__file__))
...@@ -242,6 +242,20 @@ class MainWindow(QMainWindow): ...@@ -242,6 +242,20 @@ class MainWindow(QMainWindow):
self.labelListDock.setFeatures(QDockWidget.NoDockWidgetFeatures) self.labelListDock.setFeatures(QDockWidget.NoDockWidgetFeatures)
listLayout.addWidget(self.labelListDock) listLayout.addWidget(self.labelListDock)
# enable labelList drag_drop to adjust bbox order
# 设置选择模式为单选
self.labelList.setSelectionMode(QAbstractItemView.SingleSelection)
# 启用拖拽
self.labelList.setDragEnabled(True)
# 设置接受拖放
self.labelList.viewport().setAcceptDrops(True)
# 设置显示将要被放置的位置
self.labelList.setDropIndicatorShown(True)
# 设置拖放模式为移动项目,如果不设置,默认为复制项目
self.labelList.setDragDropMode(QAbstractItemView.InternalMove)
# 触发放置
self.labelList.model().rowsMoved.connect(self.drag_drop_happened)
# ================== Detection Box ================== # ================== Detection Box ==================
self.BoxList = QListWidget() self.BoxList = QListWidget()
...@@ -589,15 +603,23 @@ class MainWindow(QMainWindow): ...@@ -589,15 +603,23 @@ class MainWindow(QMainWindow):
self.displayLabelOption.setChecked(settings.get(SETTING_PAINT_LABEL, False)) self.displayLabelOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
self.displayLabelOption.triggered.connect(self.togglePaintLabelsOption) self.displayLabelOption.triggered.connect(self.togglePaintLabelsOption)
# Add option to enable/disable box index being displayed at the top of bounding boxes
self.displayIndexOption = QAction(getStr('displayIndex'), self)
self.displayIndexOption.setCheckable(True)
self.displayIndexOption.setChecked(settings.get(SETTING_PAINT_INDEX, False))
self.displayIndexOption.triggered.connect(self.togglePaintIndexOption)
self.labelDialogOption = QAction(getStr('labelDialogOption'), self) self.labelDialogOption = QAction(getStr('labelDialogOption'), self)
self.labelDialogOption.setShortcut("Ctrl+Shift+L") self.labelDialogOption.setShortcut("Ctrl+Shift+L")
self.labelDialogOption.setCheckable(True) self.labelDialogOption.setCheckable(True)
self.labelDialogOption.setChecked(settings.get(SETTING_PAINT_LABEL, False)) self.labelDialogOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
self.displayIndexOption.setChecked(settings.get(SETTING_PAINT_INDEX, False))
self.labelDialogOption.triggered.connect(self.speedChoose) self.labelDialogOption.triggered.connect(self.speedChoose)
self.autoSaveOption = QAction(getStr('autoSaveMode'), self) self.autoSaveOption = QAction(getStr('autoSaveMode'), self)
self.autoSaveOption.setCheckable(True) self.autoSaveOption.setCheckable(True)
self.autoSaveOption.setChecked(settings.get(SETTING_PAINT_LABEL, False)) self.autoSaveOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
self.displayIndexOption.setChecked(settings.get(SETTING_PAINT_INDEX, False))
self.autoSaveOption.triggered.connect(self.autoSaveFunc) self.autoSaveOption.triggered.connect(self.autoSaveFunc)
addActions(self.menus.file, addActions(self.menus.file,
...@@ -606,7 +628,7 @@ class MainWindow(QMainWindow): ...@@ -606,7 +628,7 @@ class MainWindow(QMainWindow):
addActions(self.menus.help, (showKeys, showSteps, showInfo)) addActions(self.menus.help, (showKeys, showSteps, showInfo))
addActions(self.menus.view, ( addActions(self.menus.view, (
self.displayLabelOption, self.labelDialogOption, self.displayLabelOption, self.displayIndexOption, self.labelDialogOption,
None, None,
hideAll, showAll, None, hideAll, showAll, None,
zoomIn, zoomOut, zoomOrg, None, zoomIn, zoomOut, zoomOrg, None,
...@@ -964,6 +986,7 @@ class MainWindow(QMainWindow): ...@@ -964,6 +986,7 @@ class MainWindow(QMainWindow):
else: else:
self.canvas.selectedShapes_hShape = self.canvas.selectedShapes self.canvas.selectedShapes_hShape = self.canvas.selectedShapes
for shape in self.canvas.selectedShapes_hShape: for shape in self.canvas.selectedShapes_hShape:
if shape in self.shapesToItemsbox.keys():
item = self.shapesToItemsbox[shape] # listitem item = self.shapesToItemsbox[shape] # listitem
text = [(int(p.x()), int(p.y())) for p in shape.points] text = [(int(p.x()), int(p.y())) for p in shape.points]
item.setText(str(text)) item.setText(str(text))
...@@ -1040,6 +1063,8 @@ class MainWindow(QMainWindow): ...@@ -1040,6 +1063,8 @@ class MainWindow(QMainWindow):
def addLabel(self, shape): def addLabel(self, shape):
shape.paintLabel = self.displayLabelOption.isChecked() shape.paintLabel = self.displayLabelOption.isChecked()
shape.paintIdx = self.displayIndexOption.isChecked()
item = HashableQListWidgetItem(shape.label) item = HashableQListWidgetItem(shape.label)
item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Unchecked) if shape.difficult else item.setCheckState(Qt.Checked) item.setCheckState(Qt.Unchecked) if shape.difficult else item.setCheckState(Qt.Checked)
...@@ -1083,6 +1108,7 @@ class MainWindow(QMainWindow): ...@@ -1083,6 +1108,7 @@ class MainWindow(QMainWindow):
def loadLabels(self, shapes): def loadLabels(self, shapes):
s = [] s = []
shape_index = 0
for label, points, line_color, key_cls, difficult in shapes: for label, points, line_color, key_cls, difficult in shapes:
shape = Shape(label=label, line_color=line_color, key_cls=key_cls) shape = Shape(label=label, line_color=line_color, key_cls=key_cls)
for x, y in points: for x, y in points:
...@@ -1094,6 +1120,8 @@ class MainWindow(QMainWindow): ...@@ -1094,6 +1120,8 @@ class MainWindow(QMainWindow):
shape.addPoint(QPointF(x, y)) shape.addPoint(QPointF(x, y))
shape.difficult = difficult shape.difficult = difficult
shape.idx = shape_index
shape_index += 1
# shape.locked = False # shape.locked = False
shape.close() shape.close()
s.append(shape) s.append(shape)
...@@ -1209,6 +1237,9 @@ class MainWindow(QMainWindow): ...@@ -1209,6 +1237,9 @@ class MainWindow(QMainWindow):
self.canvas.deSelectShape() self.canvas.deSelectShape()
def labelItemChanged(self, item): def labelItemChanged(self, item):
# avoid accidentally triggering the itemChanged siganl with unhashable item
# Unknown trigger condition
if type(item) == HashableQListWidgetItem:
shape = self.itemsToShapes[item] shape = self.itemsToShapes[item]
label = item.text() label = item.text()
if label != shape.label: if label != shape.label:
...@@ -1221,6 +1252,39 @@ class MainWindow(QMainWindow): ...@@ -1221,6 +1252,39 @@ class MainWindow(QMainWindow):
else: # User probably changed item visibility else: # User probably changed item visibility
self.canvas.setShapeVisible(shape, True) # item.checkState() == Qt.Checked self.canvas.setShapeVisible(shape, True) # item.checkState() == Qt.Checked
# self.actions.save.setEnabled(True) # self.actions.save.setEnabled(True)
else:
print('enter labelItemChanged slot with unhashable item: ', item, item.text())
def drag_drop_happened(self):
'''
label list drag drop signal slot
'''
# print('___________________drag_drop_happened_______________')
# should only select single item
for item in self.labelList.selectedItems():
newIndex = self.labelList.indexFromItem(item).row()
# only support drag_drop one item
assert len(self.canvas.selectedShapes) > 0
for shape in self.canvas.selectedShapes:
selectedShapeIndex = shape.idx
if newIndex == selectedShapeIndex:
return
# move corresponding item in shape list
shape = self.canvas.shapes.pop(selectedShapeIndex)
self.canvas.shapes.insert(newIndex, shape)
# update bbox index
self.canvas.updateShapeIndex()
# boxList update simultaneously
item = self.BoxList.takeItem(selectedShapeIndex)
self.BoxList.insertItem(newIndex, item)
# changes happen
self.setDirty()
# Callback functions: # Callback functions:
def newShape(self, value=True): def newShape(self, value=True):
...@@ -1560,6 +1624,7 @@ class MainWindow(QMainWindow): ...@@ -1560,6 +1624,7 @@ class MainWindow(QMainWindow):
settings[SETTING_LAST_OPEN_DIR] = '' settings[SETTING_LAST_OPEN_DIR] = ''
settings[SETTING_PAINT_LABEL] = self.displayLabelOption.isChecked() settings[SETTING_PAINT_LABEL] = self.displayLabelOption.isChecked()
settings[SETTING_PAINT_INDEX] = self.displayIndexOption.isChecked()
settings[SETTING_DRAW_SQUARE] = self.drawSquaresOption.isChecked() settings[SETTING_DRAW_SQUARE] = self.drawSquaresOption.isChecked()
settings.save() settings.save()
try: try:
...@@ -1946,8 +2011,16 @@ class MainWindow(QMainWindow): ...@@ -1946,8 +2011,16 @@ class MainWindow(QMainWindow):
self.labelHist.append(line) self.labelHist.append(line)
def togglePaintLabelsOption(self): def togglePaintLabelsOption(self):
self.displayIndexOption.setChecked(False)
for shape in self.canvas.shapes:
shape.paintLabel = self.displayLabelOption.isChecked()
shape.paintIdx = self.displayIndexOption.isChecked()
def togglePaintIndexOption(self):
self.displayLabelOption.setChecked(False)
for shape in self.canvas.shapes: for shape in self.canvas.shapes:
shape.paintLabel = self.displayLabelOption.isChecked() shape.paintLabel = self.displayLabelOption.isChecked()
shape.paintIdx = self.displayIndexOption.isChecked()
def toogleDrawSquare(self): def toogleDrawSquare(self):
self.canvas.setDrawingShapeToSquare(self.drawSquaresOption.isChecked()) self.canvas.setDrawingShapeToSquare(self.drawSquaresOption.isChecked())
...@@ -2187,6 +2260,7 @@ class MainWindow(QMainWindow): ...@@ -2187,6 +2260,7 @@ class MainWindow(QMainWindow):
shapes = [] shapes = []
result_len = len(region['res']['boxes']) result_len = len(region['res']['boxes'])
order_index = 0
for i in range(result_len): for i in range(result_len):
bbox = np.array(region['res']['boxes'][i]) bbox = np.array(region['res']['boxes'][i])
rec_text = region['res']['rec_res'][i][0] rec_text = region['res']['rec_res'][i][0]
...@@ -2205,6 +2279,8 @@ class MainWindow(QMainWindow): ...@@ -2205,6 +2279,8 @@ class MainWindow(QMainWindow):
x, y, snapped = self.canvas.snapPointToCanvas(x, y) x, y, snapped = self.canvas.snapPointToCanvas(x, y)
shape.addPoint(QPointF(x, y)) shape.addPoint(QPointF(x, y))
shape.difficult = False shape.difficult = False
shape.idx = order_index
order_index += 1
# shape.locked = False # shape.locked = False
shape.close() shape.close()
self.addLabel(shape) self.addLabel(shape)
......
...@@ -314,6 +314,7 @@ class Canvas(QWidget): ...@@ -314,6 +314,7 @@ class Canvas(QWidget):
QApplication.restoreOverrideCursor() # ? QApplication.restoreOverrideCursor() # ?
if self.movingShape and self.hShape: if self.movingShape and self.hShape:
if self.hShape in self.shapes:
index = self.shapes.index(self.hShape) index = self.shapes.index(self.hShape)
if ( if (
self.shapesBackups[-1][index].points self.shapesBackups[-1][index].points
...@@ -329,6 +330,7 @@ class Canvas(QWidget): ...@@ -329,6 +330,7 @@ class Canvas(QWidget):
assert len(self.selectedShapesCopy) == len(self.selectedShapes) assert len(self.selectedShapesCopy) == len(self.selectedShapes)
if copy: if copy:
for i, shape in enumerate(self.selectedShapesCopy): for i, shape in enumerate(self.selectedShapesCopy):
shape.idx = len(self.shapes) # add current box index
self.shapes.append(shape) self.shapes.append(shape)
self.selectedShapes[i].selected = False self.selectedShapes[i].selected = False
self.selectedShapes[i] = shape self.selectedShapes[i] = shape
...@@ -524,6 +526,9 @@ class Canvas(QWidget): ...@@ -524,6 +526,9 @@ class Canvas(QWidget):
self.storeShapes() self.storeShapes()
self.selectedShapes = [] self.selectedShapes = []
self.update() self.update()
self.updateShapeIndex()
return deleted_shapes return deleted_shapes
def storeShapes(self): def storeShapes(self):
...@@ -651,6 +656,7 @@ class Canvas(QWidget): ...@@ -651,6 +656,7 @@ class Canvas(QWidget):
return return
self.current.close() self.current.close()
self.current.idx = len(self.shapes) # add current box index
self.shapes.append(self.current) self.shapes.append(self.current)
self.current = None self.current = None
self.setHiding(False) self.setHiding(False)
...@@ -842,6 +848,7 @@ class Canvas(QWidget): ...@@ -842,6 +848,7 @@ class Canvas(QWidget):
self.hVertex = None self.hVertex = None
# self.hEdge = None # self.hEdge = None
self.storeShapes() self.storeShapes()
self.updateShapeIndex()
self.repaint() self.repaint()
def setShapeVisible(self, shape, value): def setShapeVisible(self, shape, value):
...@@ -883,6 +890,7 @@ class Canvas(QWidget): ...@@ -883,6 +890,7 @@ class Canvas(QWidget):
self.selectedShapes = [] self.selectedShapes = []
for shape in self.shapes: for shape in self.shapes:
shape.selected = False shape.selected = False
self.updateShapeIndex()
self.repaint() self.repaint()
@property @property
...@@ -890,3 +898,8 @@ class Canvas(QWidget): ...@@ -890,3 +898,8 @@ class Canvas(QWidget):
if len(self.shapesBackups) < 2: if len(self.shapesBackups) < 2:
return False return False
return True return True
def updateShapeIndex(self):
for i in range(len(self.shapes)):
self.shapes[i].idx = i
self.update()
\ No newline at end of file
...@@ -21,6 +21,7 @@ SETTING_ADVANCE_MODE = 'advanced' ...@@ -21,6 +21,7 @@ SETTING_ADVANCE_MODE = 'advanced'
SETTING_WIN_STATE = 'window/state' SETTING_WIN_STATE = 'window/state'
SETTING_SAVE_DIR = 'savedir' SETTING_SAVE_DIR = 'savedir'
SETTING_PAINT_LABEL = 'paintlabel' SETTING_PAINT_LABEL = 'paintlabel'
SETTING_PAINT_INDEX = 'paintindex'
SETTING_LAST_OPEN_DIR = 'lastOpenDir' SETTING_LAST_OPEN_DIR = 'lastOpenDir'
SETTING_AUTO_SAVE = 'autosave' SETTING_AUTO_SAVE = 'autosave'
SETTING_SINGLE_CLASS = 'singleclass' SETTING_SINGLE_CLASS = 'singleclass'
......
...@@ -46,15 +46,16 @@ class Shape(object): ...@@ -46,15 +46,16 @@ class Shape(object):
point_size = 8 point_size = 8
scale = 1.0 scale = 1.0
def __init__(self, label=None, line_color=None, difficult=False, key_cls="None", paintLabel=False): def __init__(self, label=None, line_color=None, difficult=False, key_cls="None", paintLabel=False, paintIdx=False):
self.label = label self.label = label
self.idx = 0 self.idx = None # bbox order, only for table annotation
self.points = [] self.points = []
self.fill = False self.fill = False
self.selected = False self.selected = False
self.difficult = difficult self.difficult = difficult
self.key_cls = key_cls self.key_cls = key_cls
self.paintLabel = paintLabel self.paintLabel = paintLabel
self.paintIdx = paintIdx
self.locked = False self.locked = False
self.direction = 0 self.direction = 0
self.center = None self.center = None
...@@ -164,6 +165,25 @@ class Shape(object): ...@@ -164,6 +165,25 @@ class Shape(object):
min_y += MIN_Y_LABEL min_y += MIN_Y_LABEL
painter.drawText(min_x, min_y, self.label) painter.drawText(min_x, min_y, self.label)
# Draw number at the top-right
if self.paintIdx:
min_x = sys.maxsize
min_y = sys.maxsize
for point in self.points:
min_x = min(min_x, point.x())
min_y = min(min_y, point.y())
if min_x != sys.maxsize and min_y != sys.maxsize:
font = QFont()
font.setPointSize(8)
font.setBold(True)
painter.setFont(font)
text = ''
if self.idx != None:
text = str(self.idx)
if min_y < MIN_Y_LABEL:
min_y += MIN_Y_LABEL
painter.drawText(min_x, min_y, text)
if self.fill: if self.fill:
color = self.select_fill_color if self.selected else self.fill_color color = self.select_fill_color if self.selected else self.fill_color
painter.fillPath(line_path, color) painter.fillPath(line_path, color)
......
...@@ -61,6 +61,7 @@ labels=Labels ...@@ -61,6 +61,7 @@ labels=Labels
autoSaveMode=Auto Save mode autoSaveMode=Auto Save mode
singleClsMode=Single Class Mode singleClsMode=Single Class Mode
displayLabel=Display Labels displayLabel=Display Labels
displayIndex=Display box index
fileList=File List fileList=File List
files=Files files=Files
advancedMode=Advanced Mode advancedMode=Advanced Mode
......
...@@ -61,6 +61,7 @@ labels=标签 ...@@ -61,6 +61,7 @@ labels=标签
autoSaveMode=自动保存模式 autoSaveMode=自动保存模式
singleClsMode=单一类别模式 singleClsMode=单一类别模式
displayLabel=显示类别 displayLabel=显示类别
displayIndex=显示box序号
fileList=文件列表 fileList=文件列表
files=文件 files=文件
advancedMode=专家模式 advancedMode=专家模式
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册