Skip to content

Commit

Permalink
new commit
Browse files Browse the repository at this point in the history
  • Loading branch information
whjdark committed Jul 8, 2022
1 parent cec3464 commit 4cbacc3
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 31 deletions.
111 changes: 94 additions & 17 deletions PPOCRLabel/PPOCRLabel.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@
import platform
import subprocess
import sys
from tkinter.tix import Tree
import xlrd
from functools import partial

from PyQt5.QtCore import QSize, Qt, QPoint, QByteArray, QTimer, QFileInfo, QPointF, QProcess
from PyQt5.QtGui import QImage, QCursor, QPixmap, QImageReader
from PyQt5.QtWidgets import QMainWindow, QListWidget, QVBoxLayout, QToolButton, QHBoxLayout, QDockWidget, QWidget, \
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__))

Expand Down Expand Up @@ -242,6 +243,20 @@ def __init__(self,
self.labelListDock.setFeatures(QDockWidget.NoDockWidgetFeatures)
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 ==================
self.BoxList = QListWidget()

Expand Down Expand Up @@ -589,15 +604,23 @@ def __init__(self,
self.displayLabelOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
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.setShortcut("Ctrl+Shift+L")
self.labelDialogOption.setCheckable(True)
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.autoSaveOption = QAction(getStr('autoSaveMode'), self)
self.autoSaveOption.setCheckable(True)
self.autoSaveOption.setChecked(settings.get(SETTING_PAINT_LABEL, False))
self.displayIndexOption.setChecked(settings.get(SETTING_PAINT_INDEX, False))
self.autoSaveOption.triggered.connect(self.autoSaveFunc)

addActions(self.menus.file,
Expand All @@ -606,7 +629,7 @@ def __init__(self,

addActions(self.menus.help, (showKeys, showSteps, showInfo))
addActions(self.menus.view, (
self.displayLabelOption, self.labelDialogOption,
self.displayLabelOption, self.displayIndexOption, self.labelDialogOption,
None,
hideAll, showAll, None,
zoomIn, zoomOut, zoomOrg, None,
Expand Down Expand Up @@ -964,9 +987,10 @@ def updateBoxlist(self):
else:
self.canvas.selectedShapes_hShape = self.canvas.selectedShapes
for shape in self.canvas.selectedShapes_hShape:
item = self.shapesToItemsbox[shape] # listitem
text = [(int(p.x()), int(p.y())) for p in shape.points]
item.setText(str(text))
if shape in self.shapesToItemsbox.keys():
item = self.shapesToItemsbox[shape] # listitem
text = [(int(p.x()), int(p.y())) for p in shape.points]
item.setText(str(text))
self.actions.undo.setEnabled(True)
self.setDirty()

Expand Down Expand Up @@ -1040,6 +1064,8 @@ def shapeSelectionChanged(self, selected_shapes):

def addLabel(self, shape):
shape.paintLabel = self.displayLabelOption.isChecked()
shape.paintIdx = self.displayIndexOption.isChecked()

item = HashableQListWidgetItem(shape.label)
item.setFlags(item.flags() | Qt.ItemIsUserCheckable)
item.setCheckState(Qt.Unchecked) if shape.difficult else item.setCheckState(Qt.Checked)
Expand Down Expand Up @@ -1083,6 +1109,7 @@ def remLabels(self, shapes):

def loadLabels(self, shapes):
s = []
shape_index = 0
for label, points, line_color, key_cls, difficult in shapes:
shape = Shape(label=label, line_color=line_color, key_cls=key_cls)
for x, y in points:
Expand All @@ -1094,6 +1121,8 @@ def loadLabels(self, shapes):

shape.addPoint(QPointF(x, y))
shape.difficult = difficult
shape.idx = shape_index
shape_index += 1
# shape.locked = False
shape.close()
s.append(shape)
Expand Down Expand Up @@ -1209,18 +1238,54 @@ def boxSelectionChanged(self):
self.canvas.deSelectShape()

def labelItemChanged(self, item):
shape = self.itemsToShapes[item]
label = item.text()
if label != shape.label:
shape.label = item.text()
# shape.line_color = generateColorByText(shape.label)
self.setDirty()
elif not ((item.checkState() == Qt.Unchecked) ^ (not shape.difficult)):
shape.difficult = True if item.checkState() == Qt.Unchecked else False
self.setDirty()
else: # User probably changed item visibility
self.canvas.setShapeVisible(shape, True) # item.checkState() == Qt.Checked
# self.actions.save.setEnabled(True)
# avoid accidentally triggering the itemChanged siganl with unhashable item
# Unknown trigger condition
if type(item) == HashableQListWidgetItem:
shape = self.itemsToShapes[item]
label = item.text()
if label != shape.label:
shape.label = item.text()
# shape.line_color = generateColorByText(shape.label)
self.setDirty()
elif not ((item.checkState() == Qt.Unchecked) ^ (not shape.difficult)):
shape.difficult = True if item.checkState() == Qt.Unchecked else False
self.setDirty()
else: # User probably changed item visibility
self.canvas.setShapeVisible(shape, True) # item.checkState() == Qt.Checked
# 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:
def newShape(self, value=True):
Expand Down Expand Up @@ -1560,6 +1625,7 @@ def closeEvent(self, event):
settings[SETTING_LAST_OPEN_DIR] = ''

settings[SETTING_PAINT_LABEL] = self.displayLabelOption.isChecked()
settings[SETTING_PAINT_INDEX] = self.displayIndexOption.isChecked()
settings[SETTING_DRAW_SQUARE] = self.drawSquaresOption.isChecked()
settings.save()
try:
Expand Down Expand Up @@ -1946,8 +2012,16 @@ def loadPredefinedClasses(self, predefClassesFile):
self.labelHist.append(line)

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:
shape.paintLabel = self.displayLabelOption.isChecked()
shape.paintIdx = self.displayIndexOption.isChecked()

def toogleDrawSquare(self):
self.canvas.setDrawingShapeToSquare(self.drawSquaresOption.isChecked())
Expand Down Expand Up @@ -2187,6 +2261,7 @@ def TableRecognition(self):

shapes = []
result_len = len(region['res']['boxes'])
order_index = 0
for i in range(result_len):
bbox = np.array(region['res']['boxes'][i])
rec_text = region['res']['rec_res'][i][0]
Expand All @@ -2205,6 +2280,8 @@ def TableRecognition(self):
x, y, snapped = self.canvas.snapPointToCanvas(x, y)
shape.addPoint(QPointF(x, y))
shape.difficult = False
shape.idx = order_index
order_index += 1
# shape.locked = False
shape.close()
self.addLabel(shape)
Expand Down
35 changes: 24 additions & 11 deletions PPOCRLabel/libs/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,21 +314,23 @@ def mouseReleaseEvent(self, ev):
QApplication.restoreOverrideCursor() # ?

if self.movingShape and self.hShape:
index = self.shapes.index(self.hShape)
if (
self.shapesBackups[-1][index].points
!= self.shapes[index].points
):
self.storeShapes()
self.shapeMoved.emit() # connect to updateBoxlist in PPOCRLabel.py
if self.hShape in self.shapes:
index = self.shapes.index(self.hShape)
if (
self.shapesBackups[-1][index].points
!= self.shapes[index].points
):
self.storeShapes()
self.shapeMoved.emit() # connect to updateBoxlist in PPOCRLabel.py

self.movingShape = False
self.movingShape = False

def endMove(self, copy=False):
assert self.selectedShapes and self.selectedShapesCopy
assert len(self.selectedShapesCopy) == len(self.selectedShapes)
if copy:
for i, shape in enumerate(self.selectedShapesCopy):
shape.idx = len(self.shapes) # add current box index
self.shapes.append(shape)
self.selectedShapes[i].selected = False
self.selectedShapes[i] = shape
Expand Down Expand Up @@ -524,6 +526,9 @@ def deleteSelected(self):
self.storeShapes()
self.selectedShapes = []
self.update()

self.updateShapeIndex()

return deleted_shapes

def storeShapes(self):
Expand Down Expand Up @@ -651,7 +656,8 @@ def finalise(self):
return

self.current.close()
self.shapes.append(self.current)
self.current.idx = len(self.shapes) # add current box index
self.shapes.append(self.current)
self.current = None
self.setHiding(False)
self.newShape.emit()
Expand Down Expand Up @@ -842,6 +848,7 @@ def loadShapes(self, shapes, replace=True):
self.hVertex = None
# self.hEdge = None
self.storeShapes()
self.updateShapeIndex()
self.repaint()

def setShapeVisible(self, shape, value):
Expand Down Expand Up @@ -883,10 +890,16 @@ def restoreShape(self):
self.selectedShapes = []
for shape in self.shapes:
shape.selected = False
self.updateShapeIndex()
self.repaint()

@property
def isShapeRestorable(self):
if len(self.shapesBackups) < 2:
return False
return True
return True

def updateShapeIndex(self):
for i in range(len(self.shapes)):
self.shapes[i].idx = i
self.update()
1 change: 1 addition & 0 deletions PPOCRLabel/libs/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
SETTING_WIN_STATE = 'window/state'
SETTING_SAVE_DIR = 'savedir'
SETTING_PAINT_LABEL = 'paintlabel'
SETTING_PAINT_INDEX = 'paintindex'
SETTING_LAST_OPEN_DIR = 'lastOpenDir'
SETTING_AUTO_SAVE = 'autosave'
SETTING_SINGLE_CLASS = 'singleclass'
Expand Down
2 changes: 1 addition & 1 deletion PPOCRLabel/libs/editinlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ def mouseDoubleClickEvent(self, event):
def leaveEvent(self, event):
# close edit
for i in range(self.count()):
self.closePersistentEditor(self.item(i))
self.closePersistentEditor(self.item(i))
25 changes: 23 additions & 2 deletions PPOCRLabel/libs/shape.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# THE SOFTWARE.
# !/usr/bin/python
# -*- coding: utf-8 -*-
from email.policy import default
import math
import sys

Expand Down Expand Up @@ -46,15 +47,16 @@ class Shape(object):
point_size = 8
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.idx = 0
self.idx = None # bbox order, only for table annotation
self.points = []
self.fill = False
self.selected = False
self.difficult = difficult
self.key_cls = key_cls
self.paintLabel = paintLabel
self.paintIdx = paintIdx
self.locked = False
self.direction = 0
self.center = None
Expand Down Expand Up @@ -164,6 +166,25 @@ def paint(self, painter):
min_y += MIN_Y_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:
color = self.select_fill_color if self.selected else self.fill_color
painter.fillPath(line_path, color)
Expand Down
1 change: 1 addition & 0 deletions PPOCRLabel/resources/strings/strings-en.properties
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ labels=Labels
autoSaveMode=Auto Save mode
singleClsMode=Single Class Mode
displayLabel=Display Labels
displayIndex=Display box index
fileList=File List
files=Files
advancedMode=Advanced Mode
Expand Down
1 change: 1 addition & 0 deletions PPOCRLabel/resources/strings/strings-zh-CN.properties
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ labels=标签
autoSaveMode=自动保存模式
singleClsMode=单一类别模式
displayLabel=显示类别
displayIndex=显示box序号
fileList=文件列表
files=文件
advancedMode=专家模式
Expand Down

0 comments on commit 4cbacc3

Please sign in to comment.