Make GlyphDB store GlyphData objects instead of just text.

This commit is contained in:
Andrey Golovizin 2014-09-01 17:15:21 +02:00
parent a44091b570
commit 9c8de1ce0f
6 changed files with 90 additions and 53 deletions

View file

@ -18,34 +18,90 @@ import pickle
from collections import OrderedDict from collections import OrderedDict
from os import path from os import path
from .utils import cached_property
from .image import Image
class GlyphData(object):
def __init__(self, image_data, elevation, text, bold=False, italic=False):
self.image_data = image_data
self.elevation = elevation
self.text = text
self.bold = bold
self.italic = italic
@classmethod
def from_glyph(cls, glyph, *args, **kwargs):
return cls(glyph.image.serialize(), glyph.elevation, *args, **kwargs)
@property
def color(self):
return self.image.color
@cached_property
def image(self):
return Image.deserialize(self.image_data)
def serialize(self):
return (
self.image_data,
self.elevation,
self.text,
self.bold,
self.italic,
)
@classmethod
def deserialize(cls, args):
return cls(*args)
class GlyphDB(object): class GlyphDB(object):
def __init__(self, filename): def __init__(self, filename):
self.filename = filename self.filename = filename
self._dict = OrderedDict()
if path.isfile(self.filename): if path.isfile(self.filename):
with open(self.filename, 'rb') as fileobj: self.load()
self.data = pickle.load(fileobj)
else:
self.data = OrderedDict()
def __getitem__(self, key): def _key_from_glyph(self, glyph):
return self.data.__getitem__(key) return (glyph.image.serialize(), glyph.elevation)
def __setitem__(self, key, value): def _key_from_data(self, data):
return self.data.__setitem__(key, value) return (data.image_data, data.elevation)
def __delitem__(self, key): def __getitem__(self, glyph):
return self.data.__delitem__(key) key = self._key_from_glyph(glyph)
return self._dict[key]
def add_glyph(self, glyph, text, bold=False, italic=False):
data = GlyphData.from_glyph(glyph, text, bold=bold, italic=italic)
key = self._key_from_glyph(glyph)
self._dict[key] = data
def update(self, data):
key = self._key_from_data(data)
self._dict[key] = data
def remove(self, data):
key = self._key_from_data(data)
del self._dict[key]
def keys(self): def keys(self):
return self.data.keys() return self._dict.keys()
def items(self): def items(self):
return self.data.items() return self._dict.items()
def values(self): def values(self):
return self.data.values() return self._dict.values()
def load(self):
with open(self.filename, 'rb') as fileobj:
data = pickle.load(fileobj)
for item in data:
self.update(GlyphData.deserialize(item))
def save(self): def save(self):
data = [data.serialize() for data in self.values()]
with open(self.filename, 'wb') as fileobj: with open(self.filename, 'wb') as fileobj:
pickle.dump(self.data, fileobj) pickle.dump(data, fileobj)

View file

@ -36,27 +36,14 @@ class GlyphDBModel(QAbstractTableModel):
def __init__(self, glyphdb, parent=None): def __init__(self, glyphdb, parent=None):
super().__init__(parent) super().__init__(parent)
self.glyphdb = glyphdb self.glyphdb = glyphdb
self.keys = list(glyphdb.keys()) self.values = list(glyphdb.values())
self.images = {}
def rowCount(self, parent): def rowCount(self, parent):
return len(self.keys) return len(self.values)
def columnCount(self, parent): def columnCount(self, parent):
return 2 return 2
def _deserialize_image(self, key):
elevation_, serialized_image = key
return Image.deserialize(serialized_image).toqimage()
def get_image(self, key):
try:
image = self.images[key]
except KeyError:
image = self._deserialize_image(key)
self.images[key] = image
return image
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):
if orientation != Qt.Horizontal or role != Qt.DisplayRole: if orientation != Qt.Horizontal or role != Qt.DisplayRole:
return None return None
@ -66,24 +53,23 @@ class GlyphDBModel(QAbstractTableModel):
return 'Elevation' return 'Elevation'
def data(self, index, role): def data(self, index, role):
data = self.values[index.row()]
if index.column() == 0: if index.column() == 0:
key = self.keys[index.row()]
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
return self.glyphdb[key] return data.text
elif role == Qt.DecorationRole: elif role == Qt.DecorationRole:
return self.get_image(key) return data.image.qimage
elif index.column() == 1: elif index.column() == 1:
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
elevation, _ = self.keys[index.row()] return str(data.elevation)
return str(elevation)
def removeRows(self, row, count, parent=None): def removeRows(self, row, count, parent=None):
self.beginRemoveRows(parent, row, row + count - 1) self.beginRemoveRows(parent, row, row + count - 1)
keys = self.keys[row: row + count] values = self.values[row: row + count]
for key in keys: for value in values:
del self.glyphdb[key] self.glyphdb.remove(value)
del self.keys[row] del self.values[row]
self.endRemoveRows() self.endRemoveRows()
return True return True
@ -91,16 +77,15 @@ class GlyphDBModel(QAbstractTableModel):
def sort(self, column, order): def sort(self, column, order):
key_func = None key_func = None
if column == 0: if column == 0:
def key_func(key): def key_func(value):
return self.glyphdb[key] return value.text
elif column == 1: elif column == 1:
def key_func(key): def key_func(value):
elevation, *_ = key return value.elevation
return elevation
if key_func: if key_func:
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.keys.sort(key=key_func, reverse = (order == Qt.DescendingOrder)) self.values.sort(key=key_func, reverse = (order == Qt.DescendingOrder))
self.layoutChanged.emit() self.layoutChanged.emit()

View file

@ -75,10 +75,10 @@ class OCREngine(QThread):
if isinstance(glyph, Space): if isinstance(glyph, Space):
return ' ' return ' '
try: try:
return self.glyphdb[glyph.key] return self.glyphdb[glyph].text
except KeyError: except KeyError:
text = self.ask_for_help(glyph) text = self.ask_for_help(glyph)
self.glyphdb[glyph.key] = text self.glyphdb.add_glyph(glyph, text)
return text return text
def ask_for_help(self, unknown_glyph): def ask_for_help(self, unknown_glyph):

View file

@ -84,7 +84,7 @@ class PageScene(QGraphicsScene):
self.addRect(glyph.x - 1, glyph.y - 1, glyph.width + 1, glyph.height + 1, self.spacePen, self.spaceBrush) self.addRect(glyph.x - 1, glyph.y - 1, glyph.width + 1, glyph.height + 1, self.spacePen, self.spaceBrush)
def addPage(self, page): def addPage(self, page):
qimage = page.image.toqimage() qimage = page.image.qimage
graphicsitem = self.addPixmap(QPixmap.fromImage(qimage)) graphicsitem = self.addPixmap(QPixmap.fromImage(qimage))
return graphicsitem return graphicsitem

View file

@ -185,7 +185,8 @@ class Image(object):
array = np.fromstring(data, dtype=np.uint8).reshape(shape) array = np.fromstring(data, dtype=np.uint8).reshape(shape)
return cls(array) return cls(array)
def toqimage(self): @cached_property
def qimage(self):
from PyQt4.QtGui import QImage from PyQt4.QtGui import QImage
return QImage( return QImage(
self.data.astype(np.uint8).data, self.data.astype(np.uint8).data,

View file

@ -251,11 +251,6 @@ class Glyph(PageObject):
self.elevation = elevation self.elevation = elevation
self.line = line self.line = line
@property
def key(self):
"""Return a dictionary key uniquely representing this glyph."""
return self.elevation, self.image.serialize()
def is_body(self): def is_body(self):
"""Return True if the glyph is definitely not diacritic.""" """Return True if the glyph is definitely not diacritic."""
return self.height >= self.MIN_BODY_HEIGHT return self.height >= self.MIN_BODY_HEIGHT