mirror of
				https://github.com/PaddlePaddle/PaddleOCR.git
				synced 2025-10-31 01:39:11 +00:00 
			
		
		
		
	
						commit
						2acde6c503
					
				| @ -21,12 +21,13 @@ import os.path | |||||||
| import platform | import platform | ||||||
| import subprocess | import subprocess | ||||||
| import sys | import sys | ||||||
|  | import xlrd | ||||||
| from functools import partial | from functools import partial | ||||||
| 
 | 
 | ||||||
| from PyQt5.QtCore import QSize, Qt, QPoint, QByteArray, QTimer, QFileInfo, QPointF, QProcess | from PyQt5.QtCore import QSize, Qt, QPoint, QByteArray, QTimer, QFileInfo, QPointF, QProcess | ||||||
| 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, \ |     QSlider, QGraphicsOpacityEffect, QMessageBox, QListView, QScrollArea, QWidgetAction, QApplication, QLabel, QGridLayout, \ | ||||||
|     QFileDialog, QListWidgetItem, QComboBox, QDialog |     QFileDialog, QListWidgetItem, QComboBox, QDialog | ||||||
| 
 | 
 | ||||||
| __dir__ = os.path.dirname(os.path.abspath(__file__)) | __dir__ = os.path.dirname(os.path.abspath(__file__)) | ||||||
| @ -36,7 +37,7 @@ sys.path.append(os.path.abspath(os.path.join(__dir__, '../..'))) | |||||||
| sys.path.append(os.path.abspath(os.path.join(__dir__, '../PaddleOCR'))) | sys.path.append(os.path.abspath(os.path.join(__dir__, '../PaddleOCR'))) | ||||||
| sys.path.append("..") | sys.path.append("..") | ||||||
| 
 | 
 | ||||||
| from paddleocr import PaddleOCR | from paddleocr import PaddleOCR, PPStructure | ||||||
| from libs.constants import * | from libs.constants import * | ||||||
| from libs.utils import * | from libs.utils import * | ||||||
| from libs.labelColor import label_colormap | from libs.labelColor import label_colormap | ||||||
| @ -100,9 +101,15 @@ class MainWindow(QMainWindow): | |||||||
|                              use_gpu=gpu, |                              use_gpu=gpu, | ||||||
|                              lang=lang, |                              lang=lang, | ||||||
|                              show_log=False) |                              show_log=False) | ||||||
|  |         self.table_ocr = PPStructure(use_pdserving=False, | ||||||
|  |                                      use_gpu=gpu, | ||||||
|  |                                      lang=lang, | ||||||
|  |                                      layout=False, | ||||||
|  |                                      show_log=False) | ||||||
| 
 | 
 | ||||||
|         if os.path.exists('./data/paddle.png'): |         if os.path.exists('./data/paddle.png'): | ||||||
|             result = self.ocr.ocr('./data/paddle.png', cls=True, det=True) |             result = self.ocr.ocr('./data/paddle.png', cls=True, det=True) | ||||||
|  |             result = self.table_ocr('./data/paddle.png', return_ocr_result_in_table=True) | ||||||
| 
 | 
 | ||||||
|         # For loading all image under a directory |         # For loading all image under a directory | ||||||
|         self.mImgList = [] |         self.mImgList = [] | ||||||
| @ -196,16 +203,25 @@ class MainWindow(QMainWindow): | |||||||
|         self.reRecogButton.setIcon(newIcon('reRec', 30)) |         self.reRecogButton.setIcon(newIcon('reRec', 30)) | ||||||
|         self.reRecogButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) |         self.reRecogButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) | ||||||
| 
 | 
 | ||||||
|  |         self.tableRecButton = QToolButton() | ||||||
|  |         self.tableRecButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) | ||||||
|  | 
 | ||||||
|         self.newButton = QToolButton() |         self.newButton = QToolButton() | ||||||
|         self.newButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) |         self.newButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) | ||||||
|  |         self.createpolyButton = QToolButton() | ||||||
|  |         self.createpolyButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) | ||||||
|  | 
 | ||||||
|         self.SaveButton = QToolButton() |         self.SaveButton = QToolButton() | ||||||
|         self.SaveButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) |         self.SaveButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) | ||||||
|         self.DelButton = QToolButton() |         self.DelButton = QToolButton() | ||||||
|         self.DelButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) |         self.DelButton.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) | ||||||
| 
 | 
 | ||||||
|         leftTopToolBox = QHBoxLayout() |         leftTopToolBox = QGridLayout() | ||||||
|         leftTopToolBox.addWidget(self.newButton) |         leftTopToolBox.addWidget(self.newButton, 0, 0, 1, 1) | ||||||
|         leftTopToolBox.addWidget(self.reRecogButton) |         leftTopToolBox.addWidget(self.createpolyButton, 0, 1, 1, 1) | ||||||
|  |         leftTopToolBox.addWidget(self.reRecogButton, 1, 0, 1, 1) | ||||||
|  |         leftTopToolBox.addWidget(self.tableRecButton, 1, 1, 1, 1) | ||||||
|  | 
 | ||||||
|         leftTopToolBoxContainer = QWidget() |         leftTopToolBoxContainer = QWidget() | ||||||
|         leftTopToolBoxContainer.setLayout(leftTopToolBox) |         leftTopToolBoxContainer.setLayout(leftTopToolBox) | ||||||
|         listLayout.addWidget(leftTopToolBoxContainer) |         listLayout.addWidget(leftTopToolBoxContainer) | ||||||
| @ -446,7 +462,13 @@ class MainWindow(QMainWindow): | |||||||
|                             'Ctrl+R', 'reRec', getStr('singleRe'), enabled=False) |                             'Ctrl+R', 'reRec', getStr('singleRe'), enabled=False) | ||||||
| 
 | 
 | ||||||
|         createpoly = action(getStr('creatPolygon'), self.createPolygon, |         createpoly = action(getStr('creatPolygon'), self.createPolygon, | ||||||
|                             'q', 'new', getStr('creatPolygon'), enabled=True) |                             'q', 'new', getStr('creatPolygon'), enabled=False) | ||||||
|  |          | ||||||
|  |         tableRec = action(getStr('TableRecognition'), self.TableRecognition, | ||||||
|  |                         '', 'Auto', getStr('TableRecognition'), enabled=False) | ||||||
|  | 
 | ||||||
|  |         cellreRec = action(getStr('cellreRecognition'), self.cellreRecognition, | ||||||
|  |                         '', 'reRec', getStr('cellreRecognition'), enabled=False) | ||||||
| 
 | 
 | ||||||
|         saveRec = action(getStr('saveRec'), self.saveRecResult, |         saveRec = action(getStr('saveRec'), self.saveRecResult, | ||||||
|                          '', 'save', getStr('saveRec'), enabled=False) |                          '', 'save', getStr('saveRec'), enabled=False) | ||||||
| @ -454,6 +476,9 @@ class MainWindow(QMainWindow): | |||||||
|         saveLabel = action(getStr('saveLabel'), self.saveLabelFile,  # |         saveLabel = action(getStr('saveLabel'), self.saveLabelFile,  # | ||||||
|                            'Ctrl+S', 'save', getStr('saveLabel'), enabled=False) |                            'Ctrl+S', 'save', getStr('saveLabel'), enabled=False) | ||||||
|          |          | ||||||
|  |         exportJSON = action(getStr('exportJSON'), self.exportJSON, | ||||||
|  |                             '', 'save', getStr('exportJSON'), enabled=False) | ||||||
|  | 
 | ||||||
|         undoLastPoint = action(getStr("undoLastPoint"), self.canvas.undoLastPoint, |         undoLastPoint = action(getStr("undoLastPoint"), self.canvas.undoLastPoint, | ||||||
|                                'Ctrl+Z', "undo", getStr("undoLastPoint"), enabled=False) |                                'Ctrl+Z', "undo", getStr("undoLastPoint"), enabled=False) | ||||||
| 
 | 
 | ||||||
| @ -474,10 +499,12 @@ class MainWindow(QMainWindow): | |||||||
| 
 | 
 | ||||||
|         self.editButton.setDefaultAction(edit) |         self.editButton.setDefaultAction(edit) | ||||||
|         self.newButton.setDefaultAction(create) |         self.newButton.setDefaultAction(create) | ||||||
|  |         self.createpolyButton.setDefaultAction(createpoly) | ||||||
|         self.DelButton.setDefaultAction(deleteImg) |         self.DelButton.setDefaultAction(deleteImg) | ||||||
|         self.SaveButton.setDefaultAction(save) |         self.SaveButton.setDefaultAction(save) | ||||||
|         self.AutoRecognition.setDefaultAction(AutoRec) |         self.AutoRecognition.setDefaultAction(AutoRec) | ||||||
|         self.reRecogButton.setDefaultAction(reRec) |         self.reRecogButton.setDefaultAction(reRec) | ||||||
|  |         self.tableRecButton.setDefaultAction(tableRec) | ||||||
|         # self.preButton.setDefaultAction(openPrevImg) |         # self.preButton.setDefaultAction(openPrevImg) | ||||||
|         # self.nextButton.setDefaultAction(openNextImg) |         # self.nextButton.setDefaultAction(openNextImg) | ||||||
| 
 | 
 | ||||||
| @ -523,25 +550,25 @@ class MainWindow(QMainWindow): | |||||||
| 
 | 
 | ||||||
|         # Store actions for further handling. |         # Store actions for further handling. | ||||||
|         self.actions = struct(save=save, resetAll=resetAll, deleteImg=deleteImg, |         self.actions = struct(save=save, resetAll=resetAll, deleteImg=deleteImg, | ||||||
|                               lineColor=color1, create=create, delete=delete, edit=edit, copy=copy, |                               lineColor=color1, create=create, createpoly=createpoly, tableRec=tableRec, delete=delete, edit=edit, copy=copy, | ||||||
|                               saveRec=saveRec, singleRere=singleRere, AutoRec=AutoRec, reRec=reRec, |                               saveRec=saveRec, singleRere=singleRere, AutoRec=AutoRec, reRec=reRec, cellreRec=cellreRec, | ||||||
|                               createMode=createMode, editMode=editMode, |                               createMode=createMode, editMode=editMode, | ||||||
|                               shapeLineColor=shapeLineColor, shapeFillColor=shapeFillColor, |                               shapeLineColor=shapeLineColor, shapeFillColor=shapeFillColor, | ||||||
|                               zoom=zoom, zoomIn=zoomIn, zoomOut=zoomOut, zoomOrg=zoomOrg, |                               zoom=zoom, zoomIn=zoomIn, zoomOut=zoomOut, zoomOrg=zoomOrg, | ||||||
|                               fitWindow=fitWindow, fitWidth=fitWidth, |                               fitWindow=fitWindow, fitWidth=fitWidth, | ||||||
|                               zoomActions=zoomActions, saveLabel=saveLabel, change_cls=change_cls, |                               zoomActions=zoomActions, saveLabel=saveLabel, change_cls=change_cls, | ||||||
|                               undo=undo, undoLastPoint=undoLastPoint, open_dataset_dir=open_dataset_dir, |                               undo=undo, undoLastPoint=undoLastPoint, open_dataset_dir=open_dataset_dir, | ||||||
|                               rotateLeft=rotateLeft, rotateRight=rotateRight, lock=lock, |                               rotateLeft=rotateLeft, rotateRight=rotateRight, lock=lock, exportJSON=exportJSON, | ||||||
|                               fileMenuActions=(opendir, open_dataset_dir, saveLabel, resetAll, quit), |                               fileMenuActions=(opendir, open_dataset_dir, saveLabel, exportJSON, resetAll, quit), | ||||||
|                               beginner=(), advanced=(), |                               beginner=(), advanced=(), | ||||||
|                               editMenu=(createpoly, edit, copy, delete, singleRere, None, undo, undoLastPoint, |                               editMenu=(createpoly, edit, copy, delete, singleRere, cellreRec, None, undo, undoLastPoint, | ||||||
|                                         None, rotateLeft, rotateRight, None, color1, self.drawSquaresOption, lock, |                                         None, rotateLeft, rotateRight, None, color1, self.drawSquaresOption, lock, | ||||||
|                                         None, change_cls), |                                         None, change_cls), | ||||||
|                               beginnerContext=( |                               beginnerContext=( | ||||||
|                                   create, edit, copy, delete, singleRere, rotateLeft, rotateRight, lock, change_cls), |                                   create, createpoly, edit, copy, delete, singleRere, cellreRec, rotateLeft, rotateRight, lock, change_cls), | ||||||
|                               advancedContext=(createMode, editMode, edit, copy, |                               advancedContext=(createMode, editMode, edit, copy, | ||||||
|                                                delete, shapeLineColor, shapeFillColor), |                                                delete, shapeLineColor, shapeFillColor), | ||||||
|                               onLoadActive=(create, createMode, editMode), |                               onLoadActive=(create, createpoly, createMode, editMode), | ||||||
|                               onShapesPresent=(hideAll, showAll)) |                               onShapesPresent=(hideAll, showAll)) | ||||||
| 
 | 
 | ||||||
|         # menus |         # menus | ||||||
| @ -574,7 +601,7 @@ class MainWindow(QMainWindow): | |||||||
|         self.autoSaveOption.triggered.connect(self.autoSaveFunc) |         self.autoSaveOption.triggered.connect(self.autoSaveFunc) | ||||||
| 
 | 
 | ||||||
|         addActions(self.menus.file, |         addActions(self.menus.file, | ||||||
|                    (opendir, open_dataset_dir, None, saveLabel, saveRec, self.autoSaveOption, None, resetAll, deleteImg, |                    (opendir, open_dataset_dir, None, saveLabel, saveRec, exportJSON, self.autoSaveOption, None, resetAll, deleteImg, | ||||||
|                     quit)) |                     quit)) | ||||||
| 
 | 
 | ||||||
|         addActions(self.menus.help, (showKeys, showSteps, showInfo)) |         addActions(self.menus.help, (showKeys, showSteps, showInfo)) | ||||||
| @ -585,7 +612,7 @@ class MainWindow(QMainWindow): | |||||||
|             zoomIn, zoomOut, zoomOrg, None, |             zoomIn, zoomOut, zoomOrg, None, | ||||||
|             fitWindow, fitWidth)) |             fitWindow, fitWidth)) | ||||||
| 
 | 
 | ||||||
|         addActions(self.menus.autolabel, (AutoRec, reRec, alcm, None, help)) |         addActions(self.menus.autolabel, (AutoRec, reRec, cellreRec, alcm, None, help)) | ||||||
| 
 | 
 | ||||||
|         self.menus.file.aboutToShow.connect(self.updateFileMenu) |         self.menus.file.aboutToShow.connect(self.updateFileMenu) | ||||||
| 
 | 
 | ||||||
| @ -695,6 +722,7 @@ class MainWindow(QMainWindow): | |||||||
|         self.dirty = False |         self.dirty = False | ||||||
|         self.actions.save.setEnabled(False) |         self.actions.save.setEnabled(False) | ||||||
|         self.actions.create.setEnabled(True) |         self.actions.create.setEnabled(True) | ||||||
|  |         self.actions.createpoly.setEnabled(True) | ||||||
| 
 | 
 | ||||||
|     def toggleActions(self, value=True): |     def toggleActions(self, value=True): | ||||||
|         """Enable/Disable widgets which depend on an opened image.""" |         """Enable/Disable widgets which depend on an opened image.""" | ||||||
| @ -780,6 +808,7 @@ class MainWindow(QMainWindow): | |||||||
|         assert self.beginner() |         assert self.beginner() | ||||||
|         self.canvas.setEditing(False) |         self.canvas.setEditing(False) | ||||||
|         self.actions.create.setEnabled(False) |         self.actions.create.setEnabled(False) | ||||||
|  |         self.actions.createpoly.setEnabled(False) | ||||||
|         self.canvas.fourpoint = False |         self.canvas.fourpoint = False | ||||||
| 
 | 
 | ||||||
|     def createPolygon(self): |     def createPolygon(self): | ||||||
| @ -787,10 +816,10 @@ class MainWindow(QMainWindow): | |||||||
|         self.canvas.setEditing(False) |         self.canvas.setEditing(False) | ||||||
|         self.canvas.fourpoint = True |         self.canvas.fourpoint = True | ||||||
|         self.actions.create.setEnabled(False) |         self.actions.create.setEnabled(False) | ||||||
|  |         self.actions.createpoly.setEnabled(False) | ||||||
|         self.actions.undoLastPoint.setEnabled(True) |         self.actions.undoLastPoint.setEnabled(True) | ||||||
| 
 | 
 | ||||||
|     def rotateImg(self, filename, k, _value): |     def rotateImg(self, filename, k, _value): | ||||||
| 
 |  | ||||||
|         self.actions.rotateRight.setEnabled(_value) |         self.actions.rotateRight.setEnabled(_value) | ||||||
|         pix = cv2.imread(filename) |         pix = cv2.imread(filename) | ||||||
|         pix = np.rot90(pix, k) |         pix = np.rot90(pix, k) | ||||||
| @ -831,6 +860,7 @@ class MainWindow(QMainWindow): | |||||||
|             self.canvas.setEditing(True) |             self.canvas.setEditing(True) | ||||||
|             self.canvas.restoreCursor() |             self.canvas.restoreCursor() | ||||||
|             self.actions.create.setEnabled(True) |             self.actions.create.setEnabled(True) | ||||||
|  |             self.actions.createpoly.setEnabled(True) | ||||||
| 
 | 
 | ||||||
|     def toggleDrawMode(self, edit=True): |     def toggleDrawMode(self, edit=True): | ||||||
|         self.canvas.setEditing(edit) |         self.canvas.setEditing(edit) | ||||||
| @ -1001,6 +1031,7 @@ class MainWindow(QMainWindow): | |||||||
|         self._noSelectionSlot = False |         self._noSelectionSlot = False | ||||||
|         n_selected = len(selected_shapes) |         n_selected = len(selected_shapes) | ||||||
|         self.actions.singleRere.setEnabled(n_selected) |         self.actions.singleRere.setEnabled(n_selected) | ||||||
|  |         self.actions.cellreRec.setEnabled(n_selected) | ||||||
|         self.actions.delete.setEnabled(n_selected) |         self.actions.delete.setEnabled(n_selected) | ||||||
|         self.actions.copy.setEnabled(n_selected) |         self.actions.copy.setEnabled(n_selected) | ||||||
|         self.actions.edit.setEnabled(n_selected == 1) |         self.actions.edit.setEnabled(n_selected == 1) | ||||||
| @ -1225,6 +1256,7 @@ class MainWindow(QMainWindow): | |||||||
|             if self.beginner():  # Switch to edit mode. |             if self.beginner():  # Switch to edit mode. | ||||||
|                 self.canvas.setEditing(True) |                 self.canvas.setEditing(True) | ||||||
|                 self.actions.create.setEnabled(True) |                 self.actions.create.setEnabled(True) | ||||||
|  |                 self.actions.createpoly.setEnabled(True) | ||||||
|                 self.actions.undoLastPoint.setEnabled(False) |                 self.actions.undoLastPoint.setEnabled(False) | ||||||
|                 self.actions.undo.setEnabled(True) |                 self.actions.undo.setEnabled(True) | ||||||
|             else: |             else: | ||||||
| @ -1663,8 +1695,10 @@ class MainWindow(QMainWindow): | |||||||
|         self.haveAutoReced = False |         self.haveAutoReced = False | ||||||
|         self.AutoRecognition.setEnabled(True) |         self.AutoRecognition.setEnabled(True) | ||||||
|         self.reRecogButton.setEnabled(True) |         self.reRecogButton.setEnabled(True) | ||||||
|  |         self.tableRecButton.setEnabled(True) | ||||||
|         self.actions.AutoRec.setEnabled(True) |         self.actions.AutoRec.setEnabled(True) | ||||||
|         self.actions.reRec.setEnabled(True) |         self.actions.reRec.setEnabled(True) | ||||||
|  |         self.actions.tableRec.setEnabled(True) | ||||||
|         self.actions.open_dataset_dir.setEnabled(True) |         self.actions.open_dataset_dir.setEnabled(True) | ||||||
|         self.actions.rotateLeft.setEnabled(True) |         self.actions.rotateLeft.setEnabled(True) | ||||||
|         self.actions.rotateRight.setEnabled(True) |         self.actions.rotateRight.setEnabled(True) | ||||||
| @ -1764,6 +1798,7 @@ class MainWindow(QMainWindow): | |||||||
|                     self.openNextImg() |                     self.openNextImg() | ||||||
|                 self.actions.saveRec.setEnabled(True) |                 self.actions.saveRec.setEnabled(True) | ||||||
|                 self.actions.saveLabel.setEnabled(True) |                 self.actions.saveLabel.setEnabled(True) | ||||||
|  |                 self.actions.exportJSON.setEnabled(True)  | ||||||
| 
 | 
 | ||||||
|         elif mode == 'Auto': |         elif mode == 'Auto': | ||||||
|             if annotationFilePath and self.saveLabels(annotationFilePath, mode=mode): |             if annotationFilePath and self.saveLabels(annotationFilePath, mode=mode): | ||||||
| @ -2090,6 +2125,280 @@ class MainWindow(QMainWindow): | |||||||
|             self.singleLabel(shape) |             self.singleLabel(shape) | ||||||
|             self.setDirty() |             self.setDirty() | ||||||
| 
 | 
 | ||||||
|  |     def TableRecognition(self): | ||||||
|  |         ''' | ||||||
|  |             Table Recegnition | ||||||
|  |         ''' | ||||||
|  |         from paddleocr.ppstructure.table.predict_table import to_excel | ||||||
|  | 
 | ||||||
|  |         import time | ||||||
|  | 
 | ||||||
|  |         start = time.time() | ||||||
|  |         img = cv2.imread(self.filePath) | ||||||
|  |         res = self.table_ocr(img, return_ocr_result_in_table=True) | ||||||
|  | 
 | ||||||
|  |         TableRec_excel_dir = self.lastOpenDir + '/tableRec_excel_output/' | ||||||
|  |         os.makedirs(TableRec_excel_dir, exist_ok=True) | ||||||
|  |         filename, _ = os.path.splitext(os.path.basename(self.filePath)) | ||||||
|  | 
 | ||||||
|  |         excel_path = TableRec_excel_dir + '{}.xlsx'.format(filename) | ||||||
|  |          | ||||||
|  |         if res is None: | ||||||
|  |             msg = 'Can not recognise the table in ' + self.filePath + '. Please change manually' | ||||||
|  |             QMessageBox.information(self, "Information", msg) | ||||||
|  |             to_excel('', excel_path) # create an empty excel | ||||||
|  |             return | ||||||
|  |          | ||||||
|  |         # save res | ||||||
|  |         # ONLY SUPPORT ONE TABLE in one image | ||||||
|  |         hasTable = False | ||||||
|  |         for region in res: | ||||||
|  |             if region['type'] == 'Table': | ||||||
|  |                 if region['res']['boxes'] is None: | ||||||
|  |                     msg = 'Can not recognise the detection box in ' + self.filePath + '. Please change manually' | ||||||
|  |                     QMessageBox.information(self, "Information", msg) | ||||||
|  |                     to_excel('', excel_path) # create an empty excel | ||||||
|  |                     return | ||||||
|  |                 hasTable = True | ||||||
|  |                 # save table ocr result on PPOCRLabel | ||||||
|  |                 # clear all old annotaions before saving result | ||||||
|  |                 self.itemsToShapes.clear() | ||||||
|  |                 self.shapesToItems.clear() | ||||||
|  |                 self.itemsToShapesbox.clear()  # ADD | ||||||
|  |                 self.shapesToItemsbox.clear() | ||||||
|  |                 self.labelList.clear() | ||||||
|  |                 self.BoxList.clear() | ||||||
|  |                 self.result_dic = [] | ||||||
|  |                 self.result_dic_locked = [] | ||||||
|  | 
 | ||||||
|  |                 shapes = [] | ||||||
|  |                 result_len = len(region['res']['boxes']) | ||||||
|  |                 for i in range(result_len): | ||||||
|  |                     bbox = np.array(region['res']['boxes'][i]) | ||||||
|  |                     rec_text = region['res']['rec_res'][i][0] | ||||||
|  | 
 | ||||||
|  |                     # polys to rectangles | ||||||
|  |                     x1, y1 = np.min(bbox[:, 0]), np.min(bbox[:, 1]) | ||||||
|  |                     x2, y2 = np.max(bbox[:, 0]), np.max(bbox[:, 1]) | ||||||
|  |                     rext_bbox = [[x1, y1], [x2, y1], [x2, y2], [x1, y2]] | ||||||
|  | 
 | ||||||
|  |                     # save bbox to shape | ||||||
|  |                     shape = Shape(label=rec_text, line_color=DEFAULT_LINE_COLOR, key_cls=None) | ||||||
|  |                     for point in rext_bbox: | ||||||
|  |                         x, y = point | ||||||
|  |                         # Ensure the labels are within the bounds of the image.  | ||||||
|  |                         # If not, fix them. | ||||||
|  |                         x, y, snapped = self.canvas.snapPointToCanvas(x, y) | ||||||
|  |                         shape.addPoint(QPointF(x, y)) | ||||||
|  |                     shape.difficult = False | ||||||
|  |                     # shape.locked = False | ||||||
|  |                     shape.close() | ||||||
|  |                     self.addLabel(shape) | ||||||
|  |                     shapes.append(shape) | ||||||
|  |                 self.setDirty() | ||||||
|  |                 self.canvas.loadShapes(shapes) | ||||||
|  |                  | ||||||
|  |                 # save HTML result to excel | ||||||
|  |                 try: | ||||||
|  |                     to_excel(region['res']['html'], excel_path) | ||||||
|  |                 except: | ||||||
|  |                     print('Can not save excel file, maybe Permission denied (.xlsx is being occupied)') | ||||||
|  |                 break | ||||||
|  |          | ||||||
|  |         if not hasTable: | ||||||
|  |             msg = 'Can not recognise the table in ' + self.filePath + '. Please change manually' | ||||||
|  |             QMessageBox.information(self, "Information", msg) | ||||||
|  |             to_excel('', excel_path) # create an empty excel | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         # automatically open excel annotation file | ||||||
|  |         if platform.system() == 'Windows': | ||||||
|  |             try: | ||||||
|  |                 import win32com.client | ||||||
|  |             except: | ||||||
|  |                 print("CANNOT OPEN .xlsx. It could be one of the following reasons: " \ | ||||||
|  |                     "Only support Windows | No python win32com") | ||||||
|  | 
 | ||||||
|  |             try: | ||||||
|  |                 xl = win32com.client.Dispatch("Excel.Application") | ||||||
|  |                 xl.Visible = True | ||||||
|  |                 xl.Workbooks.Open(excel_path) | ||||||
|  |                 # excelEx = "You need to show the excel executable at this point" | ||||||
|  |                 # subprocess.Popen([excelEx, excel_path]) | ||||||
|  | 
 | ||||||
|  |                 # os.startfile(excel_path) | ||||||
|  |             except: | ||||||
|  |                 print("CANNOT OPEN .xlsx. It could be the following reasons: " \ | ||||||
|  |                     ".xlsx is not existed") | ||||||
|  |         else: | ||||||
|  |             os.system('open ' + os.path.normpath(excel_path)) | ||||||
|  |                  | ||||||
|  |         print('time cost: ', time.time() - start) | ||||||
|  | 
 | ||||||
|  |     def cellreRecognition(self): | ||||||
|  |         ''' | ||||||
|  |             re-recognise text in a cell | ||||||
|  |         ''' | ||||||
|  |         img = cv2.imread(self.filePath) | ||||||
|  |         for shape in self.canvas.selectedShapes: | ||||||
|  |             box = [[int(p.x()), int(p.y())] for p in shape.points] | ||||||
|  | 
 | ||||||
|  |             if len(box) > 4: | ||||||
|  |                 box = self.gen_quad_from_poly(np.array(box)) | ||||||
|  |             assert len(box) == 4 | ||||||
|  | 
 | ||||||
|  |             # pad around bbox for better text recognition accuracy | ||||||
|  |             _box = boxPad(box, img.shape, 6) | ||||||
|  |             img_crop = get_rotate_crop_image(img, np.array(_box, np.float32)) | ||||||
|  |             if img_crop is None: | ||||||
|  |                 msg = 'Can not recognise the detection box in ' + self.filePath + '. Please change manually' | ||||||
|  |                 QMessageBox.information(self, "Information", msg) | ||||||
|  |                 return | ||||||
|  | 
 | ||||||
|  |             # merge the text result in the cell | ||||||
|  |             texts = '' | ||||||
|  |             probs = 0. # the probability of the cell is avgerage prob of every text box in the cell | ||||||
|  |             bboxes = self.ocr.ocr(img_crop, det=True, rec=False, cls=False) | ||||||
|  |             if len(bboxes) > 0: | ||||||
|  |                 bboxes.reverse() # top row text at first | ||||||
|  |                 for _bbox in bboxes: | ||||||
|  |                     patch = get_rotate_crop_image(img_crop, np.array(_bbox, np.float32)) | ||||||
|  |                     rec_res = self.ocr.ocr(patch, det=False, rec=True, cls=False) | ||||||
|  |                     text = rec_res[0][0] | ||||||
|  |                     if text != '': | ||||||
|  |                         texts += text + (' ' if text[0].isalpha() else '') # add space between english word | ||||||
|  |                         probs += rec_res[0][1] | ||||||
|  |                 probs = probs / len(bboxes) | ||||||
|  |             result = [(texts.strip(), probs)] | ||||||
|  | 
 | ||||||
|  |             if result[0][0] != '': | ||||||
|  |                 result.insert(0, box) | ||||||
|  |                 print('result in reRec is ', result) | ||||||
|  |                 if result[1][0] == shape.label: | ||||||
|  |                     print('label no change') | ||||||
|  |                 else: | ||||||
|  |                     shape.label = result[1][0] | ||||||
|  |             else: | ||||||
|  |                 print('Can not recognise the box') | ||||||
|  |                 if self.noLabelText == shape.label: | ||||||
|  |                     print('label no change') | ||||||
|  |                 else: | ||||||
|  |                     shape.label = self.noLabelText | ||||||
|  |             self.singleLabel(shape) | ||||||
|  |             self.setDirty() | ||||||
|  | 
 | ||||||
|  |     def exportJSON(self): | ||||||
|  |         ''' | ||||||
|  |             export PPLabel and CSV to JSON (PubTabNet) | ||||||
|  |         ''' | ||||||
|  |         import pandas as pd | ||||||
|  |         from libs.dataPartitionDialog import DataPartitionDialog | ||||||
|  | 
 | ||||||
|  |         # data partition user input | ||||||
|  |         partitionDialog = DataPartitionDialog(parent=self) | ||||||
|  |         partitionDialog.exec() | ||||||
|  |         if partitionDialog.getStatus() == False: | ||||||
|  |             return | ||||||
|  | 
 | ||||||
|  |         # automatically save annotations | ||||||
|  |         self.saveFilestate() | ||||||
|  |         self.savePPlabel(mode='auto') | ||||||
|  | 
 | ||||||
|  |         # load box annotations | ||||||
|  |         labeldict = {} | ||||||
|  |         if not os.path.exists(self.PPlabelpath): | ||||||
|  |             msg = 'ERROR, Can not find Label.txt' | ||||||
|  |             QMessageBox.information(self, "Information", msg) | ||||||
|  |             return | ||||||
|  |         else: | ||||||
|  |             with open(self.PPlabelpath, 'r', encoding='utf-8') as f: | ||||||
|  |                 data = f.readlines() | ||||||
|  |                 for each in data: | ||||||
|  |                     file, label = each.split('\t') | ||||||
|  |                     if label: | ||||||
|  |                         label = label.replace('false', 'False') | ||||||
|  |                         label = label.replace('true', 'True') | ||||||
|  |                         labeldict[file] = eval(label) | ||||||
|  |                     else: | ||||||
|  |                         labeldict[file] = [] | ||||||
|  | 
 | ||||||
|  |         # if len(labeldict) != len(csv_paths): | ||||||
|  |         #     msg = 'ERROR, box label and excel label are not in the same number\n' + \ | ||||||
|  |         #           'box label: ' + str(len(labeldict)) + '\n' + \ | ||||||
|  |         #           'excel label: ' + str(len(csv_paths)) + '\n' + \ | ||||||
|  |         #           'Please check the label.txt and tableRec_excel_output\n' | ||||||
|  |         #     QMessageBox.information(self, "Information", msg) | ||||||
|  |         #     return | ||||||
|  |         train_split, val_split, test_split = partitionDialog.getDataPartition() | ||||||
|  |         # check validate | ||||||
|  |         if train_split + val_split + test_split > 100: | ||||||
|  |             msg = "The sum of training, validation and testing data should be less than 100%" | ||||||
|  |             QMessageBox.information(self, "Information", msg) | ||||||
|  |             return | ||||||
|  |         print(train_split, val_split, test_split) | ||||||
|  |         train_split, val_split, test_split = float(train_split) / 100., float(val_split) / 100., float(test_split) / 100. | ||||||
|  |         train_id = int(len(labeldict) * train_split) | ||||||
|  |         val_id = int(len(labeldict) * (train_split + val_split)) | ||||||
|  |         print('Data partition: train:', train_id,  | ||||||
|  |               'validation:',  val_id - train_id, | ||||||
|  |               'test:', len(labeldict) - val_id) | ||||||
|  | 
 | ||||||
|  |         TableRec_excel_dir = os.path.join(self.lastOpenDir, 'tableRec_excel_output') | ||||||
|  |         json_results = [] | ||||||
|  |         imgid = 0 | ||||||
|  |         for image_path in labeldict.keys(): | ||||||
|  |             # load csv annotations | ||||||
|  |             filename, _ = os.path.splitext(os.path.basename(image_path)) | ||||||
|  |             csv_path = os.path.join(TableRec_excel_dir, filename + '.xlsx') | ||||||
|  |             if not os.path.exists(csv_path): | ||||||
|  |                 msg = 'ERROR, Can not find ' + csv_path | ||||||
|  |                 QMessageBox.information(self, "Information", msg) | ||||||
|  |                 return | ||||||
|  | 
 | ||||||
|  |             # read xlsx file, convert to HTML | ||||||
|  |             # xd = pd.ExcelFile(csv_path) | ||||||
|  |             # df = xd.parse() | ||||||
|  |             # structure = df.to_html(index = False) | ||||||
|  |             excel = xlrd.open_workbook(csv_path) | ||||||
|  |             sheet0 = excel.sheet_by_index(0)  # only sheet 0 | ||||||
|  |             merged_cells = sheet0.merged_cells # (0,1,1,3) start row, end row, start col, end col | ||||||
|  | 
 | ||||||
|  |             html_list = [['td'] * sheet0.ncols for i in range(sheet0.nrows)] | ||||||
|  | 
 | ||||||
|  |             for merged in merged_cells: | ||||||
|  |                 html_list = expand_list(merged, html_list) | ||||||
|  | 
 | ||||||
|  |             token_list = convert_token(html_list) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |             # load box annotations | ||||||
|  |             cells = [] | ||||||
|  |             for anno in labeldict[image_path]: | ||||||
|  |                 tokens = list(anno['transcription']) | ||||||
|  |                 obb = anno['points'] | ||||||
|  |                 hbb = OBB2HBB(np.array(obb)).tolist() | ||||||
|  |                 cells.append({'tokens': tokens, 'bbox': hbb}) | ||||||
|  |              | ||||||
|  |             # data split | ||||||
|  |             if imgid < train_id: | ||||||
|  |                 split = 'train' | ||||||
|  |             elif imgid < val_id: | ||||||
|  |                 split = 'val' | ||||||
|  |             else: | ||||||
|  |                 split = 'test' | ||||||
|  | 
 | ||||||
|  |             #  save dict | ||||||
|  |             html = {'structure': {'tokens': token_list}, 'cell': cells} | ||||||
|  |             json_results.append({'filename': os.path.basename(image_path), 'split': split, 'imgid': imgid, 'html': html}) | ||||||
|  |             imgid += 1 | ||||||
|  | 
 | ||||||
|  |         # save json | ||||||
|  |         with open("{}/annotation.json".format(self.lastOpenDir), "w", encoding='utf-8') as fid: | ||||||
|  |             fid.write(json.dumps(json_results, ensure_ascii=False)) | ||||||
|  |          | ||||||
|  |         msg = 'JSON sucessfully saved in {}/annotation.json'.format(self.lastOpenDir) | ||||||
|  |         QMessageBox.information(self, "Information", msg) | ||||||
|  | 
 | ||||||
|     def autolcm(self): |     def autolcm(self): | ||||||
|         vbox = QVBoxLayout() |         vbox = QVBoxLayout() | ||||||
|         hbox = QHBoxLayout() |         hbox = QHBoxLayout() | ||||||
| @ -2129,6 +2438,12 @@ class MainWindow(QMainWindow): | |||||||
|         del self.ocr |         del self.ocr | ||||||
|         self.ocr = PaddleOCR(use_pdserving=False, use_angle_cls=True, det=True, cls=True, use_gpu=False, |         self.ocr = PaddleOCR(use_pdserving=False, use_angle_cls=True, det=True, cls=True, use_gpu=False, | ||||||
|                              lang=lg_idx[self.comboBox.currentText()]) |                              lang=lg_idx[self.comboBox.currentText()]) | ||||||
|  |         del self.table_ocr | ||||||
|  |         self.table_ocr = PPStructure(use_pdserving=False, | ||||||
|  |                                      use_gpu=False, | ||||||
|  |                                      lang=lg_idx[self.comboBox.currentText()], | ||||||
|  |                                      layout=False, | ||||||
|  |                                      show_log=False) | ||||||
|         self.dialog.close() |         self.dialog.close() | ||||||
| 
 | 
 | ||||||
|     def cancel(self): |     def cancel(self): | ||||||
| @ -2147,6 +2462,7 @@ class MainWindow(QMainWindow): | |||||||
|                     self.fileStatedict[file] = 1 |                     self.fileStatedict[file] = 1 | ||||||
|                 self.actions.saveLabel.setEnabled(True) |                 self.actions.saveLabel.setEnabled(True) | ||||||
|                 self.actions.saveRec.setEnabled(True) |                 self.actions.saveRec.setEnabled(True) | ||||||
|  |                 self.actions.exportJSON.setEnabled(True) | ||||||
| 
 | 
 | ||||||
|     def saveFilestate(self): |     def saveFilestate(self): | ||||||
|         with open(self.fileStatepath, 'w', encoding='utf-8') as f: |         with open(self.fileStatepath, 'w', encoding='utf-8') as f: | ||||||
|  | |||||||
							
								
								
									
										113
									
								
								PPOCRLabel/libs/dataPartitionDialog.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								PPOCRLabel/libs/dataPartitionDialog.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,113 @@ | |||||||
|  | try: | ||||||
|  |     from PyQt5.QtGui import * | ||||||
|  |     from PyQt5.QtCore import * | ||||||
|  |     from PyQt5.QtWidgets import * | ||||||
|  | except ImportError: | ||||||
|  |     from PyQt4.QtGui import * | ||||||
|  |     from PyQt4.QtCore import * | ||||||
|  | 
 | ||||||
|  | from libs.utils import newIcon | ||||||
|  | 
 | ||||||
|  | import time | ||||||
|  | import datetime | ||||||
|  | import json | ||||||
|  | import cv2 | ||||||
|  | import numpy as np | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | BB = QDialogButtonBox | ||||||
|  | 
 | ||||||
|  | class DataPartitionDialog(QDialog): | ||||||
|  |     def __init__(self, parent=None): | ||||||
|  |         super().__init__() | ||||||
|  |         self.parnet = parent | ||||||
|  |         self.title = 'DATA PARTITION' | ||||||
|  | 
 | ||||||
|  |         self.train_ratio = 70 | ||||||
|  |         self.val_ratio = 15 | ||||||
|  |         self.test_ratio = 15 | ||||||
|  |          | ||||||
|  |         self.initUI() | ||||||
|  | 
 | ||||||
|  |     def initUI(self): | ||||||
|  |         self.setWindowTitle(self.title) | ||||||
|  |         self.setWindowModality(Qt.ApplicationModal) | ||||||
|  | 
 | ||||||
|  |         self.flag_accept = True | ||||||
|  | 
 | ||||||
|  |         if self.parnet.lang == 'ch': | ||||||
|  |             msg = "导出JSON前请保存所有图像的标注且关闭EXCEL!" | ||||||
|  |         else: | ||||||
|  |             msg = "Please save all the annotations and close the EXCEL before exporting JSON!" | ||||||
|  | 
 | ||||||
|  |         info_msg = QLabel(msg, self) | ||||||
|  |         info_msg.setWordWrap(True) | ||||||
|  |         info_msg.setStyleSheet("color: red") | ||||||
|  |         info_msg.setFont(QFont('Arial', 12)) | ||||||
|  | 
 | ||||||
|  |         train_lbl = QLabel('Train split: ', self) | ||||||
|  |         train_lbl.setFont(QFont('Arial', 15)) | ||||||
|  |         val_lbl = QLabel('Valid split: ', self) | ||||||
|  |         val_lbl.setFont(QFont('Arial', 15)) | ||||||
|  |         test_lbl = QLabel('Test split: ', self) | ||||||
|  |         test_lbl.setFont(QFont('Arial', 15)) | ||||||
|  | 
 | ||||||
|  |         self.train_input = QLineEdit(self) | ||||||
|  |         self.train_input.setFont(QFont('Arial', 15)) | ||||||
|  |         self.val_input = QLineEdit(self) | ||||||
|  |         self.val_input.setFont(QFont('Arial', 15)) | ||||||
|  |         self.test_input = QLineEdit(self) | ||||||
|  |         self.test_input.setFont(QFont('Arial', 15)) | ||||||
|  | 
 | ||||||
|  |         self.train_input.setText(str(self.train_ratio)) | ||||||
|  |         self.val_input.setText(str(self.val_ratio)) | ||||||
|  |         self.test_input.setText(str(self.test_ratio)) | ||||||
|  | 
 | ||||||
|  |         validator = QIntValidator(0, 100) | ||||||
|  |         self.train_input.setValidator(validator) | ||||||
|  |         self.val_input.setValidator(validator) | ||||||
|  |         self.test_input.setValidator(validator) | ||||||
|  | 
 | ||||||
|  |         gridlayout = QGridLayout() | ||||||
|  |         gridlayout.addWidget(info_msg, 0, 0, 1, 2) | ||||||
|  |         gridlayout.addWidget(train_lbl, 1, 0) | ||||||
|  |         gridlayout.addWidget(val_lbl, 2, 0) | ||||||
|  |         gridlayout.addWidget(test_lbl, 3, 0) | ||||||
|  |         gridlayout.addWidget(self.train_input, 1, 1) | ||||||
|  |         gridlayout.addWidget(self.val_input, 2, 1) | ||||||
|  |         gridlayout.addWidget(self.test_input, 3, 1) | ||||||
|  | 
 | ||||||
|  |         bb = BB(BB.Ok | BB.Cancel, Qt.Horizontal, self) | ||||||
|  |         bb.button(BB.Ok).setIcon(newIcon('done')) | ||||||
|  |         bb.button(BB.Cancel).setIcon(newIcon('undo')) | ||||||
|  |         bb.accepted.connect(self.validate) | ||||||
|  |         bb.rejected.connect(self.cancel) | ||||||
|  |         gridlayout.addWidget(bb, 4, 0, 1, 2) | ||||||
|  | 
 | ||||||
|  |         self.setLayout(gridlayout) | ||||||
|  |          | ||||||
|  |         self.show() | ||||||
|  | 
 | ||||||
|  |     def validate(self): | ||||||
|  |         self.flag_accept = True | ||||||
|  |         self.accept() | ||||||
|  | 
 | ||||||
|  |     def cancel(self): | ||||||
|  |         self.flag_accept = False | ||||||
|  |         self.reject() | ||||||
|  |      | ||||||
|  |     def getStatus(self): | ||||||
|  |         return self.flag_accept | ||||||
|  | 
 | ||||||
|  |     def getDataPartition(self): | ||||||
|  |         self.train_ratio = int(self.train_input.text()) | ||||||
|  |         self.val_ratio = int(self.val_input.text()) | ||||||
|  |         self.test_ratio = int(self.test_input.text()) | ||||||
|  | 
 | ||||||
|  |         return self.train_ratio, self.val_ratio, self.test_ratio | ||||||
|  | 
 | ||||||
|  |     def closeEvent(self, event): | ||||||
|  |         self.flag_accept = False | ||||||
|  |         self.reject() | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| @ -161,6 +161,77 @@ def get_rotate_crop_image(img, points): | |||||||
|         print(e) |         print(e) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | def boxPad(box, imgShape, pad : int) -> np.array: | ||||||
|  |     """ | ||||||
|  |     Pad a box with [pad] pixels on each side. | ||||||
|  |     """ | ||||||
|  |     box = np.array(box, dtype=np.int32) | ||||||
|  |     box[0][0], box[0][1] = box[0][0] - pad, box[0][1] - pad | ||||||
|  |     box[1][0], box[1][1] = box[1][0] + pad, box[1][1] - pad | ||||||
|  |     box[2][0], box[2][1] = box[2][0] + pad, box[2][1] + pad | ||||||
|  |     box[3][0], box[3][1] = box[3][0] - pad, box[3][1] + pad | ||||||
|  |     h, w, _ = imgShape | ||||||
|  |     box[:,0] = np.clip(box[:,0], 0, w) | ||||||
|  |     box[:,1] = np.clip(box[:,1], 0, h) | ||||||
|  |     return box | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def OBB2HBB(obb) -> np.array: | ||||||
|  |     """ | ||||||
|  |     Convert Oriented Bounding Box to Horizontal Bounding Box. | ||||||
|  |     """ | ||||||
|  |     hbb = np.zeros(4, dtype=np.int32) | ||||||
|  |     hbb[0] = min(obb[:, 0]) | ||||||
|  |     hbb[1] = min(obb[:, 1]) | ||||||
|  |     hbb[2] = max(obb[:, 0]) | ||||||
|  |     hbb[3] = max(obb[:, 1]) | ||||||
|  |     return hbb | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def expand_list(merged, html_list): | ||||||
|  |     ''' | ||||||
|  |     Fill blanks according to merged cells | ||||||
|  |     ''' | ||||||
|  |     sr, er, sc, ec = merged | ||||||
|  |     for i in range(sr, er): | ||||||
|  |         for j in range(sc, ec): | ||||||
|  |             html_list[i][j] = None | ||||||
|  |     html_list[sr][sc] = '' | ||||||
|  |     if ec - sc > 1: | ||||||
|  |         html_list[sr][sc] += " colspan={}".format(ec - sc) | ||||||
|  |     if er - sr > 1: | ||||||
|  |         html_list[sr][sc] += " rowspan={}".format(er - sr) | ||||||
|  |     return html_list | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def convert_token(html_list): | ||||||
|  |     ''' | ||||||
|  |     Convert raw html to label format | ||||||
|  |     ''' | ||||||
|  |     token_list = ["<tbody>"] | ||||||
|  |     # final html list: | ||||||
|  |     for row in html_list: | ||||||
|  |         token_list.append("<tr>") | ||||||
|  |         for col in row: | ||||||
|  |             if col == None: | ||||||
|  |                 continue | ||||||
|  |             elif col == 'td': | ||||||
|  |                 token_list.extend(["<td>", "</td>"]) | ||||||
|  |             else: | ||||||
|  |                 token_list.append("<td") | ||||||
|  |                 if 'colspan' in col: | ||||||
|  |                     _, n = col.split('colspan=') | ||||||
|  |                     token_list.append(" colspan=\"{}\"".format(n)) | ||||||
|  |                 if 'rowspan' in col: | ||||||
|  |                     _, n = col.split('rowspan=') | ||||||
|  |                     token_list.append(" rowspan=\"{}\"".format(n)) | ||||||
|  |                 token_list.extend([">", "</td>"]) | ||||||
|  |         token_list.append("</tr>") | ||||||
|  |     token_list.append("</tbody>") | ||||||
|  | 
 | ||||||
|  |     return token_list | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| def stepsInfo(lang='en'): | def stepsInfo(lang='en'): | ||||||
|     if lang == 'ch': |     if lang == 'ch': | ||||||
|         msg = "1. 安装与运行:使用上述命令安装与运行程序。\n" \ |         msg = "1. 安装与运行:使用上述命令安装与运行程序。\n" \ | ||||||
|  | |||||||
| @ -84,7 +84,7 @@ mhelp=Help | |||||||
| iconList=Icon List | iconList=Icon List | ||||||
| detectionBoxposition=Detection box position | detectionBoxposition=Detection box position | ||||||
| recognitionResult=Recognition result | recognitionResult=Recognition result | ||||||
| creatPolygon=Create Quadrilateral | creatPolygon=Create PolygonBox | ||||||
| rotateLeft=Left turn 90 degrees | rotateLeft=Left turn 90 degrees | ||||||
| rotateRight=Right turn 90 degrees | rotateRight=Right turn 90 degrees | ||||||
| drawSquares=Draw Squares | drawSquares=Draw Squares | ||||||
| @ -110,3 +110,6 @@ lockBoxDetail=Lock selected box/Unlock all box | |||||||
| keyListTitle=Key List | keyListTitle=Key List | ||||||
| keyDialogTip=Enter object label | keyDialogTip=Enter object label | ||||||
| keyChange=Change Box Key | keyChange=Change Box Key | ||||||
|  | TableRecognition=Table Recognition | ||||||
|  | cellreRecognition=Cell Re-Recognition | ||||||
|  | exportJSON=export JSON(PubTabNet) | ||||||
|  | |||||||
| @ -84,7 +84,7 @@ mhelp=帮助 | |||||||
| iconList=缩略图 | iconList=缩略图 | ||||||
| detectionBoxposition=检测框位置 | detectionBoxposition=检测框位置 | ||||||
| recognitionResult=识别结果 | recognitionResult=识别结果 | ||||||
| creatPolygon=四点标注 | creatPolygon=多边形标注 | ||||||
| drawSquares=正方形标注 | drawSquares=正方形标注 | ||||||
| rotateLeft=图片左旋转90度 | rotateLeft=图片左旋转90度 | ||||||
| rotateRight=图片右旋转90度 | rotateRight=图片右旋转90度 | ||||||
| @ -110,3 +110,6 @@ lockBoxDetail=若当前没有框处于锁定状态则锁定选中的框,若存 | |||||||
| keyListTitle=关键词列表 | keyListTitle=关键词列表 | ||||||
| keyDialogTip=请输入类型名称 | keyDialogTip=请输入类型名称 | ||||||
| keyChange=更改Box关键字类别 | keyChange=更改Box关键字类别 | ||||||
|  | TableRecognition=表格识别 | ||||||
|  | cellreRecognition=单元格重识别 | ||||||
|  | exportJSON=导出表格JSON标注 | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Evezerest
						Evezerest