diff --git a/PPOCRLabel.py b/PPOCRLabel.py index d24f8b9..4516305 100644 --- a/PPOCRLabel.py +++ b/PPOCRLabel.py @@ -931,6 +931,15 @@ def get_str(str_id): enabled=True, ) + convertToRect = action( + get_str("convertToRect"), + self.convertToRect, + "Ctrl+T", + "edit", + get_str("convertToRectDetail"), + enabled=False, + ) + settings_action = action( get_str("settings"), self.showSettingsDialog, @@ -1012,6 +1021,7 @@ def get_str(str_id): delete=delete, edit=edit, focusAndZoom=focusAndZoom, + convertToRect=convertToRect, copy=copy, saveRec=saveRec, singleRere=singleRere, @@ -1054,6 +1064,7 @@ def get_str(str_id): createpoly, edit, focusAndZoom, + convertToRect, copy, delete, singleRere, @@ -1078,6 +1089,7 @@ def get_str(str_id): createpoly, edit, focusAndZoom, + convertToRect, copy, delete, singleRere, @@ -1815,6 +1827,7 @@ def shapeSelectionChanged(self, selected_shapes): self.actions.lock.setEnabled(n_selected) self.actions.change_cls.setEnabled(n_selected) self.actions.expand.setEnabled(n_selected) + self.actions.convertToRect.setEnabled(n_selected > 0) def addLabel(self, shape): shape.paintLabel = self.displayLabelOption.isChecked() @@ -3868,70 +3881,115 @@ def get_top_left(rect): [max(p[1] for p in rect) - min(p[1] for p in rect) for rect in rectangles] ) / len(rectangles) threshold = avg_height * row_height_threshold + + # Keep track of original indices to handle duplicates correctly indexed_rects = [(i, get_top_left(rect)) for i, rect in enumerate(rectangles)] indexed_rects.sort(key=lambda x: x[1][1]) + rows = [] current_row = [] - last_y = indexed_rects[0][1][1] - for item in indexed_rects: - i, (x, y) = item - if abs(y - last_y) <= threshold: - current_row.append(item) - else: + if indexed_rects: + last_y = indexed_rects[0][1][1] + for item in indexed_rects: + i, (x, y) = item + if abs(y - last_y) <= threshold: + current_row.append(item) + else: + rows.append(current_row) + current_row = [item] + last_y = y + if current_row: rows.append(current_row) - current_row = [item] - last_y = y - if current_row: - rows.append(current_row) - sorted_rects = [] + + sorted_indices = [] for row in rows: row.sort(key=lambda x: x[1][0]) - sorted_rects.extend([rectangles[i] for i, _ in row]) - return sorted_rects + sorted_indices.extend([i for i, _ in row]) + return sorted_indices def resortBoxPosition(self): - # get original elements - items = [] - for i in range(self.BoxList.count()): - item = self.BoxList.item(i) - items.append({"text": item.text(), "object": item}) - # get coordinate points + # get coordinate points from shapes directly to be more reliable rectangles = [] - for item in items: - text = item["text"] - try: - rect = ast.literal_eval(text) # 转为列表 - rectangles.append(rect) - except (ValueError, SyntaxError) as e: - logger.error(f"Error parsing text: {text}") - continue - # start resort - sorted_rectangles = self.sort_rectangles(rectangles, row_height_threshold=0.5) - # old_idx <--> new_idx - index_map = [] - for sorted_rect in sorted_rectangles: - for old_idx, rect in enumerate(rectangles): - if rect == sorted_rect: - index_map.append(old_idx) - break - # resort BoxList labelList canvas.shapes - items = [self.BoxList.takeItem(0) for _ in range(self.BoxList.count())] + for shape in self.canvas.shapes: + rect = [[int(p.x()), int(p.y())] for p in shape.points] + rectangles.append(rect) + + if not rectangles: + return + + # start resort - now returns indices + index_map = self.sort_rectangles(rectangles, row_height_threshold=0.5) + + if len(index_map) != len(self.canvas.shapes): + logger.error("Resort failed: index map size mismatch") + return + + # resort BoxList, labelList, and canvas.shapes + # Take all items out first + items_box = [self.BoxList.takeItem(0) for _ in range(self.BoxList.count())] items_label = [ self.labelList.takeItem(0) for _ in range(self.labelList.count()) ] - shapes = self.canvas.shapes + shapes = list(self.canvas.shapes) + self.canvas.shapes = [] - for new_idx in range(len(index_map)): - old_idx = index_map[new_idx] - self.BoxList.insertItem(new_idx, items[old_idx]) + for new_idx, old_idx in enumerate(index_map): + self.BoxList.insertItem(new_idx, items_box[old_idx]) self.labelList.insertItem(new_idx, items_label[old_idx]) - self.canvas.shapes.insert(new_idx, shapes[old_idx]) + self.canvas.shapes.append(shapes[old_idx]) + + # Update internal indices and refresh UI + self.updateIndexList() + for i, shape in enumerate(self.canvas.shapes): + shape.idx = i + + self.canvas.update() + self.setDirty() + QMessageBox.information( self, "Information", "resort success!", ) + def convertToRect(self): + if not self.canvas.selectedShapes: + return + + changed = False + for shape in self.canvas.selectedShapes: + if not shape.points: + continue + + if len(shape.points) == 4: + p0, p1, p2, p3 = shape.points + if ( + p0.x() == p3.x() + and p0.y() == p1.y() + and p2.x() == p1.x() + and p2.y() == p3.y() + ): + continue + + min_x = min(p.x() for p in shape.points) + max_x = max(p.x() for p in shape.points) + min_y = min(p.y() for p in shape.points) + max_y = max(p.y() for p in shape.points) + + shape.points = [ + QPointF(min_x, min_y), + QPointF(max_x, min_y), + QPointF(max_x, max_y), + QPointF(min_x, max_y), + ] + shape.close() + changed = True + + if changed: + self.updateBoxlist() + self.setDirty() + self.canvas.repaint() + def inverted(color): return QColor(*[255 - v for v in color.getRgb()]) diff --git a/README.md b/README.md index 0f73830..de5d263 100644 --- a/README.md +++ b/README.md @@ -244,6 +244,7 @@ labeling in the Excel file, the recommended steps are: | Ctrl + R | Re-recognize the selected box | | Ctrl + C | Copy and paste the selected box | | Ctrl + B | Resort Bounding Box Positions | +| Ctrl + T | Convert PolygonBox to RectBox | | Ctrl + Left Mouse Button | Multi select the label box | | Backspace or Delete | Delete the selected box | | Ctrl + V or End | Check image | diff --git a/README_ch.md b/README_ch.md index 0a26c98..cfb3c4f 100644 --- a/README_ch.md +++ b/README_ch.md @@ -220,6 +220,7 @@ PPOCRLabel.exe --lang ch | Ctrl + R | 重新识别所选标记 | | Ctrl + C | 【复制并粘贴】选中的标记框 | | Ctrl + B | 重新排序坐标框位置 | +| Ctrl + T | 将多边形框转换为矩形框 | | Ctrl + 鼠标左键 | 多选标记框 | | Backspace 或 Delete | 删除所选框 | | Ctrl + V 或 End | 确认本张图片标记 | diff --git a/libs/autoDialog.py b/libs/autoDialog.py index 6e605b1..4330f8c 100644 --- a/libs/autoDialog.py +++ b/libs/autoDialog.py @@ -72,12 +72,11 @@ def run(self): "The size of %s is too small to be recognised", img_path, ) - self.result_dic = None + self.result_dic = [] # Clear it instead of None # 结果保存 - if self.result_dic is None or len(self.result_dic) == 0: + if not self.result_dic: logger.warning("No text detected in file %s", img_path) - pass else: strs = "" for res in self.result_dic: @@ -93,12 +92,16 @@ def run(self): + json.dumps(posi) + "\n" ) - # Sending large amounts of data repeatedly through pyqtSignal may affect the program efficiency + self.listValue.emit(strs) self.mainThread.result_dic = self.result_dic self.mainThread.filePath = img_path - # 保存 self.mainThread.saveFile(mode="Auto") + # CRITICAL: Clear the result_dic after saving to prevent it from + # leaking into the next image or back into the main UI + self.mainThread.result_dic = [] + self.result_dic = [] + findex += 1 self.progressBarValue.emit(findex) else: diff --git a/libs/canvas.py b/libs/canvas.py index 14fe9e6..bf96c6b 100644 --- a/libs/canvas.py +++ b/libs/canvas.py @@ -472,7 +472,15 @@ def boundedMoveVertex(self, pos): else: shiftPos = pos - point - if [shape[0].x(), shape[0].y(), shape[2].x(), shape[2].y()] == [ + # Symmetric resizing with Ctrl + is_ctrl_pressed = int(QApplication.keyboardModifiers()) == Qt.ControlModifier + + if len(shape.points) == 4 and [ + shape[0].x(), + shape[0].y(), + shape[2].x(), + shape[2].y(), + ] == [ shape[3].x(), shape[1].y(), shape[1].x(), @@ -492,8 +500,22 @@ def boundedMoveVertex(self, pos): shape.moveVertexBy(rindex, rshift) shape.moveVertexBy(lindex, lshift) + if is_ctrl_pressed: + opp_index = (index + 2) % 4 + shape.moveVertexBy(opp_index, -shiftPos) + shape.moveVertexBy((opp_index + 1) % 4, -lshift) + shape.moveVertexBy((opp_index + 3) % 4, -rshift) + else: shape.moveVertexBy(index, shiftPos) + if is_ctrl_pressed and len(shape.points) > 1: + # Calculate symmetric opposite index for simple shapes + if len(shape.points) == 4: + opp_index = (index + 2) % 4 + shape.moveVertexBy(opp_index, -shiftPos) + elif len(shape.points) == 2: + opp_index = (index + 1) % 2 + shape.moveVertexBy(opp_index, -shiftPos) def boundedMoveShape(self, shapes, pos): if type(shapes).__name__ != "list": diff --git a/libs/resources.py b/libs/resources.py index 1147fd7..f33c2f8 100644 --- a/libs/resources.py +++ b/libs/resources.py @@ -9092,7 +9092,7 @@ \xc2\xbe\x6b\xc0\x55\xc8\x31\xa0\x80\x1e\x20\x21\xee\xf8\x2f\xe5\ \xea\x8d\x7f\x05\xf8\x03\xd8\xcb\xf0\xd4\x8e\x80\x5e\x37\x00\x00\ \x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x0e\x91\ +\x00\x00\x0e\xe9\ \x73\ \x61\x76\x65\x41\x73\x44\x65\x74\x61\x69\x6c\x3d\xe5\xb0\x87\xe6\ \xa0\x87\xe7\xad\xbe\xe4\xbf\x9d\xe5\xad\x98\xe5\x88\xb0\xe5\x85\ @@ -9327,7 +9327,12 @@ \x73\x44\x65\x74\x61\x69\x6c\x3d\xe5\xba\x94\xe7\x94\xa8\xe7\xa8\ \x8b\xe5\xba\x8f\xe8\xae\xbe\xe7\xbd\xae\x0a\x66\x6f\x6e\x74\x53\ \x69\x7a\x65\x3d\xe5\xad\x97\xe4\xbd\x93\xe5\xa4\xa7\xe5\xb0\x8f\ -\ +\x0a\x63\x6f\x6e\x76\x65\x72\x74\x54\x6f\x52\x65\x63\x74\x3d\xe8\ +\xbd\xac\xe6\x8d\xa2\xe4\xb8\xba\xe7\x9f\xa9\xe5\xbd\xa2\xe6\xa1\ +\x86\x0a\x63\x6f\x6e\x76\x65\x72\x74\x54\x6f\x52\x65\x63\x74\x44\ +\x65\x74\x61\x69\x6c\x3d\xe5\xb0\x86\xe5\xa4\x9a\xe8\xbe\xb9\xe5\ +\xbd\xa2\xe6\xa1\x86\xe8\xbd\xac\xe6\x8d\xa2\xe4\xb8\xba\xe7\x9f\ +\xa9\xe5\xbd\xa2\xe6\xa1\x86\x0a\ \x00\x00\x03\x12\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -11017,7 +11022,7 @@ \xbe\xde\xa3\x73\x07\x05\x00\x88\x83\x48\x18\x92\xaf\x02\xff\x03\ \x39\x5c\xc9\x23\xd1\xf6\x50\x87\x00\x00\x00\x00\x49\x45\x4e\x44\ \xae\x42\x60\x82\ -\x00\x00\x0e\x89\ +\x00\x00\x0e\xdd\ \x6f\ \x70\x65\x6e\x46\x69\x6c\x65\x3d\x4f\x70\x65\x6e\x0a\x6f\x70\x65\ \x6e\x46\x69\x6c\x65\x44\x65\x74\x61\x69\x6c\x3d\x4f\x70\x65\x6e\ @@ -11251,7 +11256,12 @@ \x73\x0a\x73\x65\x74\x74\x69\x6e\x67\x73\x44\x65\x74\x61\x69\x6c\ \x3d\x41\x70\x70\x6c\x69\x63\x61\x74\x69\x6f\x6e\x20\x73\x65\x74\ \x74\x69\x6e\x67\x73\x0a\x66\x6f\x6e\x74\x53\x69\x7a\x65\x3d\x46\ -\x6f\x6e\x74\x20\x53\x69\x7a\x65\ +\x6f\x6e\x74\x20\x53\x69\x7a\x65\x0a\x63\x6f\x6e\x76\x65\x72\x74\ +\x54\x6f\x52\x65\x63\x74\x3d\x43\x6f\x6e\x76\x65\x72\x74\x20\x74\ +\x6f\x20\x52\x65\x63\x74\x42\x6f\x78\x0a\x63\x6f\x6e\x76\x65\x72\ +\x74\x54\x6f\x52\x65\x63\x74\x44\x65\x74\x61\x69\x6c\x3d\x43\x6f\ +\x6e\x76\x65\x72\x74\x20\x50\x6f\x6c\x79\x67\x6f\x6e\x42\x6f\x78\ +\x20\x74\x6f\x20\x52\x65\x63\x74\x42\x6f\x78\x0a\ \x00\x00\x0e\x35\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -11654,21 +11664,21 @@ \x00\x00\x00\xfc\x00\x00\x00\x00\x00\x01\x00\x02\x2b\xd3\ \x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x02\x30\x8c\ \x00\x00\x01\x20\x00\x00\x00\x00\x00\x01\x00\x02\x34\xdb\ -\x00\x00\x01\x40\x00\x00\x00\x00\x00\x01\x00\x02\x43\x70\ -\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x46\x86\ -\x00\x00\x01\x74\x00\x00\x00\x00\x00\x01\x00\x02\x4a\xd8\ -\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x01\x00\x02\x4f\x0e\ -\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x02\x58\x52\ -\x00\x00\x01\xb6\x00\x00\x00\x00\x00\x01\x00\x02\x60\xae\ -\x00\x00\x01\xc8\x00\x00\x00\x00\x00\x01\x00\x02\x62\x01\ -\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x02\x6b\x52\ -\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x70\xab\ -\x00\x00\x02\x04\x00\x00\x00\x00\x00\x01\x00\x02\x7c\x90\ -\x00\x00\x02\x20\x00\x00\x00\x00\x00\x01\x00\x02\x7f\x37\ -\x00\x00\x02\x3a\x00\x00\x00\x00\x00\x01\x00\x02\x8f\xb3\ -\x00\x00\x02\x4e\x00\x00\x00\x00\x00\x01\x00\x02\x9a\xb2\ -\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x02\xab\x2b\ -\x00\x00\x02\x7e\x00\x00\x00\x00\x00\x01\x00\x02\xb9\xb8\ +\x00\x00\x01\x40\x00\x00\x00\x00\x00\x01\x00\x02\x43\xc8\ +\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x46\xde\ +\x00\x00\x01\x74\x00\x00\x00\x00\x00\x01\x00\x02\x4b\x30\ +\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x01\x00\x02\x4f\x66\ +\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x02\x58\xaa\ +\x00\x00\x01\xb6\x00\x00\x00\x00\x00\x01\x00\x02\x61\x06\ +\x00\x00\x01\xc8\x00\x00\x00\x00\x00\x01\x00\x02\x62\x59\ +\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x02\x6b\xaa\ +\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x71\x03\ +\x00\x00\x02\x04\x00\x00\x00\x00\x00\x01\x00\x02\x7c\xe8\ +\x00\x00\x02\x20\x00\x00\x00\x00\x00\x01\x00\x02\x7f\x8f\ +\x00\x00\x02\x3a\x00\x00\x00\x00\x00\x01\x00\x02\x90\x0b\ +\x00\x00\x02\x4e\x00\x00\x00\x00\x00\x01\x00\x02\x9b\x0a\ +\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x02\xab\x83\ +\x00\x00\x02\x7e\x00\x00\x00\x00\x00\x01\x00\x02\xba\x64\ " qt_resource_struct_v2 = b"\ @@ -11715,36 +11725,36 @@ \x00\x00\x01\x0c\x00\x00\x00\x00\x00\x01\x00\x02\x30\x8c\ \x00\x00\x01\x9c\xc0\x4e\x25\x94\ \x00\x00\x01\x20\x00\x00\x00\x00\x00\x01\x00\x02\x34\xdb\ -\x00\x00\x01\x9d\x27\x67\x29\xa7\ -\x00\x00\x01\x40\x00\x00\x00\x00\x00\x01\x00\x02\x43\x70\ +\x00\x00\x01\x9d\x2a\x26\xbc\xd6\ +\x00\x00\x01\x40\x00\x00\x00\x00\x00\x01\x00\x02\x43\xc8\ \x00\x00\x01\x9c\xc0\x4e\x25\x92\ -\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x46\x86\ +\x00\x00\x01\x5a\x00\x00\x00\x00\x00\x01\x00\x02\x46\xde\ \x00\x00\x01\x9c\xc0\x4e\x25\x91\ -\x00\x00\x01\x74\x00\x00\x00\x00\x00\x01\x00\x02\x4a\xd8\ +\x00\x00\x01\x74\x00\x00\x00\x00\x00\x01\x00\x02\x4b\x30\ \x00\x00\x01\x9c\xc0\x4e\x25\x94\ -\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x01\x00\x02\x4f\x0e\ +\x00\x00\x01\x8a\x00\x00\x00\x00\x00\x01\x00\x02\x4f\x66\ \x00\x00\x01\x9c\xc0\x4e\x25\x90\ -\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x02\x58\x52\ +\x00\x00\x01\xa4\x00\x00\x00\x00\x00\x01\x00\x02\x58\xaa\ \x00\x00\x01\x9c\xc0\x4e\x25\x90\ -\x00\x00\x01\xb6\x00\x00\x00\x00\x00\x01\x00\x02\x60\xae\ +\x00\x00\x01\xb6\x00\x00\x00\x00\x00\x01\x00\x02\x61\x06\ \x00\x00\x01\x9c\xc0\x4e\x25\x91\ -\x00\x00\x01\xc8\x00\x00\x00\x00\x00\x01\x00\x02\x62\x01\ +\x00\x00\x01\xc8\x00\x00\x00\x00\x00\x01\x00\x02\x62\x59\ \x00\x00\x01\x9c\xc0\x4e\x25\x92\ -\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x02\x6b\x52\ +\x00\x00\x01\xda\x00\x00\x00\x00\x00\x01\x00\x02\x6b\xaa\ \x00\x00\x01\x9c\xc0\x4e\x25\x91\ -\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x70\xab\ +\x00\x00\x01\xf2\x00\x00\x00\x00\x00\x01\x00\x02\x71\x03\ \x00\x00\x01\x9c\xc0\x4e\x25\x94\ -\x00\x00\x02\x04\x00\x00\x00\x00\x00\x01\x00\x02\x7c\x90\ +\x00\x00\x02\x04\x00\x00\x00\x00\x00\x01\x00\x02\x7c\xe8\ \x00\x00\x01\x9c\xc0\x4e\x25\x92\ -\x00\x00\x02\x20\x00\x00\x00\x00\x00\x01\x00\x02\x7f\x37\ +\x00\x00\x02\x20\x00\x00\x00\x00\x00\x01\x00\x02\x7f\x8f\ \x00\x00\x01\x9c\xc0\x4e\x25\x93\ -\x00\x00\x02\x3a\x00\x00\x00\x00\x00\x01\x00\x02\x8f\xb3\ +\x00\x00\x02\x3a\x00\x00\x00\x00\x00\x01\x00\x02\x90\x0b\ \x00\x00\x01\x9c\xc0\x4e\x25\x93\ -\x00\x00\x02\x4e\x00\x00\x00\x00\x00\x01\x00\x02\x9a\xb2\ +\x00\x00\x02\x4e\x00\x00\x00\x00\x00\x01\x00\x02\x9b\x0a\ \x00\x00\x01\x9c\xc0\x4e\x25\x93\ -\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x02\xab\x2b\ -\x00\x00\x01\x9d\x27\x67\x05\xcb\ -\x00\x00\x02\x7e\x00\x00\x00\x00\x00\x01\x00\x02\xb9\xb8\ +\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x02\xab\x83\ +\x00\x00\x01\x9d\x2a\x26\x8b\xb5\ +\x00\x00\x02\x7e\x00\x00\x00\x00\x00\x01\x00\x02\xba\x64\ \x00\x00\x01\x9c\xc0\x4e\x25\x93\ " diff --git a/libs/shape.py b/libs/shape.py index 07906f6..e32f1b2 100644 --- a/libs/shape.py +++ b/libs/shape.py @@ -104,14 +104,12 @@ def rotatePoint(self, p, theta): return pRes def close(self): - try: - self.center = QPointF( - (self.points[0].x() + self.points[2].x()) / 2, - (self.points[0].y() + self.points[2].y()) / 2, - ) - except Exception: + if self.points: + x = sum(p.x() for p in self.points) / len(self.points) + y = sum(p.y() for p in self.points) / len(self.points) + self.center = QPointF(x, y) + else: self.center = None - logger.warning("The XY coordinates of QPointF are not detectable!") self._closed = True def reachMaxPoints(self): diff --git a/libs/utils.py b/libs/utils.py index 7847302..467c5dd 100644 --- a/libs/utils.py +++ b/libs/utils.py @@ -322,6 +322,7 @@ def keysInfo(lang="en"): "Ctrl + R\t\t重新识别所选标记\n" "Ctrl + C\t\t复制并粘贴选中的标记框\n" "Ctrl + B\t\t重新排序坐标框位置\n" + "Ctrl + T\t\t将多边形框转换为矩形框\n" "Ctrl + 鼠标左键\t\t多选标记框\n" "Backspace 或 Delete\t\t删除所选框\n" "Ctrl + V 或 End\t\t确认本张图片标记\n" @@ -350,6 +351,7 @@ def keysInfo(lang="en"): "Ctrl + R\t\tRe-recognize the selected box\n" "Ctrl + C\t\tCopy and paste the selected\n" "Ctrl + B\t\tResort the position of bounding boxes\n" + "Ctrl + T\t\tConvert PolygonBox to RectBox\n" "\t\t\tbox\n" "\n" "Ctrl + Left Mouse\tMulti select the label\n" diff --git a/resources/strings/strings-en.properties b/resources/strings/strings-en.properties index 25f65a0..87627ab 100644 --- a/resources/strings/strings-en.properties +++ b/resources/strings/strings-en.properties @@ -124,4 +124,6 @@ resortposition=Resort position resortpositiondetail=Resort the bounding box positions settings=Settings settingsDetail=Application settings -fontSize=Font Size \ No newline at end of file +fontSize=Font Size +convertToRect=Convert to RectBox +convertToRectDetail=Convert PolygonBox to RectBox diff --git a/resources/strings/strings-zh-CN.properties b/resources/strings/strings-zh-CN.properties index 73593b7..ef7dd63 100644 --- a/resources/strings/strings-zh-CN.properties +++ b/resources/strings/strings-zh-CN.properties @@ -124,4 +124,6 @@ resortposition=重新排序位置 resortpositiondetail=重新排序坐标框位置 settings=设置 settingsDetail=应用程序设置 -fontSize=字体大小 \ No newline at end of file +fontSize=字体大小 +convertToRect=转换为矩形框 +convertToRectDetail=将多边形框转换为矩形框