From 38a08a4b17af1296317989bec53a4b1aff2446f2 Mon Sep 17 00:00:00 2001 From: 283375 Date: Sat, 19 Aug 2023 01:21:23 +0800 Subject: [PATCH] wip: b30 ocr tab --- index.py | 4 +- ui/designer/components/ocrQueue.ui | 2 +- ui/designer/components/ocrQueue_ui.py | 3 +- ui/designer/mainwindow.ui | 8 +- ui/designer/mainwindow_ui.py | 5 +- ui/designer/tabs/tabOcr/tabOcr_B30.ui | 113 ++++++++++++ ui/designer/tabs/tabOcr/tabOcr_B30_ui.py | 126 ++++++++++++++ .../{tabOcr.ui => tabOcr/tabOcr_Device.ui} | 16 +- .../tabOcr_Device_ui.py} | 56 +++--- ui/designer/tabs/tabOcrEntry.ui | 52 ++++++ ui/designer/tabs/tabOcrEntry_ui.py | 57 +++++++ ui/extends/components/ocrQueue/__init__.py | 108 +++++++----- ui/extends/shared/cv2_utils.py | 28 +++ ui/extends/shared/delegates/imageDelegate.py | 5 +- ui/extends/tabs/tabOcr/tabOcr_B30.py | 51 ++++++ .../{tabOcr.py => tabOcr/tabOcr_Device.py} | 6 +- ui/implements/mainwindow.py | 4 +- ui/implements/tabs/tabOcr/tabOcr_B30.py | 161 ++++++++++++++++++ .../{tabOcr.py => tabOcr/tabOcr_Device.py} | 9 +- ui/implements/tabs/tabOcrEntry.py | 9 + 20 files changed, 726 insertions(+), 97 deletions(-) create mode 100644 ui/designer/tabs/tabOcr/tabOcr_B30.ui create mode 100644 ui/designer/tabs/tabOcr/tabOcr_B30_ui.py rename ui/designer/tabs/{tabOcr.ui => tabOcr/tabOcr_Device.ui} (92%) rename ui/designer/tabs/{tabOcr_ui.py => tabOcr/tabOcr_Device_ui.py} (79%) create mode 100644 ui/designer/tabs/tabOcrEntry.ui create mode 100644 ui/designer/tabs/tabOcrEntry_ui.py create mode 100644 ui/extends/shared/cv2_utils.py create mode 100644 ui/extends/tabs/tabOcr/tabOcr_B30.py rename ui/extends/tabs/{tabOcr.py => tabOcr/tabOcr_Device.py} (93%) create mode 100644 ui/implements/tabs/tabOcr/tabOcr_B30.py rename ui/implements/tabs/{tabOcr.py => tabOcr/tabOcr_Device.py} (94%) create mode 100644 ui/implements/tabs/tabOcrEntry.py diff --git a/index.py b/index.py index 94d0584..de517b3 100644 --- a/index.py +++ b/index.py @@ -7,10 +7,10 @@ from PySide6.QtCore import QLibraryInfo, QLocale, QTranslator from PySide6.QtGui import QIcon from PySide6.QtWidgets import QApplication, QDialog, QMessageBox -from ui.startup.databaseChecker import DatabaseChecker -from ui.implements.mainwindow import MainWindow import ui.resources.images.images_rc import ui.resources.translations.translations_rc +from ui.implements.mainwindow import MainWindow +from ui.startup.databaseChecker import DatabaseChecker logging.basicConfig(level=logging.INFO, stream=sys.stdout, force=True) diff --git a/ui/designer/components/ocrQueue.ui b/ui/designer/components/ocrQueue.ui index 1d17689..03980ad 100644 --- a/ui/designer/components/ocrQueue.ui +++ b/ui/designer/components/ocrQueue.ui @@ -11,7 +11,7 @@ - OcrQueue + OcrQueue diff --git a/ui/designer/components/ocrQueue_ui.py b/ui/designer/components/ocrQueue_ui.py index eba0cb6..cccc531 100644 --- a/ui/designer/components/ocrQueue_ui.py +++ b/ui/designer/components/ocrQueue_ui.py @@ -25,6 +25,7 @@ class Ui_OcrQueue(object): if not OcrQueue.objectName(): OcrQueue.setObjectName(u"OcrQueue") OcrQueue.resize(741, 372) + OcrQueue.setWindowTitle(u"OcrQueue") self.horizontalLayout_2 = QHBoxLayout(OcrQueue) self.horizontalLayout_2.setObjectName(u"horizontalLayout_2") self.groupBox_3 = QGroupBox(OcrQueue) @@ -119,7 +120,6 @@ class Ui_OcrQueue(object): # setupUi def retranslateUi(self, OcrQueue): - OcrQueue.setWindowTitle(QCoreApplication.translate("OcrQueue", u"OcrQueue", None)) self.groupBox_3.setTitle(QCoreApplication.translate("OcrQueue", u"ocr.queue.title", None)) self.ocr_addImageButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.queue.addImageButton", None)) self.ocr_removeSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.queue.removeSelected", None)) @@ -129,5 +129,6 @@ class Ui_OcrQueue(object): self.ocr_acceptSelectedButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.results.acceptSelectedButton", None)) self.ocr_acceptAllButton.setText(QCoreApplication.translate("OcrQueue", u"ocr.results.acceptAllButton", None)) self.ocr_ignoreValidateCheckBox.setText(QCoreApplication.translate("OcrQueue", u"ocr.results.ignoreValidate", None)) + pass # retranslateUi diff --git a/ui/designer/mainwindow.ui b/ui/designer/mainwindow.ui index 4aaa966..2752238 100644 --- a/ui/designer/mainwindow.ui +++ b/ui/designer/mainwindow.ui @@ -35,7 +35,7 @@ tab.db - + tab.ocr @@ -86,6 +86,12 @@
ui.implements.tabs.tabDbEntry
1 + + TabOcrEntry + QWidget +
ui.implements.tabs.tabOcrEntry
+ 1 +
diff --git a/ui/designer/mainwindow_ui.py b/ui/designer/mainwindow_ui.py index 506e2a2..3e2a6fc 100644 --- a/ui/designer/mainwindow_ui.py +++ b/ui/designer/mainwindow_ui.py @@ -3,7 +3,7 @@ ################################################################################ ## Form generated from reading UI file 'mainwindow.ui' ## -## Created by: Qt User Interface Compiler version 6.5.0 +## Created by: Qt User Interface Compiler version 6.5.1 ## ## WARNING! All changes made in this file will be lost when recompiling UI file! ################################################################################ @@ -21,6 +21,7 @@ from PySide6.QtWidgets import (QApplication, QMainWindow, QSizePolicy, QTabWidge from ui.implements.tabs.tabAbout import TabAbout from ui.implements.tabs.tabDbEntry import TabDbEntry from ui.implements.tabs.tabInputScore import TabInputScore +from ui.implements.tabs.tabOcrEntry import TabOcrEntry from ui.implements.tabs.tabOverview import TabOverview from ui.implements.tabs.tabSettings import TabSettings @@ -45,7 +46,7 @@ class Ui_MainWindow(object): self.tab_db = TabDbEntry() self.tab_db.setObjectName(u"tab_db") self.tabWidget.addTab(self.tab_db, "") - self.tab_ocr = QWidget() + self.tab_ocr = TabOcrEntry() self.tab_ocr.setObjectName(u"tab_ocr") self.tabWidget.addTab(self.tab_ocr, "") self.tab_settings = TabSettings() diff --git a/ui/designer/tabs/tabOcr/tabOcr_B30.ui b/ui/designer/tabs/tabOcr/tabOcr_B30.ui new file mode 100644 index 0000000..42df52c --- /dev/null +++ b/ui/designer/tabs/tabOcr/tabOcr_B30.ui @@ -0,0 +1,113 @@ + + + TabOcr_B30 + + + + 0 + 0 + 555 + 461 + + + + TabOcr_B30 + + + + + + b30type + + + + + + + + + + + + + + knnModelSelector.title + + + + + + + + + + + + b30KnnModelSelector.title + + + + + + + + + + + + + + + + siftDatabaseSelector.title + + + + + + + + + + + + imageSelector.title + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + FileSelector + QWidget +
ui.implements.components.fileSelector
+ 1 +
+ + OcrQueue + QWidget +
ui.implements.components.ocrQueue
+ 1 +
+
+ + +
diff --git a/ui/designer/tabs/tabOcr/tabOcr_B30_ui.py b/ui/designer/tabs/tabOcr/tabOcr_B30_ui.py new file mode 100644 index 0000000..adf42f1 --- /dev/null +++ b/ui/designer/tabs/tabOcr/tabOcr_B30_ui.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'tabOcr_B30.ui' +## +## Created by: Qt User Interface Compiler version 6.5.1 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QComboBox, QGroupBox, QHBoxLayout, + QSizePolicy, QVBoxLayout, QWidget) + +from ui.implements.components.fileSelector import FileSelector +from ui.implements.components.ocrQueue import OcrQueue + +class Ui_TabOcr_B30(object): + def setupUi(self, TabOcr_B30): + if not TabOcr_B30.objectName(): + TabOcr_B30.setObjectName(u"TabOcr_B30") + TabOcr_B30.resize(555, 461) + TabOcr_B30.setWindowTitle(u"TabOcr_B30") + self.verticalLayout_3 = QVBoxLayout(TabOcr_B30) + self.verticalLayout_3.setObjectName(u"verticalLayout_3") + self.groupBox = QGroupBox(TabOcr_B30) + self.groupBox.setObjectName(u"groupBox") + self.verticalLayout = QVBoxLayout(self.groupBox) + self.verticalLayout.setObjectName(u"verticalLayout") + self.b30TypeComboBox = QComboBox(self.groupBox) + self.b30TypeComboBox.setObjectName(u"b30TypeComboBox") + + self.verticalLayout.addWidget(self.b30TypeComboBox) + + + self.verticalLayout_3.addWidget(self.groupBox) + + self.horizontalLayout = QHBoxLayout() + self.horizontalLayout.setObjectName(u"horizontalLayout") + self.groupBox_3 = QGroupBox(TabOcr_B30) + self.groupBox_3.setObjectName(u"groupBox_3") + self.verticalLayout_4 = QVBoxLayout(self.groupBox_3) + self.verticalLayout_4.setObjectName(u"verticalLayout_4") + self.knnModelSelector = FileSelector(self.groupBox_3) + self.knnModelSelector.setObjectName(u"knnModelSelector") + + self.verticalLayout_4.addWidget(self.knnModelSelector) + + + self.horizontalLayout.addWidget(self.groupBox_3) + + self.groupBox_5 = QGroupBox(TabOcr_B30) + self.groupBox_5.setObjectName(u"groupBox_5") + self.verticalLayout_6 = QVBoxLayout(self.groupBox_5) + self.verticalLayout_6.setObjectName(u"verticalLayout_6") + self.b30KnnModelSelector = FileSelector(self.groupBox_5) + self.b30KnnModelSelector.setObjectName(u"b30KnnModelSelector") + + self.verticalLayout_6.addWidget(self.b30KnnModelSelector) + + + self.horizontalLayout.addWidget(self.groupBox_5) + + + self.verticalLayout_3.addLayout(self.horizontalLayout) + + self.horizontalLayout_3 = QHBoxLayout() + self.horizontalLayout_3.setObjectName(u"horizontalLayout_3") + self.groupBox_4 = QGroupBox(TabOcr_B30) + self.groupBox_4.setObjectName(u"groupBox_4") + self.verticalLayout_5 = QVBoxLayout(self.groupBox_4) + self.verticalLayout_5.setObjectName(u"verticalLayout_5") + self.siftDatabaseSelector = FileSelector(self.groupBox_4) + self.siftDatabaseSelector.setObjectName(u"siftDatabaseSelector") + + self.verticalLayout_5.addWidget(self.siftDatabaseSelector) + + + self.horizontalLayout_3.addWidget(self.groupBox_4) + + self.groupBox_2 = QGroupBox(TabOcr_B30) + self.groupBox_2.setObjectName(u"groupBox_2") + self.verticalLayout_2 = QVBoxLayout(self.groupBox_2) + self.verticalLayout_2.setObjectName(u"verticalLayout_2") + self.imageSelector = FileSelector(self.groupBox_2) + self.imageSelector.setObjectName(u"imageSelector") + + self.verticalLayout_2.addWidget(self.imageSelector) + + + self.horizontalLayout_3.addWidget(self.groupBox_2) + + + self.verticalLayout_3.addLayout(self.horizontalLayout_3) + + self.ocrQueue = OcrQueue(TabOcr_B30) + self.ocrQueue.setObjectName(u"ocrQueue") + sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.ocrQueue.sizePolicy().hasHeightForWidth()) + self.ocrQueue.setSizePolicy(sizePolicy) + + self.verticalLayout_3.addWidget(self.ocrQueue) + + + self.retranslateUi(TabOcr_B30) + + QMetaObject.connectSlotsByName(TabOcr_B30) + # setupUi + + def retranslateUi(self, TabOcr_B30): + self.groupBox.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30type", None)) + self.groupBox_3.setTitle(QCoreApplication.translate("TabOcr_B30", u"knnModelSelector.title", None)) + self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_B30", u"b30KnnModelSelector.title", None)) + self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_B30", u"siftDatabaseSelector.title", None)) + self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr_B30", u"imageSelector.title", None)) + pass + # retranslateUi + diff --git a/ui/designer/tabs/tabOcr.ui b/ui/designer/tabs/tabOcr/tabOcr_Device.ui similarity index 92% rename from ui/designer/tabs/tabOcr.ui rename to ui/designer/tabs/tabOcr/tabOcr_Device.ui index 82c92ec..09be870 100644 --- a/ui/designer/tabs/tabOcr.ui +++ b/ui/designer/tabs/tabOcr/tabOcr_Device.ui @@ -1,7 +1,7 @@ - TabOcr - + TabOcr_Device + 0 @@ -11,7 +11,7 @@ - TabOcr + TabOcr_Device @@ -136,21 +136,13 @@
- + 0 0 - - ocr.title - - - - - -
diff --git a/ui/designer/tabs/tabOcr_ui.py b/ui/designer/tabs/tabOcr/tabOcr_Device_ui.py similarity index 79% rename from ui/designer/tabs/tabOcr_ui.py rename to ui/designer/tabs/tabOcr/tabOcr_Device_ui.py index a72a97e..7c4f81c 100644 --- a/ui/designer/tabs/tabOcr_ui.py +++ b/ui/designer/tabs/tabOcr/tabOcr_Device_ui.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- ################################################################################ -## Form generated from reading UI file 'tabOcr.ui' +## Form generated from reading UI file 'tabOcr_Device.ui' ## ## Created by: Qt User Interface Compiler version 6.5.1 ## @@ -22,20 +22,20 @@ from ui.implements.components.devicesComboBox import DevicesComboBox from ui.implements.components.fileSelector import FileSelector from ui.implements.components.ocrQueue import OcrQueue -class Ui_TabOcr(object): - def setupUi(self, TabOcr): - if not TabOcr.objectName(): - TabOcr.setObjectName(u"TabOcr") - TabOcr.resize(632, 527) - TabOcr.setWindowTitle(u"TabOcr") - self.verticalLayout_3 = QVBoxLayout(TabOcr) +class Ui_TabOcr_Device(object): + def setupUi(self, TabOcr_Device): + if not TabOcr_Device.objectName(): + TabOcr_Device.setObjectName(u"TabOcr_Device") + TabOcr_Device.resize(632, 527) + TabOcr_Device.setWindowTitle(u"TabOcr_Device") + self.verticalLayout_3 = QVBoxLayout(TabOcr_Device) self.verticalLayout_3.setObjectName(u"verticalLayout_3") - self.openWizardButton = QPushButton(TabOcr) + self.openWizardButton = QPushButton(TabOcr_Device) self.openWizardButton.setObjectName(u"openWizardButton") self.verticalLayout_3.addWidget(self.openWizardButton) - self.groupBox = QGroupBox(TabOcr) + self.groupBox = QGroupBox(TabOcr_Device) self.groupBox.setObjectName(u"groupBox") self.verticalLayout = QVBoxLayout(self.groupBox) self.verticalLayout.setObjectName(u"verticalLayout") @@ -52,7 +52,7 @@ class Ui_TabOcr(object): self.verticalLayout_3.addWidget(self.groupBox) - self.horizontalWidget = QWidget(TabOcr) + self.horizontalWidget = QWidget(TabOcr_Device) self.horizontalWidget.setObjectName(u"horizontalWidget") sizePolicy = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Maximum) sizePolicy.setHorizontalStretch(0) @@ -118,39 +118,31 @@ class Ui_TabOcr(object): self.verticalLayout_3.addWidget(self.horizontalWidget) - self.groupBox_2 = QGroupBox(TabOcr) - self.groupBox_2.setObjectName(u"groupBox_2") + self.ocrQueue = OcrQueue(TabOcr_Device) + self.ocrQueue.setObjectName(u"ocrQueue") sizePolicy1 = QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Expanding) sizePolicy1.setHorizontalStretch(0) sizePolicy1.setVerticalStretch(0) - sizePolicy1.setHeightForWidth(self.groupBox_2.sizePolicy().hasHeightForWidth()) - self.groupBox_2.setSizePolicy(sizePolicy1) - self.horizontalLayout = QHBoxLayout(self.groupBox_2) - self.horizontalLayout.setObjectName(u"horizontalLayout") - self.ocrQueue = OcrQueue(self.groupBox_2) - self.ocrQueue.setObjectName(u"ocrQueue") + sizePolicy1.setHeightForWidth(self.ocrQueue.sizePolicy().hasHeightForWidth()) + self.ocrQueue.setSizePolicy(sizePolicy1) - self.horizontalLayout.addWidget(self.ocrQueue) + self.verticalLayout_3.addWidget(self.ocrQueue) - self.verticalLayout_3.addWidget(self.groupBox_2) - - - self.retranslateUi(TabOcr) + self.retranslateUi(TabOcr_Device) self.deviceDependenciesStackedWidget.setCurrentIndex(0) - QMetaObject.connectSlotsByName(TabOcr) + QMetaObject.connectSlotsByName(TabOcr_Device) # setupUi - def retranslateUi(self, TabOcr): - self.openWizardButton.setText(QCoreApplication.translate("TabOcr", u"openWizardButton", None)) - self.groupBox.setTitle(QCoreApplication.translate("TabOcr", u"deviceSelector.title", None)) - self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr", u"knnModelSelector.title", None)) - self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr", u"tesseractSelector.title", None)) - self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr", u"siftDatabaseSelector.title", None)) - self.groupBox_2.setTitle(QCoreApplication.translate("TabOcr", u"ocr.title", None)) + def retranslateUi(self, TabOcr_Device): + self.openWizardButton.setText(QCoreApplication.translate("TabOcr_Device", u"openWizardButton", None)) + self.groupBox.setTitle(QCoreApplication.translate("TabOcr_Device", u"deviceSelector.title", None)) + self.groupBox_6.setTitle(QCoreApplication.translate("TabOcr_Device", u"knnModelSelector.title", None)) + self.groupBox_4.setTitle(QCoreApplication.translate("TabOcr_Device", u"tesseractSelector.title", None)) + self.groupBox_5.setTitle(QCoreApplication.translate("TabOcr_Device", u"siftDatabaseSelector.title", None)) pass # retranslateUi diff --git a/ui/designer/tabs/tabOcrEntry.ui b/ui/designer/tabs/tabOcrEntry.ui new file mode 100644 index 0000000..7314699 --- /dev/null +++ b/ui/designer/tabs/tabOcrEntry.ui @@ -0,0 +1,52 @@ + + + TabOcrEntry + + + + 0 + 0 + 600 + 478 + + + + TabOcrEntry + + + + + + 0 + + + + tab.device + + + + + tab.b30 + + + + + + + + + TabOcr_Device + QWidget +
ui.implements.tabs.tabOcr.tabOcr_Device
+ 1 +
+ + TabOcr_B30 + QWidget +
ui.implements.tabs.tabOcr.tabOcr_B30
+ 1 +
+
+ + +
diff --git a/ui/designer/tabs/tabOcrEntry_ui.py b/ui/designer/tabs/tabOcrEntry_ui.py new file mode 100644 index 0000000..6144e6a --- /dev/null +++ b/ui/designer/tabs/tabOcrEntry_ui.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- + +################################################################################ +## Form generated from reading UI file 'tabOcrEntry.ui' +## +## Created by: Qt User Interface Compiler version 6.5.1 +## +## WARNING! All changes made in this file will be lost when recompiling UI file! +################################################################################ + +from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale, + QMetaObject, QObject, QPoint, QRect, + QSize, QTime, QUrl, Qt) +from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor, + QFont, QFontDatabase, QGradient, QIcon, + QImage, QKeySequence, QLinearGradient, QPainter, + QPalette, QPixmap, QRadialGradient, QTransform) +from PySide6.QtWidgets import (QApplication, QSizePolicy, QTabWidget, QVBoxLayout, + QWidget) + +from ui.implements.tabs.tabOcr.tabOcr_B30 import TabOcr_B30 +from ui.implements.tabs.tabOcr.tabOcr_Device import TabOcr_Device + +class Ui_TabOcrEntry(object): + def setupUi(self, TabOcrEntry): + if not TabOcrEntry.objectName(): + TabOcrEntry.setObjectName(u"TabOcrEntry") + TabOcrEntry.resize(600, 478) + TabOcrEntry.setWindowTitle(u"TabOcrEntry") + self.verticalLayout = QVBoxLayout(TabOcrEntry) + self.verticalLayout.setObjectName(u"verticalLayout") + self.tabWidget = QTabWidget(TabOcrEntry) + self.tabWidget.setObjectName(u"tabWidget") + self.tab = TabOcr_Device() + self.tab.setObjectName(u"tab") + self.tabWidget.addTab(self.tab, "") + self.tab_2 = TabOcr_B30() + self.tab_2.setObjectName(u"tab_2") + self.tabWidget.addTab(self.tab_2, "") + + self.verticalLayout.addWidget(self.tabWidget) + + + self.retranslateUi(TabOcrEntry) + + self.tabWidget.setCurrentIndex(0) + + + QMetaObject.connectSlotsByName(TabOcrEntry) + # setupUi + + def retranslateUi(self, TabOcrEntry): + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), QCoreApplication.translate("TabOcrEntry", u"tab.device", None)) + self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), QCoreApplication.translate("TabOcrEntry", u"tab.b30", None)) + pass + # retranslateUi + diff --git a/ui/extends/components/ocrQueue/__init__.py b/ui/extends/components/ocrQueue/__init__.py index 08060d8..abe8b66 100644 --- a/ui/extends/components/ocrQueue/__init__.py +++ b/ui/extends/components/ocrQueue/__init__.py @@ -1,9 +1,10 @@ import logging -from typing import Any, Callable +from typing import Any, Callable, Optional, overload from arcaea_offline.calculate import calculate_score_range from arcaea_offline.database import Database from arcaea_offline.models import Chart, ScoreInsert +from arcaea_offline_ocr.b30.shared import B30OcrResultItem from arcaea_offline_ocr.device.shared import DeviceOcrResult from PySide6.QtCore import ( QAbstractListModel, @@ -30,7 +31,7 @@ logger = logging.getLogger(__name__) class OcrRunnableSignals(QObject): rowId: int = -1 - resultReady = Signal(DeviceOcrResult) + resultReady = Signal("QVariant") finished = Signal() @@ -45,7 +46,7 @@ class OcrQueueModel(QAbstractListModel): ImageQImageRole = Qt.ItemDataRole.UserRole + 2 ImagePixmapRole = Qt.ItemDataRole.UserRole + 3 - DeviceOcrResultRole = Qt.ItemDataRole.UserRole + 10 + OcrResultRole = Qt.ItemDataRole.UserRole + 10 ScoreInsertRole = Qt.ItemDataRole.UserRole + 11 ChartRole = Qt.ItemDataRole.UserRole + 12 ScoreValidateOkRole = Qt.ItemDataRole.UserRole + 13 @@ -97,55 +98,86 @@ class OcrQueueModel(QAbstractListModel): item = self.__items[index.row()] updateRole = None - if role == self.DeviceOcrResultRole and isinstance(value, DeviceOcrResult): - item[self.DeviceOcrResultRole] = value - self.updateRole = role + if role == self.OcrResultRole: + item[self.OcrResultRole] = value + updateRole = role if role == self.ChartRole and isinstance(value, Chart): item[self.ChartRole] = value self.updateScoreValidateOk(index.row()) - self.updateRole = role + updateRole = role if role == self.ScoreInsertRole and isinstance(value, ScoreInsert): item[self.ScoreInsertRole] = value self.updateScoreValidateOk(index.row()) - self.updateRole = role + updateRole = role if role == self.ScoreValidateOkRole and isinstance(value, bool): item[self.ScoreValidateOkRole] = value - self.updateRole = role + updateRole = role if role == self.OcrRunnableRole and isinstance(value, OcrRunnable): item[self.OcrRunnableRole] = value - self.updateRole = role + updateRole = role if role == self.ProcessOcrResultFuncRole and callable(value): item[self.ProcessOcrResultFuncRole] = value - self.updateRole = role + updateRole = role if updateRole is not None: self.dataChanged.emit(index, index, [updateRole]) return True else: + logger.warning( + f"{repr(self)} setData at row {index.row()} with role {role} and value {value} rejected." + ) return False + @overload + def addItem( + self, + image: str, + runnable: OcrRunnable = None, + process_func: Callable[[Optional[str], QImage, Any], ScoreInsert] = None, + ): + ... + + @overload + def addItem( + self, + image: QImage, + runnable: OcrRunnable = None, + process_func: Callable[[Optional[str], QImage, Any], ScoreInsert] = None, + ): + ... + def addItem( self, - imagePath: str, - runnable: OcrRunnable = None, - process_func: Callable = None, + image, + runnable=None, + process_func=None, ): - if imagePath in self.imagePaths or not QFileInfo(imagePath).exists(): - logger.warning(f"Attempting to add an invalid file {imagePath}") - return + if isinstance(image, str): + if image in self.imagePaths or not QFileInfo(image).exists(): + logger.warning(f"Attempting to add an invalid file {image}") + return + imagePath = image + qImage = QImage(image) + qPixmap = QPixmap(image) + elif isinstance(image, QImage): + imagePath = None + qImage = image.copy() + qPixmap = QPixmap(qImage) + else: + raise ValueError("Unsupported type for `image`") self.beginInsertRows(QModelIndex(), self.rowCount(), self.rowCount()) self.__items.append( { self.ImagePathRole: imagePath, - self.ImageQImageRole: QImage(imagePath), - self.ImagePixmapRole: QPixmap(imagePath), - self.DeviceOcrResultRole: None, + self.ImageQImageRole: qImage, + self.ImagePixmapRole: qPixmap, + self.OcrResultRole: None, self.ScoreInsertRole: None, self.ChartRole: None, self.ScoreValidateOkRole: False, @@ -155,19 +187,19 @@ class OcrQueueModel(QAbstractListModel): ) self.endInsertRows() - def updateOcrResult(self, row: int, result: DeviceOcrResult) -> bool: - if not 0 <= row < self.rowCount() or not isinstance(result, DeviceOcrResult): + def updateOcrResult(self, row: int, result: Any) -> bool: + if not 0 <= row < self.rowCount(): return False index = self.index(row, 0) imagePath: str = index.data(self.ImagePathRole) + qImage: QImage = index.data(self.ImageQImageRole) + print(row, result) processOcrResultFunc = index.data(self.ProcessOcrResultFuncRole) - chart, scoreInsert = processOcrResultFunc(imagePath, result) + chart, scoreInsert = processOcrResultFunc(imagePath, qImage, result) - # song_id = self.__db.fuzzy_search_song_id(result.title)[0][0] - - self.setData(index, result, self.DeviceOcrResultRole) + self.setData(index, result, self.OcrResultRole) self.setData(index, chart, self.ChartRole) self.setData(index, scoreInsert, self.ScoreInsertRole) return True @@ -175,7 +207,6 @@ class OcrQueueModel(QAbstractListModel): @Slot(DeviceOcrResult) def ocrTaskReady(self, result: DeviceOcrResult): row = self.sender().rowId - print(row) self.updateOcrResult(row, result) @Slot() @@ -184,7 +215,6 @@ class OcrQueueModel(QAbstractListModel): self.progress.emit(self.__taskFinishedNum) if self.__taskFinishedNum == self.__taskNum: self.finished.emit() - print("model finished") def startQueue(self): self.__taskNum = self.rowCount() @@ -266,11 +296,11 @@ class OcrQueueTableProxyModel(QAbstractTableModel): OcrQueueModel.ImagePixmapRole, ], [ - OcrQueueModel.DeviceOcrResultRole, + OcrQueueModel.OcrResultRole, OcrQueueModel.ChartRole, ], [ - OcrQueueModel.DeviceOcrResultRole, + OcrQueueModel.OcrResultRole, OcrQueueModel.ScoreInsertRole, OcrQueueModel.ChartRole, OcrQueueModel.ScoreValidateOkRole, @@ -333,9 +363,9 @@ class OcrQueueTableProxyModel(QAbstractTableModel): def setData(self, index, value, role): if index.column() == 2 and role == OcrQueueModel.ChartRole: - return self.sourceModel().setItemChart(index.row(), value) + return self.sourceModel().setData(index, value, role) if index.column() == 3 and role == OcrQueueModel.ScoreInsertRole: - return self.sourceModel().setItemScore(index.row(), value) + return self.sourceModel().setData(index, value, role) return False def flags(self, index: QModelIndex) -> Qt.ItemFlag: @@ -365,9 +395,7 @@ class OcrChartDelegate(ChartDelegate): return index.data(OcrQueueModel.ChartRole) def paintWarningBackground(self, index: QModelIndex) -> bool: - return isinstance( - index.data(OcrQueueModel.DeviceOcrResultRole), DeviceOcrResult - ) + return isinstance(index.data(OcrQueueModel.OcrResultRole), DeviceOcrResult) def setModelData(self, editor, model: OcrQueueTableProxyModel, index): if editor.validate(): @@ -385,9 +413,13 @@ class OcrScoreDelegate(ScoreDelegate): return index.data(OcrQueueModel.ScoreValidateOkRole) def paintWarningBackground(self, index: QModelIndex) -> bool: - return isinstance( - index.data(OcrQueueModel.DeviceOcrResultRole), DeviceOcrResult - ) + return True + # return isinstance(self.getChart(index), Chart) and isinstance( + # self.getScore(index), ScoreInsert + # ) + # return isinstance( + # index.data(OcrQueueModel.OcrResultRole), (DeviceOcrResult, B30OcrResultItem) + # ) def setModelData(self, editor, model: OcrQueueTableProxyModel, index): if super().confirmSetModelData(editor): diff --git a/ui/extends/shared/cv2_utils.py b/ui/extends/shared/cv2_utils.py new file mode 100644 index 0000000..c587a4d --- /dev/null +++ b/ui/extends/shared/cv2_utils.py @@ -0,0 +1,28 @@ +import cv2 +import numpy as np +from PySide6.QtGui import QImage + + +def cv2BgrMatToQImage(mat) -> QImage: + arr = np.ascontiguousarray(mat) + return QImage( + arr.data, + arr.shape[1], + arr.shape[0], + arr.strides[0], + QImage.Format.Format_RGB888, + ).rgbSwapped() + + +def qImageToCvMatBgr(qImg: QImage): + # from Bing AI, references + # 1: https://stackoverflow.com/q/384759/16484891 | CC BY-SA 4.0 + # 2: https://stackoverflow.com/q/37552924/16484891 | CC BY-SA 3.0 + qImg = qImg.convertToFormat(QImage.Format.Format_RGB888) + qImg = qImg.copy().rgbSwapped() + return np.ndarray( + (qImg.height(), qImg.width(), 3), + buffer=qImg.constBits(), + strides=[qImg.bytesPerLine(), 3, 1], + dtype=np.uint8, + ) diff --git a/ui/extends/shared/delegates/imageDelegate.py b/ui/extends/shared/delegates/imageDelegate.py index 64694c6..80481da 100644 --- a/ui/extends/shared/delegates/imageDelegate.py +++ b/ui/extends/shared/delegates/imageDelegate.py @@ -51,7 +51,10 @@ class ImageDelegate(QStyledItemDelegate): label.setWindowFlag(Qt.WindowType.WindowMinimizeButtonHint, False) label.setWindowFlag(Qt.WindowType.WindowMaximizeButtonHint, False) label.setWindowFlag(Qt.WindowType.WindowCloseButtonHint, True) - label.setWindowTitle(QFileInfo(self.getImagePath(index)).fileName()) + imagePath = self.getImagePath(index) + label.setWindowTitle( + QFileInfo(imagePath).fileName() if imagePath else "Preview" + ) pixmap = pixmap.scaled( 800, 800, diff --git a/ui/extends/tabs/tabOcr/tabOcr_B30.py b/ui/extends/tabs/tabOcr/tabOcr_B30.py new file mode 100644 index 0000000..c58dab2 --- /dev/null +++ b/ui/extends/tabs/tabOcr/tabOcr_B30.py @@ -0,0 +1,51 @@ +import logging + +from arcaea_offline.database import Database +from arcaea_offline.models import Chart, ScoreInsert +from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr +from arcaea_offline_ocr.b30.shared import B30OcrResultItem +from PySide6.QtGui import QImage + +logger = logging.getLogger(__name__) + +from ui.extends.components.ocrQueue import OcrRunnable + + +class ChieriV4OcrRunnable(OcrRunnable): + def __init__(self, ocr: ChieriBotV4Ocr, component): + super().__init__() + self.ocr = ocr + self.component = component.copy() + + def run(self): + try: + result = self.ocr.ocr_component(self.component) + self.signals.resultReady.emit(result) + except Exception: + logger.exception("ChieriV4 ocr component error") + finally: + self.signals.finished.emit() + + +def b30ResultToScoreInsert(_, qImage: QImage, result: B30OcrResultItem): + if not result.song_id and not result.title: + raise ValueError("no title or song_id") + + db = Database() + if not result.song_id: + song_id = db.fuzzy_search_song_id(result.title)[0][0] + else: + song_id = result.song_id + + chart = Chart.from_db_row(db.get_chart(song_id, result.rating_class)) + score = ScoreInsert( + song_id=song_id, + rating_class=result.rating_class, + score=result.score, + time=1485014400, + pure=result.pure, + far=result.far, + lost=result.lost, + ) + + return (chart, score) diff --git a/ui/extends/tabs/tabOcr.py b/ui/extends/tabs/tabOcr/tabOcr_Device.py similarity index 93% rename from ui/extends/tabs/tabOcr.py rename to ui/extends/tabs/tabOcr/tabOcr_Device.py index b0bebe4..14a0598 100644 --- a/ui/extends/tabs/tabOcr.py +++ b/ui/extends/tabs/tabOcr/tabOcr_Device.py @@ -3,7 +3,7 @@ import logging from typing import Tuple from arcaea_offline.database import Database -from arcaea_offline.models import ScoreInsert, Chart +from arcaea_offline.models import Chart, ScoreInsert from arcaea_offline_ocr.device.shared import DeviceOcrResult from arcaea_offline_ocr.device.v2.ocr import DeviceV2Ocr from arcaea_offline_ocr.device.v2.rois import DeviceV2Rois @@ -52,7 +52,9 @@ def getImageDate(imagePath: str) -> QDateTime: class ScoreInsertConverter: @staticmethod - def deviceV2(imagePath: str, result: DeviceOcrResult) -> Tuple[Chart, ScoreInsert]: + def deviceV2( + imagePath: str, _, result: DeviceOcrResult + ) -> Tuple[Chart, ScoreInsert]: db = Database() scoreInsert = ScoreInsert( song_id=result.song_id, diff --git a/ui/implements/mainwindow.py b/ui/implements/mainwindow.py index 0770a5c..8cb2f36 100644 --- a/ui/implements/mainwindow.py +++ b/ui/implements/mainwindow.py @@ -3,7 +3,7 @@ from traceback import format_exception from PySide6.QtWidgets import QMainWindow from ui.designer.mainwindow_ui import Ui_MainWindow -from ui.implements.tabs.tabOcr import TabOcr +from ui.implements.tabs.tabOcrEntry import TabOcrEntry # try: # import arcaea_offline_ocr @@ -32,7 +32,7 @@ class MainWindow(Ui_MainWindow, QMainWindow): # else: # self.tab_ocr = TabOcrDisabled(self.tabWidget) # self.tab_ocr.contentLabel.setText(OCR_ERROR_TEXT) - self.tab_ocr = TabOcr(self.tabWidget) + self.tab_ocr = TabOcrEntry(self.tabWidget) self.tabWidget.insertTab(ocrTabIndex, self.tab_ocr, "") self.tabWidget.setCurrentIndex(currentIndex) self.retranslateUi(self) diff --git a/ui/implements/tabs/tabOcr/tabOcr_B30.py b/ui/implements/tabs/tabOcr/tabOcr_B30.py new file mode 100644 index 0000000..190a989 --- /dev/null +++ b/ui/implements/tabs/tabOcr/tabOcr_B30.py @@ -0,0 +1,161 @@ +import logging +from pathlib import Path + +import cv2 +from arcaea_offline_ocr.b30.chieri.v4.ocr import ChieriBotV4Ocr +from arcaea_offline_ocr.sift_db import SIFTDatabase +from arcaea_offline_ocr.utils import imread_unicode + +# from paddleocr import PaddleOCR +from PySide6.QtCore import Signal, Slot +from PySide6.QtWidgets import QWidget + +from ui.designer.tabs.tabOcr.tabOcr_B30_ui import Ui_TabOcr_B30 +from ui.extends.components.ocrQueue import OcrQueueModel +from ui.extends.shared.cv2_utils import cv2BgrMatToQImage, qImageToCvMatBgr +from ui.extends.tabs.tabOcr.tabOcr_B30 import ( + ChieriV4OcrRunnable, + b30ResultToScoreInsert, +) + +logger = logging.getLogger(__name__) + + +class TabOcr_B30(Ui_TabOcr_B30, QWidget): + tryPrepareOcr = Signal() + + def __init__(self, parent=None): + super().__init__(parent) + self.setupUi(self) + + self.b30TypeComboBox.addItem("ChieriV4", "chieri_v4") + self.b30TypeComboBox.setCurrentIndex(0) + self.b30TypeComboBox.setEnabled(False) + + # self.paddleFolderSelector.setMode( + # self.paddleFolderSelector.getExistingDirectory + # ) + + self.imageSelector.filesSelected.connect(self.imageSelected) + self.knnModelSelector.filesSelected.connect(self.knnModelSelected) + self.b30KnnModelSelector.filesSelected.connect(self.b30KnnModelSelected) + # self.paddleFolderSelector.filesSelected.connect(self.paddleFolderSelected) + self.siftDatabaseSelector.filesSelected.connect(self.siftDatabaseSelected) + + self.imagePath = None # for checking only + self.img = None + self.paddleFolder = None + self.paddle = None + self.knnModel = None + self.b30KnnModel = None + self.siftDatabase = None + + self.ocr = None + + self.tryPrepareOcr.connect(self.prepareOcr) + + self.ocrQueueModel = OcrQueueModel(self) + self.ocrQueue.setModel(self.ocrQueueModel) + + def imageSelected(self): + selectedFiles = self.imageSelector.selectedFiles() + if selectedFiles: + imagePath = selectedFiles[0] + self.imagePath = imagePath + self.img = imread_unicode(imagePath) + self.tryPrepareOcr.emit() + + def knnModelSelected(self): + selectedFiles = self.knnModelSelector.selectedFiles() + if selectedFiles: + knnModelPath = selectedFiles[0] + self.knnModel = cv2.ml.KNearest.load(knnModelPath) + self.tryPrepareOcr.emit() + + def b30KnnModelSelected(self): + selectedFiles = self.b30KnnModelSelector.selectedFiles() + if selectedFiles: + b30KnnModelPath = selectedFiles[0] + self.b30KnnModel = cv2.ml.KNearest.load(b30KnnModelPath) + self.tryPrepareOcr.emit() + + def siftDatabaseSelected(self): + selectedFiles = self.siftDatabaseSelector.selectedFiles() + if selectedFiles: + siftDatabasePath = selectedFiles[0] + self.siftDatabase = SIFTDatabase(siftDatabasePath) + self.tryPrepareOcr.emit() + + def paddleFolderSelected(self): + selectedFiles = self.paddleFolderSelector.selectedFiles() + if selectedFiles: + self.paddleFolder = selectedFiles[0] + self.initPaddle() + self.tryPrepareOcr.emit() + + def initPaddle(self): + paddleFolder = Path(self.paddleFolder) + paddleDetFolder = paddleFolder / "det" + paddleClsFolder = paddleFolder / "cls" + paddleRecFolder = paddleFolder / "rec" + + if not (paddleDetFolder.exists() and paddleRecFolder.exists()): + logger.warning("paddleocr folder incomplete, aborting.") + return + + self.paddle = PaddleOCR( + show_log=False, + use_angle_cls=False, + det_model_dir=str(paddleDetFolder), + cls_model_dir=str(paddleClsFolder), + rec_model_dir=str(paddleRecFolder), + ) + + def prepareOcr(self): + b30Type = self.b30TypeComboBox.currentData() + if not b30Type: + return + + if b30Type == "chieri_v4": + if ( + not self.imagePath + or not self.knnModel + or not self.b30KnnModel + or not self.siftDatabase + ): + return + + self.ocrQueueModel.clear() + + ocr = ChieriBotV4Ocr(self.knnModel, self.b30KnnModel, self.siftDatabase) + ocr.set_factor(self.img) + self.ocr = ocr + + roi = ocr.rois + for component in roi.components(self.img): + qImage = cv2BgrMatToQImage(component.copy()) + self.ocrQueueModel.addItem(qImage) + self.ocrQueue.resizeTableView() + + @Slot() + def on_ocr_startButton_clicked(self): + if ( + not self.imagePath + or not self.knnModel + or not self.b30KnnModel + or not self.siftDatabase + ): + return + + for row in range(self.ocrQueueModel.rowCount()): + index = self.ocrQueueModel.index(row, 0) + qImage = index.data(OcrQueueModel.ImageQImageRole) + cv2Mat = qImageToCvMatBgr(qImage) + runnable = ChieriV4OcrRunnable(self.ocr, cv2Mat) + self.ocrQueueModel.setData(index, runnable, OcrQueueModel.OcrRunnableRole) + self.ocrQueueModel.setData( + index, + b30ResultToScoreInsert, + OcrQueueModel.ProcessOcrResultFuncRole, + ) + self.ocrQueueModel.startQueue() diff --git a/ui/implements/tabs/tabOcr.py b/ui/implements/tabs/tabOcr/tabOcr_Device.py similarity index 94% rename from ui/implements/tabs/tabOcr.py rename to ui/implements/tabs/tabOcr/tabOcr_Device.py index aa55473..2b4d5bd 100644 --- a/ui/implements/tabs/tabOcr.py +++ b/ui/implements/tabs/tabOcr/tabOcr_Device.py @@ -8,13 +8,16 @@ from arcaea_offline_ocr.sift_db import SIFTDatabase from PySide6.QtCore import Slot from PySide6.QtWidgets import QFileDialog, QWidget -from ui.designer.tabs.tabOcr_ui import Ui_TabOcr +from ui.designer.tabs.tabOcr.tabOcr_Device_ui import Ui_TabOcr_Device from ui.extends.components.ocrQueue import OcrQueueModel from ui.extends.settings import Settings -from ui.extends.tabs.tabOcr import ScoreInsertConverter, TabDeviceV2OcrRunnable +from ui.extends.tabs.tabOcr.tabOcr_Device import ( + ScoreInsertConverter, + TabDeviceV2OcrRunnable, +) -class TabOcr(Ui_TabOcr, QWidget): +class TabOcr_Device(Ui_TabOcr_Device, QWidget): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) diff --git a/ui/implements/tabs/tabOcrEntry.py b/ui/implements/tabs/tabOcrEntry.py new file mode 100644 index 0000000..9be167f --- /dev/null +++ b/ui/implements/tabs/tabOcrEntry.py @@ -0,0 +1,9 @@ +from PySide6.QtWidgets import QWidget + +from ui.designer.tabs.tabOcrEntry_ui import Ui_TabOcrEntry + + +class TabOcrEntry(Ui_TabOcrEntry, QWidget): + def __init__(self, parent=None): + super().__init__(parent) + self.setupUi(self)