Move all GUI-related stuff from OCREngine to GUIProxy.
This commit is contained in:
parent
8e4eb7d152
commit
4e35d56696
5 changed files with 113 additions and 40 deletions
|
|
@ -28,10 +28,16 @@ from PyQt4 import QtCore
|
||||||
QtCore.signal = QtCore.pyqtSignal
|
QtCore.signal = QtCore.pyqtSignal
|
||||||
QtCore.slot = QtCore.pyqtSlot
|
QtCore.slot = QtCore.pyqtSlot
|
||||||
|
|
||||||
|
from PyQt4.QtCore import (
|
||||||
|
QThread,
|
||||||
|
)
|
||||||
|
|
||||||
from PyQt4.QtGui import (
|
from PyQt4.QtGui import (
|
||||||
|
qApp,
|
||||||
QApplication,
|
QApplication,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from .guiproxy import GUIProxy
|
||||||
from .window import MainWindow
|
from .window import MainWindow
|
||||||
from .ocrengine import OCREngine
|
from .ocrengine import OCREngine
|
||||||
|
|
||||||
|
|
@ -51,6 +57,18 @@ def load_entry_point(group, name):
|
||||||
raise ValueError('Entry point {} in group {} not found'.format(name, group))
|
raise ValueError('Entry point {} in group {} not found'.format(name, group))
|
||||||
|
|
||||||
|
|
||||||
|
class WorkerThread(QThread):
|
||||||
|
def __init__(self, ocr, quit=False):
|
||||||
|
super().__init__()
|
||||||
|
self.ocr = ocr
|
||||||
|
self.quit = quit
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.ocr.recognize()
|
||||||
|
if self.quit:
|
||||||
|
qApp.quit()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
|
@ -58,19 +76,21 @@ def main():
|
||||||
QApplication.setApplicationName("PixelOCR");
|
QApplication.setApplicationName("PixelOCR");
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
gui_proxy = GUIProxy()
|
||||||
ocr = OCREngine(
|
ocr = OCREngine(
|
||||||
args.filename,
|
args.filename,
|
||||||
|
ui=gui_proxy,
|
||||||
skip=args.skip,
|
skip=args.skip,
|
||||||
limit=args.limit,
|
limit=args.limit,
|
||||||
quit=args.quit,
|
|
||||||
output_format=load_entry_point('pixelocr.formatting', args.output_format).load()(),
|
output_format=load_entry_point('pixelocr.formatting', args.output_format).load()(),
|
||||||
)
|
)
|
||||||
app.aboutToQuit.connect(ocr.save_glyphdb)
|
app.aboutToQuit.connect(ocr.save_glyphdb)
|
||||||
|
ocr_thread = WorkerThread(ocr, quit=args.quit)
|
||||||
|
|
||||||
win = MainWindow(ocr)
|
win = MainWindow(ocr)
|
||||||
win.glyphEntered.connect(ocr.give_help)
|
win.glyphEntered.connect(gui_proxy.give_help)
|
||||||
win.show()
|
win.show()
|
||||||
ocr.start()
|
ocr_thread.start()
|
||||||
|
|
||||||
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
signal.signal(signal.SIGINT, signal.SIG_DFL)
|
||||||
sys.exit(app.exec_())
|
sys.exit(app.exec_())
|
||||||
|
|
|
||||||
55
pixelocr/gui/guiproxy.py
Normal file
55
pixelocr/gui/guiproxy.py
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Copyright (C) 2014 Andrey Golovizin
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
from queue import Queue
|
||||||
|
|
||||||
|
from PyQt4.QtCore import (
|
||||||
|
signal,
|
||||||
|
slot,
|
||||||
|
QObject,
|
||||||
|
)
|
||||||
|
|
||||||
|
from PyQt4.QtGui import (
|
||||||
|
qApp,
|
||||||
|
)
|
||||||
|
|
||||||
|
from ..ui import BaseUI
|
||||||
|
from ..page import Page, Glyph
|
||||||
|
|
||||||
|
|
||||||
|
class GUIProxy(QObject, BaseUI):
|
||||||
|
unknownGlyph = signal([Glyph, bool, bool])
|
||||||
|
pageChanged = signal([Page])
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
self.help_queue = Queue()
|
||||||
|
|
||||||
|
def turn_page(self, page):
|
||||||
|
self.pageChanged.emit(page)
|
||||||
|
|
||||||
|
def process_events(self):
|
||||||
|
qApp.processEvents()
|
||||||
|
|
||||||
|
def ask_for_help(self, unknown_glyph):
|
||||||
|
self.unknownGlyph.emit(unknown_glyph, self.last_style.bold, self.last_style.italic)
|
||||||
|
return self.receive_help()
|
||||||
|
|
||||||
|
def give_help(self, *args):
|
||||||
|
self.help_queue.put(args)
|
||||||
|
|
||||||
|
def receive_help(self):
|
||||||
|
return self.help_queue.get()
|
||||||
|
|
@ -17,35 +17,22 @@
|
||||||
import itertools
|
import itertools
|
||||||
from glob import glob
|
from glob import glob
|
||||||
from os import path
|
from os import path
|
||||||
from queue import Queue
|
|
||||||
|
|
||||||
from PyQt4.QtCore import (
|
|
||||||
signal,
|
|
||||||
slot,
|
|
||||||
QThread,
|
|
||||||
)
|
|
||||||
from PyQt4.QtGui import (
|
|
||||||
qApp
|
|
||||||
)
|
|
||||||
|
|
||||||
from .. import formatting
|
from .. import formatting
|
||||||
from ..image import Image
|
from ..image import Image
|
||||||
from ..page import Page, Glyph, Space
|
from ..page import Page, Space
|
||||||
from ..glyphdb import GlyphDB, SPACE, NEWLINE
|
from ..glyphdb import GlyphDB, SPACE, NEWLINE
|
||||||
|
|
||||||
|
|
||||||
class OCREngine(QThread):
|
class OCREngine(object):
|
||||||
SPACE_WIDTH = 15
|
SPACE_WIDTH = 15
|
||||||
unknownGlyph = signal([Glyph, bool, bool])
|
|
||||||
pageChanged = signal([Page])
|
|
||||||
|
|
||||||
def __init__(self, dirname, skip=0, limit=None, quit=False, output_format='text'):
|
def __init__(self, dirname, ui, skip=0, limit=None, output_format='text'):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.dirname = dirname
|
self.dirname = dirname
|
||||||
|
self.ui = ui
|
||||||
self.filenames = sorted(glob(path.join(dirname, '*.png')))[skip:skip + limit if limit else None]
|
self.filenames = sorted(glob(path.join(dirname, '*.png')))[skip:skip + limit if limit else None]
|
||||||
self.glyphdb = GlyphDB(path.join(self.dirname, 'glyphdb.pickle'))
|
self.glyphdb = GlyphDB(path.join(self.dirname, 'glyphdb.pickle'))
|
||||||
self.help_queue = Queue()
|
|
||||||
self.quit = quit
|
|
||||||
self.output_format = output_format
|
self.output_format = output_format
|
||||||
self.last_style = (False, False, (255, 255, 255)) # FIXME get rid of hardcoded value
|
self.last_style = (False, False, (255, 255, 255)) # FIXME get rid of hardcoded value
|
||||||
|
|
||||||
|
|
@ -55,15 +42,10 @@ class OCREngine(QThread):
|
||||||
def load_page(self, filename):
|
def load_page(self, filename):
|
||||||
return Page(Image.fromfile(filename), filename)
|
return Page(Image.fromfile(filename), filename)
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.recognize()
|
|
||||||
if self.quit:
|
|
||||||
qApp.quit()
|
|
||||||
|
|
||||||
def recognize(self):
|
def recognize(self):
|
||||||
for filename in self.filenames:
|
for filename in self.filenames:
|
||||||
page = self.load_page(filename)
|
page = self.load_page(filename)
|
||||||
self.pageChanged.emit(page)
|
self.ui.turn_page(page)
|
||||||
page_text = self.recognize_page(page)
|
page_text = self.recognize_page(page)
|
||||||
print(page_text)
|
print(page_text)
|
||||||
with open(filename + self.output_format.suffix, 'w') as page_text_file:
|
with open(filename + self.output_format.suffix, 'w') as page_text_file:
|
||||||
|
|
@ -80,24 +62,15 @@ class OCREngine(QThread):
|
||||||
yield NEWLINE
|
yield NEWLINE
|
||||||
|
|
||||||
def recognize_glyph(self, glyph):
|
def recognize_glyph(self, glyph):
|
||||||
qApp.processEvents()
|
self.ui.process_events()
|
||||||
|
|
||||||
if isinstance(glyph, Space):
|
if isinstance(glyph, Space):
|
||||||
return SPACE
|
return SPACE
|
||||||
try:
|
try:
|
||||||
glyph_data = self.glyphdb[glyph]
|
glyph_data = self.glyphdb[glyph]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
text, bold, italic = self.ask_for_help(glyph)
|
text, bold, italic = self.ui.ask_for_help(glyph)
|
||||||
glyph_data = self.glyphdb.add_glyph(glyph, text, bold, italic)
|
glyph_data = self.glyphdb.add_glyph(glyph, text, bold, italic)
|
||||||
self.last_style = glyph_data.style
|
self.last_style = glyph_data.style
|
||||||
return glyph_data
|
return glyph_data
|
||||||
|
|
||||||
def ask_for_help(self, unknown_glyph):
|
|
||||||
self.unknownGlyph.emit(unknown_glyph, self.last_style.bold, self.last_style.italic)
|
|
||||||
return self.receive_help()
|
|
||||||
|
|
||||||
def give_help(self, *args):
|
|
||||||
self.help_queue.put(args)
|
|
||||||
|
|
||||||
def receive_help(self):
|
|
||||||
return self.help_queue.get()
|
|
||||||
|
|
|
||||||
|
|
@ -59,9 +59,9 @@ class MainWindow(QMainWindow):
|
||||||
self.glyphEdit.glyphEntered.connect(self.unknownGlyphEntered)
|
self.glyphEdit.glyphEntered.connect(self.unknownGlyphEntered)
|
||||||
self.glyphEdit.glyphEntered.connect(self.pageScene.clearHighlight)
|
self.glyphEdit.glyphEntered.connect(self.pageScene.clearHighlight)
|
||||||
self.glyphEdit.glyphEntered.connect(self.glyphDBEdit.updateData)
|
self.glyphEdit.glyphEntered.connect(self.glyphDBEdit.updateData)
|
||||||
ocr.pageChanged.connect(self.pageScene.setPage)
|
ocr.ui.pageChanged.connect(self.pageScene.setPage)
|
||||||
ocr.pageChanged.connect(self.showPageTitle)
|
ocr.ui.pageChanged.connect(self.showPageTitle)
|
||||||
ocr.unknownGlyph.connect(self.unknownGlyph)
|
ocr.ui.unknownGlyph.connect(self.unknownGlyph)
|
||||||
self.page.setFocusProxy(self.glyphEdit)
|
self.page.setFocusProxy(self.glyphEdit)
|
||||||
|
|
||||||
layout = QVBoxLayout(centralWidget)
|
layout = QVBoxLayout(centralWidget)
|
||||||
|
|
|
||||||
25
pixelocr/ui.py
Normal file
25
pixelocr/ui.py
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Copyright (C) 2014 Andrey Golovizin
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
class BaseUI(object):
|
||||||
|
def ask_for_help(self, unknown_glyph):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def turn_page(self, page):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def process_events(self):
|
||||||
|
pass
|
||||||
Loading…
Add table
Add a link
Reference in a new issue