feat: TabOcr_BuildPHashDatabase

This commit is contained in:
2023-10-09 22:48:08 +08:00
parent de8c5d28a7
commit 4a1e20a45f
9 changed files with 746 additions and 5 deletions

View File

@ -0,0 +1,255 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>tabOcr_BuildPHashDatabase</class>
<widget class="QWidget" name="tabOcr_BuildPHashDatabase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>632</width>
<height>551</height>
</rect>
</property>
<property name="windowTitle">
<string notr="true">tabOcr_BuildPHashDatabase</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>folders.title</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>folders.songDir</string>
</property>
</widget>
</item>
<item>
<widget class="FileSelector" name="songDirSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>folders.charIconDir</string>
</property>
</widget>
</item>
<item>
<widget class="FileSelector" name="charIconDirSelector" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>options.title</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string notr="true">hash_size</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="hashSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimum">
<number>2</number>
</property>
<property name="maximum">
<number>64</number>
</property>
<property name="value">
<number>16</number>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_4">
<property name="text">
<string notr="true">highfreq_factor</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="highfreqFactorSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximum">
<number>32</number>
</property>
<property name="value">
<number>4</number>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="optionsResetButton">
<property name="text">
<string>resetButton</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>0</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="format">
<string notr="true">%v/%m - %p%</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="buildButton">
<property name="text">
<string>buildButton</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>FileSelector</class>
<extends>QWidget</extends>
<header>ui.implements.components.fileSelector</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,193 @@
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'tabOcr_BuildPHashDatabase.ui'
##
## Created by: Qt User Interface Compiler version 6.5.2
##
## 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, QGroupBox, QHBoxLayout, QLabel,
QProgressBar, QPushButton, QSizePolicy, QSpacerItem,
QSpinBox, QVBoxLayout, QWidget)
from ui.implements.components.fileSelector import FileSelector
class Ui_tabOcr_BuildPHashDatabase(object):
def setupUi(self, tabOcr_BuildPHashDatabase):
if not tabOcr_BuildPHashDatabase.objectName():
tabOcr_BuildPHashDatabase.setObjectName(u"tabOcr_BuildPHashDatabase")
tabOcr_BuildPHashDatabase.resize(632, 551)
tabOcr_BuildPHashDatabase.setWindowTitle(u"tabOcr_BuildPHashDatabase")
self.verticalLayout_3 = QVBoxLayout(tabOcr_BuildPHashDatabase)
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
self.verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer)
self.groupBox = QGroupBox(tabOcr_BuildPHashDatabase)
self.groupBox.setObjectName(u"groupBox")
self.verticalLayout = QVBoxLayout(self.groupBox)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.label = QLabel(self.groupBox)
self.label.setObjectName(u"label")
self.horizontalLayout.addWidget(self.label)
self.songDirSelector = FileSelector(self.groupBox)
self.songDirSelector.setObjectName(u"songDirSelector")
sizePolicy = QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.songDirSelector.sizePolicy().hasHeightForWidth())
self.songDirSelector.setSizePolicy(sizePolicy)
self.horizontalLayout.addWidget(self.songDirSelector)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.label_2 = QLabel(self.groupBox)
self.label_2.setObjectName(u"label_2")
self.horizontalLayout_2.addWidget(self.label_2)
self.charIconDirSelector = FileSelector(self.groupBox)
self.charIconDirSelector.setObjectName(u"charIconDirSelector")
sizePolicy.setHeightForWidth(self.charIconDirSelector.sizePolicy().hasHeightForWidth())
self.charIconDirSelector.setSizePolicy(sizePolicy)
self.horizontalLayout_2.addWidget(self.charIconDirSelector)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.verticalLayout_3.addWidget(self.groupBox)
self.groupBox_2 = QGroupBox(tabOcr_BuildPHashDatabase)
self.groupBox_2.setObjectName(u"groupBox_2")
self.horizontalLayout_3 = QHBoxLayout(self.groupBox_2)
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.verticalLayout_2 = QVBoxLayout()
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
self.horizontalLayout_6 = QHBoxLayout()
self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
self.label_3 = QLabel(self.groupBox_2)
self.label_3.setObjectName(u"label_3")
self.label_3.setText(u"hash_size")
self.label_3.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_6.addWidget(self.label_3)
self.hashSizeSpinBox = QSpinBox(self.groupBox_2)
self.hashSizeSpinBox.setObjectName(u"hashSizeSpinBox")
sizePolicy1 = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
sizePolicy1.setHorizontalStretch(0)
sizePolicy1.setVerticalStretch(0)
sizePolicy1.setHeightForWidth(self.hashSizeSpinBox.sizePolicy().hasHeightForWidth())
self.hashSizeSpinBox.setSizePolicy(sizePolicy1)
self.hashSizeSpinBox.setMinimum(2)
self.hashSizeSpinBox.setMaximum(64)
self.hashSizeSpinBox.setValue(16)
self.horizontalLayout_6.addWidget(self.hashSizeSpinBox)
self.verticalLayout_2.addLayout(self.horizontalLayout_6)
self.horizontalLayout_7 = QHBoxLayout()
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
self.label_4 = QLabel(self.groupBox_2)
self.label_4.setObjectName(u"label_4")
self.label_4.setText(u"highfreq_factor")
self.label_4.setAlignment(Qt.AlignRight|Qt.AlignTrailing|Qt.AlignVCenter)
self.horizontalLayout_7.addWidget(self.label_4)
self.highfreqFactorSpinBox = QSpinBox(self.groupBox_2)
self.highfreqFactorSpinBox.setObjectName(u"highfreqFactorSpinBox")
sizePolicy1.setHeightForWidth(self.highfreqFactorSpinBox.sizePolicy().hasHeightForWidth())
self.highfreqFactorSpinBox.setSizePolicy(sizePolicy1)
self.highfreqFactorSpinBox.setMaximum(32)
self.highfreqFactorSpinBox.setValue(4)
self.horizontalLayout_7.addWidget(self.highfreqFactorSpinBox)
self.verticalLayout_2.addLayout(self.horizontalLayout_7)
self.horizontalLayout_3.addLayout(self.verticalLayout_2)
self.horizontalSpacer_3 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_3.addItem(self.horizontalSpacer_3)
self.optionsResetButton = QPushButton(self.groupBox_2)
self.optionsResetButton.setObjectName(u"optionsResetButton")
self.horizontalLayout_3.addWidget(self.optionsResetButton)
self.verticalLayout_3.addWidget(self.groupBox_2)
self.progressBar = QProgressBar(tabOcr_BuildPHashDatabase)
self.progressBar.setObjectName(u"progressBar")
self.progressBar.setMaximum(0)
self.progressBar.setValue(0)
self.progressBar.setAlignment(Qt.AlignCenter)
self.progressBar.setFormat(u"%v/%m - %p%")
self.verticalLayout_3.addWidget(self.progressBar)
self.horizontalLayout_5 = QHBoxLayout()
self.horizontalLayout_5.setObjectName(u"horizontalLayout_5")
self.horizontalSpacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer)
self.buildButton = QPushButton(tabOcr_BuildPHashDatabase)
self.buildButton.setObjectName(u"buildButton")
self.horizontalLayout_5.addWidget(self.buildButton)
self.horizontalSpacer_2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
self.horizontalLayout_5.addItem(self.horizontalSpacer_2)
self.verticalLayout_3.addLayout(self.horizontalLayout_5)
self.verticalSpacer_2 = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
self.verticalLayout_3.addItem(self.verticalSpacer_2)
self.retranslateUi(tabOcr_BuildPHashDatabase)
QMetaObject.connectSlotsByName(tabOcr_BuildPHashDatabase)
# setupUi
def retranslateUi(self, tabOcr_BuildPHashDatabase):
self.groupBox.setTitle(QCoreApplication.translate("tabOcr_BuildPHashDatabase", u"folders.title", None))
self.label.setText(QCoreApplication.translate("tabOcr_BuildPHashDatabase", u"folders.songDir", None))
self.label_2.setText(QCoreApplication.translate("tabOcr_BuildPHashDatabase", u"folders.charIconDir", None))
self.groupBox_2.setTitle(QCoreApplication.translate("tabOcr_BuildPHashDatabase", u"options.title", None))
self.optionsResetButton.setText(QCoreApplication.translate("tabOcr_BuildPHashDatabase", u"resetButton", None))
self.buildButton.setText(QCoreApplication.translate("tabOcr_BuildPHashDatabase", u"buildButton", None))
pass
# retranslateUi

View File

@ -29,6 +29,11 @@
<string>tab.b30</string>
</attribute>
</widget>
<widget class="TabOcr_BuildPHashDatabase" name="tab_3">
<attribute name="title">
<string>tab.buildPHashDatabase</string>
</attribute>
</widget>
</widget>
</item>
</layout>
@ -46,6 +51,12 @@
<header>ui.implements.tabs.tabOcr.tabOcr_B30</header>
<container>1</container>
</customwidget>
<customwidget>
<class>TabOcr_BuildPHashDatabase</class>
<extends>QWidget</extends>
<header>ui.implements.tabs.tabOcr.tabOcr_BuildPHashDatabase</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -3,7 +3,7 @@
################################################################################
## Form generated from reading UI file 'tabOcrEntry.ui'
##
## Created by: Qt User Interface Compiler version 6.5.1
## Created by: Qt User Interface Compiler version 6.5.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
@ -19,6 +19,7 @@ from PySide6.QtWidgets import (QApplication, QSizePolicy, QTabWidget, QVBoxLayou
QWidget)
from ui.implements.tabs.tabOcr.tabOcr_B30 import TabOcr_B30
from ui.implements.tabs.tabOcr.tabOcr_BuildPHashDatabase import TabOcr_BuildPHashDatabase
from ui.implements.tabs.tabOcr.tabOcr_Device import TabOcr_Device
class Ui_TabOcrEntry(object):
@ -37,6 +38,9 @@ class Ui_TabOcrEntry(object):
self.tab_2 = TabOcr_B30()
self.tab_2.setObjectName(u"tab_2")
self.tabWidget.addTab(self.tab_2, "")
self.tab_3 = TabOcr_BuildPHashDatabase()
self.tab_3.setObjectName(u"tab_3")
self.tabWidget.addTab(self.tab_3, "")
self.verticalLayout.addWidget(self.tabWidget)
@ -52,6 +56,7 @@ class Ui_TabOcrEntry(object):
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))
self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3), QCoreApplication.translate("TabOcrEntry", u"tab.buildPHashDatabase", None))
pass
# retranslateUi

View File

@ -1,3 +1,5 @@
from .build_phash import build_image_phash_database
try:
import json

View File

@ -0,0 +1,61 @@
import sqlite3
import time
from pathlib import Path
from typing import Any, Callable, Optional
import cv2
from arcaea_offline_ocr.phash_db import phash_opencv
def build_image_phash_database(
images: list[Path],
labels: list[str],
*,
hash_size: int = 16,
highfreq_factor: int = 4,
progress_func: Optional[Callable[[int, int], Any]] = None,
):
assert len(images) == len(labels)
conn = sqlite3.connect(":memory:", check_same_thread=False)
with conn:
cursor = conn.cursor()
cursor.execute("CREATE TABLE properties (key TEXT, value TEXT)")
cursor.executemany(
"INSERT INTO properties VALUES (?, ?)",
[
("hash_size", hash_size),
("highfreq_factor", highfreq_factor),
],
)
image_num = len(images)
id_hashes = []
for i, label, image_path in zip(range(image_num), labels, images):
image_hash = phash_opencv(
cv2.imread(str(image_path.resolve()), cv2.IMREAD_GRAYSCALE),
hash_size=hash_size,
highfreq_factor=highfreq_factor,
)
image_hash_bytes = image_hash.flatten().tobytes()
id_hashes.append([label, image_hash_bytes])
if progress_func:
progress_func(i + 1, image_num)
hash_length = len(id_hashes[0][1])
cursor.execute(f"CREATE TABLE hashes (id TEXT, hash BLOB({hash_length}))")
cursor.executemany(
"INSERT INTO hashes VALUES (?, ?)",
id_hashes,
)
cursor.executemany(
"INSERT INTO properties VALUES (?, ?)",
[("built_timestamp", int(time.time()))],
)
conn.commit()
return conn

View File

@ -0,0 +1,138 @@
import logging
import re
import sqlite3
import time
from pathlib import Path
from PySide6.QtCore import QThread, Signal, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox, QWidget
from ui.designer.tabs.tabOcr.tabOcr_BuildPHashDatabase_ui import (
Ui_tabOcr_BuildPHashDatabase,
)
from ui.extends.ocr import build_image_phash_database
logger = logging.getLogger(__name__)
class BuildDatabaseThread(QThread):
conn: sqlite3.Connection
progress = Signal(int, int)
success = Signal()
error = Signal(str)
finished = Signal()
def __init__(
self,
images: list[Path],
labels: list[str],
*,
hashSize: int | None = None,
highfreqFactor: int | None = None,
):
super().__init__()
self.images = images
self.labels = labels
self.hashSize = hashSize
self.highfreqFactor = highfreqFactor
def run(self):
try:
progressFunc = lambda i, total: self.progress.emit(i, total)
kwargsDict = {}
if self.hashSize is not None:
kwargsDict["hash_size"] = self.hashSize
if self.highfreqFactor is not None:
kwargsDict["highfreq_factor"] = self.highfreqFactor
self.conn = build_image_phash_database(
self.images, self.labels, progress_func=progressFunc, **kwargsDict
)
self.success.emit()
except Exception as e:
logger.exception("Error during pHash database build")
self.error.emit(str(e))
finally:
self.finished.emit()
class TabOcr_BuildPHashDatabase(Ui_tabOcr_BuildPHashDatabase, QWidget):
def __init__(self, parent=None):
super().__init__(parent)
self.setupUi(self)
self.songDirSelector.setMode(self.songDirSelector.getExistingDirectory)
self.charIconDirSelector.setMode(self.charIconDirSelector.getExistingDirectory)
self.buildButton.clicked.connect(self.databaseBuildStart)
@Slot()
def on_optionsResetButton_clicked(self):
self.hashSizeSpinBox.setValue(16)
self.highfreqFactorSpinBox.setValue(4)
def databaseFileName(self):
return f"image-phash-{int(time.time() * 1000)}.db"
def databaseBuildStart(self):
if not self.songDirSelector.selectedFiles():
QMessageBox.critical(self, None, "Song directory not selected.")
return
if not self.charIconDirSelector.selectedFiles():
QMessageBox.critical(self, None, "Char icon directory not selected.")
return
songDir = self.songDirSelector.selectedFiles()[0]
charIconDir = self.charIconDirSelector.selectedFiles()[0]
acceptExts = [".jpg", ".png"]
songFilePaths = [
p for p in Path(songDir).glob("**/*") if p.suffix in acceptExts
]
charIconFilePaths = [
p for p in Path(charIconDir).glob("**/*") if p.suffix in acceptExts
]
songLabels = [re.sub(r"_.*$", "", p.stem) for p in songFilePaths]
charLabels = [f"character||{p.stem}" for p in charIconFilePaths]
self.databaseBuildThread = BuildDatabaseThread(
songFilePaths + charIconFilePaths, songLabels + charLabels
)
self.databaseBuildThread.progress.connect(self.databaseBuildProgress)
self.databaseBuildThread.success.connect(self.databaseBuildSuccess)
self.databaseBuildThread.error.connect(self.databaseBuildError)
self.buildButton.setEnabled(False)
self.databaseBuildThread.start()
@Slot(int, int)
def databaseBuildProgress(self, i: int, total: int):
if i < 5:
self.progressBar.setMaximum(total)
self.progressBar.setValue(i)
@Slot(str)
def databaseBuildError(self, msg: str):
QMessageBox.critical(self, "Error", msg)
self.databaseBuildCleanUp()
@Slot()
def databaseBuildSuccess(self):
dbMemory = self.databaseBuildThread.conn
dbFileName, _ = QFileDialog.getSaveFileName(self, None, self.databaseFileName())
if not dbFileName:
self.databaseBuildCleanUp()
QMessageBox.information(self, None, "User canceled operation.")
return
dbDisk = sqlite3.connect(dbFileName)
dbMemory.backup(dbDisk)
self.databaseBuildCleanUp()
def databaseBuildCleanUp(self):
self.databaseBuildThread.deleteLater()
self.databaseBuildThread = None
self.progressBar.setMaximum(0)
self.progressBar.setValue(0)
self.buildButton.setEnabled(True)

View File

@ -89,12 +89,12 @@
<translation>Continue</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="115"/>
<location filename="../../startup/databaseChecker.py" line="117"/>
<source>dialog.tryInitExistingDatabase</source>
<translation>The existing database doesn&apos;t seem to be initialized properly, try initialize again?</translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="131"/>
<location filename="../../startup/databaseChecker.py" line="133"/>
<source>dialog.confirmNewDatabase</source>
<translation>Database file does not exist. Create now?</translation>
</message>
@ -696,6 +696,11 @@ validation</translation>
<source>tab.b30</source>
<translation>B30</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcrEntry.ui" line="34"/>
<source>tab.buildPHashDatabase</source>
<translation>Build pHash Database</translation>
</message>
</context>
<context>
<name>TabOcr_B30</name>
@ -1167,4 +1172,37 @@ validation</translation>
<translation>Result (play rating)</translation>
</message>
</context>
<context>
<name>tabOcr_BuildPHashDatabase</name>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="33"/>
<source>folders.title</source>
<translation>Data Folders</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="41"/>
<source>folders.songDir</source>
<translation>Song jackets</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="62"/>
<source>folders.charIconDir</source>
<translation>Character icons</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="84"/>
<source>options.title</source>
<translation>Options</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="170"/>
<source>resetButton</source>
<translation>Reset</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="211"/>
<source>buildButton</source>
<translation>Build</translation>
</message>
</context>
</TS>

View File

@ -89,12 +89,12 @@
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="115"/>
<location filename="../../startup/databaseChecker.py" line="117"/>
<source>dialog.tryInitExistingDatabase</source>
<translation></translation>
</message>
<message>
<location filename="../../startup/databaseChecker.py" line="131"/>
<location filename="../../startup/databaseChecker.py" line="133"/>
<source>dialog.confirmNewDatabase</source>
<translation></translation>
</message>
@ -695,6 +695,11 @@
<source>tab.b30</source>
<translation>B30</translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcrEntry.ui" line="34"/>
<source>tab.buildPHashDatabase</source>
<translation> pHash </translation>
</message>
</context>
<context>
<name>TabOcr_B30</name>
@ -1166,4 +1171,37 @@
<translation> PTT</translation>
</message>
</context>
<context>
<name>tabOcr_BuildPHashDatabase</name>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="33"/>
<source>folders.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="41"/>
<source>folders.songDir</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="62"/>
<source>folders.charIconDir</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="84"/>
<source>options.title</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="170"/>
<source>resetButton</source>
<translation></translation>
</message>
<message>
<location filename="../../designer/tabs/tabOcr/tabOcr_BuildPHashDatabase.ui" line="211"/>
<source>buildButton</source>
<translation></translation>
</message>
</context>
</TS>