From 0fd694787a03dc127c37f54f21f7140bc90992dc Mon Sep 17 00:00:00 2001 From: Gres Date: Sat, 10 Oct 2015 13:33:18 +0300 Subject: [PATCH] Added ability to automatically fix defined recognition errors. --- Recognizer.cpp | 9 +++-- Recognizer.h | 2 + RecognizerHelper.cpp | 78 +++++++++++++++++++++++++++++++++++++++ RecognizerHelper.h | 33 +++++++++++++++++ ScreenTranslator.pro | 6 ++- SettingsEditor.cpp | 87 +++++++++++++++++++++++++++++++++++++++++++- SettingsEditor.h | 9 +++++ SettingsEditor.ui | 47 ++++++++++++++++++------ 8 files changed, 253 insertions(+), 18 deletions(-) create mode 100644 RecognizerHelper.cpp create mode 100644 RecognizerHelper.h diff --git a/Recognizer.cpp b/Recognizer.cpp index 895234b..caa274f 100644 --- a/Recognizer.cpp +++ b/Recognizer.cpp @@ -8,10 +8,11 @@ #include "Settings.h" #include "ImageProcessing.h" #include "StAssert.h" +#include "RecognizerHelper.h" Recognizer::Recognizer (QObject *parent) : QObject (parent), - engine_ (NULL), imageScale_ (0) { + engine_ (NULL), recognizerHelper_ (new RecognizerHelper), imageScale_ (0) { applySettings (); } @@ -19,6 +20,8 @@ void Recognizer::applySettings () { QSettings settings; settings.beginGroup (settings_names::recogntionGroup); + recognizerHelper_->load (); + tessDataDir_ = settings.value (settings_names::tessDataPlace, settings_values::tessDataPlace).toString (); if (tessDataDir_.right (1) != "/") { @@ -57,8 +60,8 @@ void Recognizer::recognize (ProcessingItem item) { bool isCustomLanguage = (!item.ocrLanguage.isEmpty () && item.ocrLanguage != ocrLanguage_); tesseract::TessBaseAPI *engine = (isCustomLanguage) ? NULL : engine_; + QString language = (isCustomLanguage) ? item.ocrLanguage : ocrLanguage_; if (engine == NULL) { - QString language = (isCustomLanguage) ? item.ocrLanguage : ocrLanguage_; if (!initEngine (engine, language)) { return; } @@ -78,7 +81,7 @@ void Recognizer::recognize (ProcessingItem item) { } if (!result.isEmpty ()) { - item.recognized = result; + item.recognized = recognizerHelper_->substitute (result, language); emit recognized (item); } else { diff --git a/Recognizer.h b/Recognizer.h index 5cfde88..b7e5760 100644 --- a/Recognizer.h +++ b/Recognizer.h @@ -9,6 +9,7 @@ namespace tesseract { class TessBaseAPI; } +class RecognizerHelper; class Recognizer : public QObject { Q_OBJECT @@ -29,6 +30,7 @@ class Recognizer : public QObject { private: tesseract::TessBaseAPI *engine_; + RecognizerHelper *recognizerHelper_; QString tessDataDir_; QString ocrLanguage_; diff --git a/RecognizerHelper.cpp b/RecognizerHelper.cpp new file mode 100644 index 0000000..844c9c9 --- /dev/null +++ b/RecognizerHelper.cpp @@ -0,0 +1,78 @@ +#include + +#include "RecognizerHelper.h" + +RecognizerHelper::RecognizerHelper () + : fileName_ ("subs.csv") { +} + +void RecognizerHelper::load () { + subs_.clear (); + QFile f (fileName_); + if (!f.open (QFile::ReadOnly)) { + return; + } + QByteArray data = f.readAll (); + f.close (); + QStringList lines = QString::fromUtf8 (data).split ('\n', QString::SkipEmptyParts); + foreach (const QString &line, lines) { + QStringList parts = line.mid (1, line.size () - 2).split ("\",\""); // remove " + if (parts.size () < 3) { + continue; + } + subs_.append (Sub (parts[0], parts[1], parts[2])); + } +} + +void RecognizerHelper::save () { + QFile f (fileName_); + if (!f.open (QFile::WriteOnly)) { + return; + } + foreach (const Sub &sub, subs_) { + QStringList parts = QStringList () << sub.language << sub.source << sub.target; + QString line = "\"" + parts.join ("\",\"") + "\"\n"; + f.write (line.toUtf8 ()); + } + f.close (); +} + +QString RecognizerHelper::substitute (const QString &source, const QString &language) const { + QString result = source; + while (true) { + int bestMatchIndex = -1; + int bestMatchLen = 0; + int index = -1; + foreach (const Sub &sub, subs_) { + ++index; + if (sub.language != language || !result.contains (sub.source)) { + continue; + } + int len = sub.source.length (); + if (len > bestMatchLen) { + bestMatchLen = len; + bestMatchIndex = index; + } + } + if (bestMatchIndex > -1) { + const Sub &sub = subs_.at (bestMatchIndex); + result.replace (sub.source, sub.target); + continue; + } + break; + } + + return result; +} + +const RecognizerHelper::Subs &RecognizerHelper::subs () const { + return subs_; +} + +void RecognizerHelper::setSubs (const Subs &subs) { + subs_ = subs; +} + +RecognizerHelper::Sub::Sub (const QString &language, const QString &source, const QString &target) + : language (language), source (source), target (target) { +} diff --git a/RecognizerHelper.h b/RecognizerHelper.h new file mode 100644 index 0000000..eff2e15 --- /dev/null +++ b/RecognizerHelper.h @@ -0,0 +1,33 @@ +#ifndef RECOGNIZERHELPER_H +#define RECOGNIZERHELPER_H + +#include + +class RecognizerHelper { + public: + struct Sub { + Sub (const QString &language = QString (), const QString &source = QString (), + const QString &target = QString ()); + QString language; + QString source; + QString target; + }; + typedef QList Subs; + + public: + RecognizerHelper (); + + void load (); + void save (); + + QString substitute (const QString &source, const QString& language) const; + + const Subs &subs () const; + void setSubs (const Subs &subs); + + private: + QString fileName_; + Subs subs_; +}; + +#endif // RECOGNIZERHELPER_H diff --git a/ScreenTranslator.pro b/ScreenTranslator.pro index b7abe99..0147d6a 100644 --- a/ScreenTranslator.pro +++ b/ScreenTranslator.pro @@ -39,7 +39,8 @@ SOURCES += main.cpp\ LanguageHelper.cpp \ WebTranslator.cpp \ WebTranslatorProxy.cpp \ - TranslatorHelper.cpp + TranslatorHelper.cpp \ + RecognizerHelper.cpp HEADERS += \ Manager.h \ @@ -55,7 +56,8 @@ HEADERS += \ WebTranslator.h \ WebTranslatorProxy.h \ StAssert.h \ - TranslatorHelper.h + TranslatorHelper.h \ + RecognizerHelper.h FORMS += \ SettingsEditor.ui \ diff --git a/SettingsEditor.cpp b/SettingsEditor.cpp index 1f452eb..eddf170 100644 --- a/SettingsEditor.cpp +++ b/SettingsEditor.cpp @@ -2,6 +2,8 @@ #include "ui_SettingsEditor.h" #include "LanguageHelper.h" #include "TranslatorHelper.h" +#include "RecognizerHelper.h" +#include "StAssert.h" #include #include @@ -11,7 +13,8 @@ SettingsEditor::SettingsEditor (const LanguageHelper &dictionary, QWidget *parent) : QDialog (parent), - ui (new Ui::SettingsEditor), translatorHelper_ (new TranslatorHelper), dictionary_ (dictionary), + ui (new Ui::SettingsEditor), translatorHelper_ (new TranslatorHelper), + recognizerHelper_ (new RecognizerHelper), dictionary_ (dictionary), buttonGroup_ (new QButtonGroup (this)) { ui->setupUi (this); @@ -22,6 +25,9 @@ SettingsEditor::SettingsEditor (const LanguageHelper &dictionary, QWidget *paren connect (ui->tessdataEdit, SIGNAL (textChanged (const QString &)), SLOT (initOcrLangCombo (const QString &))); + connect (ui->recognizerFixTable, SIGNAL (itemChanged (QTableWidgetItem *)), + SLOT (recognizerFixTableItemChanged (QTableWidgetItem *))); + ui->translateLangCombo->addItems (dictionary_.translateLanguagesUi ()); loadSettings (); loadState (); @@ -29,6 +35,7 @@ SettingsEditor::SettingsEditor (const LanguageHelper &dictionary, QWidget *paren SettingsEditor::~SettingsEditor () { saveState (); + delete recognizerHelper_; delete translatorHelper_; delete ui; } @@ -58,8 +65,29 @@ void SettingsEditor::saveSettings () const { QString ocrLanguageVal = dictionary_.ocrUiToCode (ui->ocrLangCombo->currentText ()); settings.setValue (ocrLanguage, ocrLanguageVal); settings.setValue (imageScale, ui->imageScaleSpin->value ()); - settings.endGroup (); + { //Recognizer substitutions + RecognizerHelper::Subs subs; + QTableWidget *t = ui->recognizerFixTable; // Shortcut + for (int i = 0, end = t->rowCount () - 1; i < end; ++i) { + QComboBox *combo = static_cast(t->cellWidget (i, SubsColLanguage)); + QString langUi = combo->currentText (); + RecognizerHelper::Sub sub; + sub.language = dictionary_.ocrUiToCode (langUi); +#define GET(COL) (t->item (i, COL) ? t->item (i, COL)->text () : QString ()) + sub.source = GET (SubsColSource); + sub.target = GET (SubsColTarget); +#undef GET + if (langUi.isEmpty () || sub.language == langUi || sub.source.isEmpty ()) { + continue; + } + subs.append (sub); + } + recognizerHelper_->setSubs (subs); + recognizerHelper_->save (); + } + + settings.endGroup (); settings.beginGroup (translationGroup); settings.setValue (doTranslation, ui->doTranslationCheck->isChecked ()); @@ -115,8 +143,28 @@ void SettingsEditor::loadSettings () { QString ocrLanguage = dictionary_.ocrCodeToUi (GET (ocrLanguage).toString ()); ui->ocrLangCombo->setCurrentText (ocrLanguage); ui->imageScaleSpin->setValue (GET (imageScale).toInt ()); + + {//Recognizer substitutions + recognizerHelper_->load (); + RecognizerHelper::Subs subs = recognizerHelper_->subs (); + ui->recognizerFixTable->setRowCount (subs.size ()); + int row = 0; + foreach (const RecognizerHelper::Sub & sub, subs) { + if (!initSubsTableRow (row, sub.language)) { + continue; + } + ui->recognizerFixTable->setItem (row, SubsColSource, new QTableWidgetItem (sub.source)); + ui->recognizerFixTable->setItem (row, SubsColTarget, new QTableWidgetItem (sub.target)); + ++row; + } + ui->recognizerFixTable->setRowCount (row + 1); + initSubsTableRow (row); + ui->recognizerFixTable->resizeColumnsToContents (); + } + settings.endGroup (); + settings.beginGroup (settings_names::translationGroup); ui->doTranslationCheck->setChecked (GET (doTranslation).toBool ()); ui->translatorDebugCheck->setChecked (GET (translationDebugMode).toBool ()); @@ -138,6 +186,23 @@ void SettingsEditor::loadSettings () { #undef GET } +bool SettingsEditor::initSubsTableRow (int row, const QString &languageCode) { + QString lang = dictionary_.ocrCodeToUi (languageCode); + if (!languageCode.isEmpty () && lang == languageCode) { + return false; + } + QComboBox *langCombo = new QComboBox (ui->recognizerFixTable); + langCombo->setModel (ui->ocrLangCombo->model ()); + if (!languageCode.isEmpty ()) { + langCombo->setCurrentText (lang); + } + else { + langCombo->setCurrentIndex (ui->ocrLangCombo->currentIndex ()); + } + ui->recognizerFixTable->setCellWidget (row, SubsColLanguage, langCombo); + return true; +} + void SettingsEditor::saveState () const { QSettings settings; settings.beginGroup (settings_names::guiGroup); @@ -154,3 +219,21 @@ void SettingsEditor::initOcrLangCombo (const QString &path) { ui->ocrLangCombo->clear (); ui->ocrLangCombo->addItems (dictionary_.availableOcrLanguagesUi (path)); } + +void SettingsEditor::recognizerFixTableItemChanged (QTableWidgetItem *item) { + ST_ASSERT (item->column () < 3); + int row = item->row (); + QTableWidget *t = ui->recognizerFixTable; +#define CHECK(COL) (!t->item (row, COL) || t->item (row, COL)->text ().isEmpty ()) + bool isRowEmpty = CHECK (SubsColSource) && CHECK (SubsColTarget); +#undef CHECK + int lastRow = ui->recognizerFixTable->rowCount () - 1; + if (isRowEmpty && row != lastRow) { + ui->recognizerFixTable->removeRow (row); + } + else if (!isRowEmpty && row == lastRow) { + int newRow = lastRow + 1; + ui->recognizerFixTable->insertRow (newRow); + initSubsTableRow (newRow); + } +} diff --git a/SettingsEditor.h b/SettingsEditor.h index 0558bc2..6cb5796 100644 --- a/SettingsEditor.h +++ b/SettingsEditor.h @@ -5,15 +5,21 @@ #include #include +class QTableWidgetItem; namespace Ui { class SettingsEditor; } class LanguageHelper; class TranslatorHelper; +class RecognizerHelper; class SettingsEditor : public QDialog { Q_OBJECT + enum SubsCol { + SubsColLanguage = 0, SubsColSource, SubsColTarget + }; + public: explicit SettingsEditor (const LanguageHelper &dictionary, QWidget *parent = 0); ~SettingsEditor (); @@ -28,15 +34,18 @@ class SettingsEditor : public QDialog { void saveSettings () const; void openTessdataDialog (); void initOcrLangCombo (const QString &path); + void recognizerFixTableItemChanged (QTableWidgetItem *item); private: void loadSettings (); void saveState () const; void loadState (); + bool initSubsTableRow (int row, const QString &languageCode = QString ()); private: Ui::SettingsEditor *ui; TranslatorHelper *translatorHelper_; + RecognizerHelper *recognizerHelper_; const LanguageHelper &dictionary_; QButtonGroup *buttonGroup_; }; diff --git a/SettingsEditor.ui b/SettingsEditor.ui index c74df95..18bea41 100644 --- a/SettingsEditor.ui +++ b/SettingsEditor.ui @@ -140,7 +140,7 @@ Распознавание - + @@ -196,18 +196,43 @@ - - - - Qt::Vertical + + + + <html><head/><body><p>Символы, регулярно распознаваемые с ошибками. При обнаружении будут заменены на указанные.</p></body></html> - - - 20 - 132 - + + Исправления: - + + Qt::AlignCenter + + + + + + + QAbstractItemView::SelectRows + + + true + + + + Язык + + + + + Исходный текст + + + + + Исправление + + +