Add image.SubImage, move page-related logic to page.* classes.

This commit is contained in:
Andrey Golovizin 2014-08-09 13:53:37 +02:00
parent e3a68c3043
commit b5194ca018
2 changed files with 143 additions and 39 deletions

View file

@ -31,42 +31,70 @@ def _is_nonblank(bitmap):
class Image(object): class Image(object):
"""Basic image class.""" """Basic image class."""
child_cls = None
def __init__(self, data): def __init__(self, data):
self._data = data self.parent = self
self.data = data
self.y1 = 0
self.y2 = self.width
self.x1 = 0
self.x2 = self.height
def __getitem__(self, key): def __getitem__(self, key):
return type(self)(self._data.__getitem__(key)) """Return a SubImage for the specified region."""
def indices(sliceobj, length):
"""Decode a slice object and return a pair of end:start indices."""
if sliceobj is None:
return 0, length
elif isinstance(sliceobj, int):
return sliceobj, sliceobj + 1
elif isinstance(sliceobj, slice):
start, end, stride = sliceobj.indices(length)
if stride != 1:
raise NotImplementedError
return start, end
else:
raise NotImplementedError(sliceobj)
if not isinstance(key, tuple):
yslice = key
xslice = None
else:
yslice, xslice = key
ystart, yend = indices(yslice, self.height)
xstart, xend = indices(xslice, self.width)
y1 = self.y1 + ystart
y2 = self.y1 + yend
x1 = self.x1 + xstart
x2 = self.x1 + xend
return SubImage(self.parent, y1, y2, x1, x2)
def _repr_png_(self): def _repr_png_(self):
buf = BytesIO() buf = BytesIO()
imsave(buf, self._data) imsave(buf, self.data)
return buf.getvalue() return buf.getvalue()
@classmethod @classmethod
def fromfile(cls, filename): def fromfile(cls, filename):
return cls(imread(filename)) return cls(imread(filename))
@classmethod
def space(cls, height, width):
return cls(np.ones((height, width)))
@property @property
def shape(self): def shape(self):
return self._data.shape return self.data.shape
@property @cached_property
def T(self): def T(self):
return type(self)(self._data.swapaxes(0, 1)) return type(self)(self.data.swapaxes(0, 1))
@property @property
def height(self): def height(self):
return self._data.shape[0] return self.data.shape[0]
@property @property
def width(self): def width(self):
return self._data.shape[1] return self.data.shape[1]
@cached_property @cached_property
def bitmap(self): def bitmap(self):
@ -76,7 +104,7 @@ class Image(object):
1 = black (letter) pixel 1 = black (letter) pixel
""" """
grayscale = rgb2gray(self._data) grayscale = rgb2gray(self.data)
return (grayscale < 1).astype('b') return (grayscale < 1).astype('b')
@cached_property @cached_property
@ -123,10 +151,28 @@ class Image(object):
bottom_margin = _get_margin_height(reversed(self.bitmap)) bottom_margin = _get_margin_height(reversed(self.bitmap))
return self[top_margin:self.height - bottom_margin, :] return self[top_margin:self.height - bottom_margin, :]
def _iter_children(self, min_space):
if self.child_cls is None:
raise NotImplementedError
class SubImage(Image):
def __init__(self, parent, y1, y2, x1, x2):
self.parent = parent
self.y1 = y1
self.y2 = y2
self.x1 = x1
self.x2 = x2
@property
def data(self):
return self.parent.data[self.y1:self.y2, self.x1:self.x2]
@property
def bitmap(self):
return self.parent.bitmap[self.y1:self.y2, self.x1:self.x2]
@property
def T(self):
return type(self)(self.parent.T, self.x1, self.x2, self.y1, self.y2)
def _iter_lines(self, min_space, T=False):
line_start = None line_start = None
prev_line_end = 0 prev_line_end = 0
@ -136,28 +182,9 @@ class Image(object):
line_start = i line_start = i
height = line_start - prev_line_end height = line_start - prev_line_end
if height >= min_space: if height >= min_space:
yield self.child_cls.space(height, self.width) yield self[prev_line_end:line_start]
else: else:
if line_start is not None: if line_start is not None:
yield self.child_cls(self._data[line_start:i, :]) yield self[line_start:i,:]
line_start = None line_start = None
prev_line_end = i prev_line_end = i
class Letter(Image):
pass
class Line(Image):
child_cls = Letter
def __iter__(self):
for rotated_letter in self.T._iter_children(min_space=10):
yield rotated_letter.T.strip()
class Page(Image):
child_cls = Line
def __iter__(self):
return self._iter_children(min_space=200)

77
pixelocr/page.py Normal file
View file

@ -0,0 +1,77 @@
# 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 .image import Image
class PageObject(object):
def __init__(self, image):
self.image = image
def _repr_png_(self):
return self.image._repr_png_()
@property
def shape(self):
return self.image.shape
@property
def height(self):
return self.image.width
@property
def width(self):
return self.image.width
@property
def x1(self):
return self.image.x1
@property
def x2(self):
return self.image.x2
@property
def y1(self):
return self.image.y1
@property
def y2(self):
return self.image.y2
@property
def x1(self):
return self.image.x1
@property
def x2(self):
return self.image.x2
class Page(PageObject):
def __iter__(self):
for line_img in self.image._iter_lines(min_space=200):
yield Line(line_img)
class Line(PageObject):
def __iter__(self):
for rotated_letter_img in self.image.T._iter_lines(min_space=10, T=True):
yield Letter(rotated_letter_img.T.strip())
class Letter(PageObject):
pass