diff --git a/PPOCRLabel/PPOCRLabel.py b/PPOCRLabel/PPOCRLabel.py index 34c045e96aa10ba678447eefc1d007f9042804b8..a2906e71756e4f4cdd1d254c693b2cce70fe8b15 100644 --- a/PPOCRLabel/PPOCRLabel.py +++ b/PPOCRLabel/PPOCRLabel.py @@ -152,16 +152,6 @@ class MainWindow(QMainWindow): self.fileListWidget.setIconSize(QSize(25, 25)) filelistLayout.addWidget(self.fileListWidget) - self.AutoRecognition = QToolButton() - self.AutoRecognition.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - self.AutoRecognition.setIcon(newIcon('Auto')) - autoRecLayout = QHBoxLayout() - autoRecLayout.setContentsMargins(0, 0, 0, 0) - autoRecLayout.addWidget(self.AutoRecognition) - autoRecContainer = QWidget() - autoRecContainer.setLayout(autoRecLayout) - filelistLayout.addWidget(autoRecContainer) - fileListContainer = QWidget() fileListContainer.setLayout(filelistLayout) self.fileListName = getStr('fileList') @@ -172,17 +162,30 @@ class MainWindow(QMainWindow): # ================== Key List ================== if self.kie_mode: - # self.keyList = QListWidget() self.keyList = UniqueLabelQListWidget() - # self.keyList.itemSelectionChanged.connect(self.keyListSelectionChanged) - # self.keyList.itemDoubleClicked.connect(self.editBox) - # self.keyList.itemChanged.connect(self.keyListItemChanged) + + # set key list height + key_list_height = int(QApplication.desktop().height() // 4) + if key_list_height < 50: + key_list_height = 50 + self.keyList.setMaximumHeight(key_list_height) + self.keyListDockName = getStr('keyListTitle') self.keyListDock = QDockWidget(self.keyListDockName, self) self.keyListDock.setWidget(self.keyList) self.keyListDock.setFeatures(QDockWidget.NoDockWidgetFeatures) filelistLayout.addWidget(self.keyListDock) + self.AutoRecognition = QToolButton() + self.AutoRecognition.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + self.AutoRecognition.setIcon(newIcon('Auto')) + autoRecLayout = QHBoxLayout() + autoRecLayout.setContentsMargins(0, 0, 0, 0) + autoRecLayout.addWidget(self.AutoRecognition) + autoRecContainer = QWidget() + autoRecContainer.setLayout(autoRecLayout) + filelistLayout.addWidget(autoRecContainer) + # ================== Right Area ================== listLayout = QVBoxLayout() listLayout.setContentsMargins(0, 0, 0, 0) @@ -431,8 +434,7 @@ class MainWindow(QMainWindow): # ================== New Actions ================== edit = action(getStr('editLabel'), self.editLabel, - 'Ctrl+E', 'edit', getStr('editLabelDetail'), - enabled=False) + 'Ctrl+E', 'edit', getStr('editLabelDetail'), enabled=False) AutoRec = action(getStr('autoRecognition'), self.autoRecognition, '', 'Auto', getStr('autoRecognition'), enabled=False) @@ -465,11 +467,10 @@ class MainWindow(QMainWindow): 'Ctrl+Z', "undo", getStr("undo"), enabled=False) change_cls = action(getStr("keyChange"), self.change_box_key, - 'Ctrl+B', "edit", getStr("keyChange"), enabled=False) + 'Ctrl+X', "edit", getStr("keyChange"), enabled=False) lock = action(getStr("lockBox"), self.lockSelectedShape, - None, "lock", getStr("lockBoxDetail"), - enabled=False) + None, "lock", getStr("lockBoxDetail"), enabled=False) self.editButton.setDefaultAction(edit) self.newButton.setDefaultAction(create) @@ -534,9 +535,10 @@ class MainWindow(QMainWindow): fileMenuActions=(opendir, open_dataset_dir, saveLabel, resetAll, quit), beginner=(), advanced=(), editMenu=(createpoly, edit, copy, delete, singleRere, None, undo, undoLastPoint, - None, rotateLeft, rotateRight, None, color1, self.drawSquaresOption, lock), + None, rotateLeft, rotateRight, None, color1, self.drawSquaresOption, lock, + None, change_cls), beginnerContext=( - create, edit, copy, delete, singleRere, rotateLeft, rotateRight, lock, change_cls), + create, edit, copy, delete, singleRere, rotateLeft, rotateRight, lock, change_cls), advancedContext=(createMode, editMode, edit, copy, delete, shapeLineColor, shapeFillColor), onLoadActive=(create, createMode, editMode), @@ -1105,7 +1107,9 @@ class MainWindow(QMainWindow): shapes = [format_shape(shape) for shape in self.canvas.shapes if shape.line_color != DEFAULT_LOCK_COLOR] # Can add differrent annotation formats here for box in self.result_dic: - trans_dic = {"label": box[1][0], "points": box[0], "difficult": False, "key_cls": "None"} + trans_dic = {"label": box[1][0], "points": box[0], "difficult": False} + if self.kie_mode: + trans_dic.update({"key_cls": "None"}) if trans_dic["label"] == "" and mode == 'Auto': continue shapes.append(trans_dic) @@ -1113,8 +1117,10 @@ class MainWindow(QMainWindow): try: trans_dic = [] for box in shapes: - trans_dic.append({"transcription": box['label'], "points": box['points'], - "difficult": box['difficult'], "key_cls": box['key_cls']}) + trans_dict = {"transcription": box['label'], "points": box['points'], "difficult": box['difficult']} + if self.kie_mode: + trans_dict.update({"key_cls": box['key_cls']}) + trans_dic.append(trans_dict) self.PPlabel[annotationFilePath] = trans_dic if mode == 'Auto': self.Cachelabel[annotationFilePath] = trans_dic @@ -1424,15 +1430,17 @@ class MainWindow(QMainWindow): # box['ratio'] of the shapes saved in lockedShapes contains the ratio of the # four corner coordinates of the shapes to the height and width of the image for box in self.canvas.lockedShapes: + key_cls = None if not self.kie_mode else box['key_cls'] if self.canvas.isInTheSameImage: shapes.append((box['transcription'], [[s[0] * width, s[1] * height] for s in box['ratio']], - DEFAULT_LOCK_COLOR, box['key_cls'], box['difficult'])) + DEFAULT_LOCK_COLOR, key_cls, box['difficult'])) else: shapes.append(('锁定框:待检测', [[s[0] * width, s[1] * height] for s in box['ratio']], - DEFAULT_LOCK_COLOR, box['key_cls'], box['difficult'])) + DEFAULT_LOCK_COLOR, key_cls, box['difficult'])) if imgidx in self.PPlabel.keys(): for box in self.PPlabel[imgidx]: - shapes.append((box['transcription'], box['points'], None, box['key_cls'], box['difficult'])) + key_cls = None if not self.kie_mode else box['key_cls'] + shapes.append((box['transcription'], box['points'], None, key_cls, box['difficult'])) self.loadLabels(shapes) self.canvas.verified = False @@ -1460,6 +1468,7 @@ class MainWindow(QMainWindow): def adjustScale(self, initial=False): value = self.scalers[self.FIT_WINDOW if initial else self.zoomMode]() self.zoomWidget.setValue(int(100 * value)) + self.imageSlider.setValue(self.zoomWidget.value()) # set zoom slider value def scaleFitWindow(self): """Figure out the size of the pixmap in order to fit the main widget.""" @@ -1600,7 +1609,6 @@ class MainWindow(QMainWindow): else: self.keyDialog.labelList.addItems(self.existed_key_cls_set) - def importDirImages(self, dirpath, isDelete=False): if not self.mayContinue() or not dirpath: return @@ -2200,13 +2208,22 @@ class MainWindow(QMainWindow): print('The program will automatically save once after confirming 5 images (default)') def change_box_key(self): + if not self.kie_mode: + return key_text, _ = self.keyDialog.popUp(self.key_previous_text) if key_text is None: return self.key_previous_text = key_text for shape in self.canvas.selectedShapes: shape.key_cls = key_text + if not self.keyList.findItemsByLabel(key_text): + item = self.keyList.createItemFromLabel(key_text) + self.keyList.addItem(item) + rgb = self._get_rgb_by_label(key_text, self.kie_mode) + self.keyList.setItemLabel(item, key_text, rgb) + self._update_shape_color(shape) + self.keyDialog.addLabelHistory(key_text) def undoShapeEdit(self): self.canvas.restoreShape() @@ -2250,9 +2267,10 @@ class MainWindow(QMainWindow): shapes = [format_shape(shape) for shape in self.canvas.selectedShapes] trans_dic = [] for box in shapes: - trans_dic.append({"transcription": box['label'], "ratio": box['ratio'], - "difficult": box['difficult'], - "key_cls": "None" if "key_cls" not in box else box["key_cls"]}) + trans_dict = {"transcription": box['label'], "ratio": box['ratio'], "difficult": box['difficult']} + if self.kie_mode: + trans_dict.update({"key_cls": box["key_cls"]}) + trans_dic.append(trans_dict) self.canvas.lockedShapes = trans_dic self.actions.save.setEnabled(True) diff --git a/PPOCRLabel/README.md b/PPOCRLabel/README.md index 4d25e670ae6d07d569a247bc5f9c35c939b23f8e..9fc5f3437af0076bdbe2afedf8c194009c2c0e72 100644 --- a/PPOCRLabel/README.md +++ b/PPOCRLabel/README.md @@ -9,7 +9,7 @@ PPOCRLabel is a semi-automatic graphic annotation tool suitable for OCR field, w ### Recent Update - 2022.02:(by [PeterH0323](https://github.com/peterh0323) ) - - Added KIE mode, for [detection + identification + keyword extraction] labeling. + - Add KIE Mode by using `--kie`, for [detection + identification + keyword extraction] labeling. - 2022.01:(by [PeterH0323](https://github.com/peterh0323) ) - Improve user experience: prompt for the number of files and labels, optimize interaction, and fix bugs such as only use CPU when inference - 2021.11.17: @@ -156,6 +156,7 @@ python PPOCRLabel.py --kie True # [KIE mode] for [detection + recognition + keyw | X | Rotate the box anti-clockwise | | C | Rotate the box clockwise | | Ctrl + E | Edit label of the selected box | +| Ctrl + X | Change key class of the box when enable `--kie` | | Ctrl + R | Re-recognize the selected box | | Ctrl + C | Copy and paste the selected box | | Ctrl + Left Mouse Button | Multi select the label box | diff --git a/PPOCRLabel/README_ch.md b/PPOCRLabel/README_ch.md index 3f8dc4f0c6b7cc88a71409d123f598e74b3f2cad..67c2e877288a73b07e5dd1ca90993bde8a96bf20 100644 --- a/PPOCRLabel/README_ch.md +++ b/PPOCRLabel/README_ch.md @@ -9,7 +9,7 @@ PPOCRLabel是一款适用于OCR领域的半自动化图形标注工具,内置P #### 近期更新 - 2022.02:(by [PeterH0323](https://github.com/peterh0323) ) - - 新增:KIE 功能,用于打【检测+识别+关键字提取】的标签 + - 新增:使用 `--kie` 进入 KIE 功能,用于打【检测+识别+关键字提取】的标签 - 2022.01:(by [PeterH0323](https://github.com/peterh0323) ) - 提升用户体验:新增文件与标记数目提示、优化交互、修复gpu使用等问题 - 2021.11.17: @@ -137,25 +137,26 @@ python PPOCRLabel.py --lang ch --kie True # 启动 【KIE 模式】,用于打 ### 3.1 快捷键 -| 快捷键 | 说明 | -|------------------|----------------| -| Ctrl + shift + R | 对当前图片的所有标记重新识别 | -| W | 新建矩形框 | -| Q | 新建四点框 | -| X | 框逆时针旋转 | -| C | 框顺时针旋转 | -| Ctrl + E | 编辑所选框标签 | -| Ctrl + R | 重新识别所选标记 | -| Ctrl + C | 复制并粘贴选中的标记框 | -| Ctrl + 鼠标左键 | 多选标记框 | -| Alt + X | 删除所选框 | -| Ctrl + V | 确认本张图片标记 | -| Ctrl + Shift + d | 删除本张图片 | -| D | 下一张图片 | -| A | 上一张图片 | -| Ctrl++ | 缩小 | -| Ctrl-- | 放大 | -| ↑→↓← | 移动标记框 | +| 快捷键 | 说明 | +|------------------|---------------------------------| +| Ctrl + shift + R | 对当前图片的所有标记重新识别 | +| W | 新建矩形框 | +| Q | 新建四点框 | +| X | 框逆时针旋转 | +| C | 框顺时针旋转 | +| Ctrl + E | 编辑所选框标签 | +| Ctrl + X | `--kie` 模式下,修改 Box 的关键字种类 | +| Ctrl + R | 重新识别所选标记 | +| Ctrl + C | 复制并粘贴选中的标记框 | +| Ctrl + 鼠标左键 | 多选标记框 | +| Alt + X | 删除所选框 | +| Ctrl + V | 确认本张图片标记 | +| Ctrl + Shift + d | 删除本张图片 | +| D | 下一张图片 | +| A | 上一张图片 | +| Ctrl++ | 缩小 | +| Ctrl-- | 放大 | +| ↑→↓← | 移动标记框 | ### 3.2 内置模型 diff --git a/PPOCRLabel/libs/canvas.py b/PPOCRLabel/libs/canvas.py index 095fe5ab06553dcb05c8bcc061f950ded606ebb3..db1071e9b73486d9ca1e6da2f711a0ebf3aa160a 100644 --- a/PPOCRLabel/libs/canvas.py +++ b/PPOCRLabel/libs/canvas.py @@ -546,7 +546,7 @@ class Canvas(QWidget): # Give up if both fail. for shape in shapes: point = shape[0] - offset = QPointF(2.0, 2.0) + offset = QPointF(5.0, 5.0) self.calculateOffsets(shape, point) self.prevPoint = point if not self.boundedMoveShape(shape, point - offset): diff --git a/PPOCRLabel/libs/unique_label_qlist_widget.py b/PPOCRLabel/libs/unique_label_qlist_widget.py index f1eff7a172d3fecf9c18579ccead5f62ba65ecd5..07ae05fe67d8b1a924d04666220e33f664891e83 100644 --- a/PPOCRLabel/libs/unique_label_qlist_widget.py +++ b/PPOCRLabel/libs/unique_label_qlist_widget.py @@ -1,6 +1,6 @@ # -*- encoding: utf-8 -*- -from PyQt5.QtCore import Qt +from PyQt5.QtCore import Qt, QSize from PyQt5 import QtWidgets @@ -40,6 +40,7 @@ class UniqueLabelQListWidget(EscapableQListWidget): qlabel.setText(' {} '.format(*color, label)) qlabel.setAlignment(Qt.AlignBottom) - item.setSizeHint(qlabel.sizeHint()) + # item.setSizeHint(qlabel.sizeHint()) + item.setSizeHint(QSize(25, 25)) self.setItemWidget(item, qlabel)