~bkrpr/bookliberator/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
#!/usr/bin/python

import sys
import gtk
import Image, ImageDraw, ImageEnhance, ImageChops, ImageFilter, ImageOps
import pytesser

from misc import *

tesseract_bin = '/usr/local/bin/tesseract'

#for k, v in globals().items():             
#    print k, "=", v


class graphic():
    '''Since we cannot subclass from PIL's image class, we're making a wrapper.'''

    FLIP_LEFT_RIGHT = Image.FLIP_LEFT_RIGHT
    FLIP_TOP_BOTTOM = Image.FLIP_TOP_BOTTOM
    ROTATE_90 = Image.ROTATE_90
    ROTATE_180 = Image.ROTATE_180 
    ROTATE_270 = Image.ROTATE_270

    def __init__(self, tiff=''):
        if isinstance(tiff, str):
            self.im = Image.open(tiff)
        elif isinstance(tiff, Image.Image):
            self.im = tiff
        elif isinstance(tiff, graphic):
            self.im = tiff.im
        elif isinstance(tiff, unicode):
            self.im = Image.open(tiff)
        else:
            assert False, "Unhandled type in graphic init: %s" % tiff.__class__.__name__

        self.w, self.h = self.im.size
        self.size = self.im.size
        self.mode = self.im.mode
        self.data = False

    def tesseract(self):
        'run tesseract on the image and return the resulting text'
        try:
            text = pytesser.image_to_string(self.im)
        except pytesser.errors.Tesser_General_Exception, value:
            print "Couldn't OCR image with tesseract!"
            text = ''

        return text

    def pixbuf(self):
        '''Return pixbuf version of this image.

        From http://faq.pygtk.org/index.py?req=show&file=faq08.014.htp'''
        import StringIO
        fd = StringIO.StringIO()
        self.im.save(fd, "ppm")
        contents = fd.getvalue()
        fd.close()
        loader = gtk.gdk.PixbufLoader("pnm")
        loader.write(contents, len(contents))
        pixbuf = loader.get_pixbuf()
        loader.close()
        return pixbuf

    def enhance(self, brightness, contrast):
        '''contrast and brightness are the target percentage values of each,
        so contrast equals 400 is will enhance the contrast by +300%'''

        self.im = self.im.convert("L") #grayscale
        self.im = ImageOps.autocontrast(self.im, cutoff=0)
        if contrast != 100:
            self.im = ImageEnhance.Contrast(self.im).enhance(float(contrast)/100)
        if brightness != 100:
            self.im = ImageEnhance.Brightness(self.im).enhance(float(brightness)/100)

    def whitespace(self, p1, p2, **kwargs):
        '''Returns the percentage of the line p1,p2 that is whitespace.
        p1 and p2 are arrays of 2 ints each.
        
        skip is an optional kwarg.  Set it to n to scan every nth line.'''

        if p1[0] > p2[0]:
            p_tmp = p1
            p1 = p2
            p2 = p_tmp

        ## Calc slope and y offset of line
        s = slope(p1,p2)
        b = p1[1] - s * p1[0]

        ## Count pixels on that line
        white = black = noise = float(0)
        x = p1[0]

        if 'skip' in kwargs:
            skip = kwargs['skip']
        else:
            skip = 1

        while x < p2[0]:
            ## Calc y
            y = int(s * x + b)
            if y < 0: y = 0
            if y > self.h-1: y = self.h-1

            ## Tally pixel (x,y)
            color = self.im.getpixel((x,y))
            if color <= 256/3:
                black = black + 1
            elif color >= 256*2/3:
                white = white + 1
            else:
                noise = noise + 1

            x = x + skip

        if black == 0: black = 1 # divide by zero
        return float(white)/black / (self.w / skip) * 100

    def whitest_line(self, r1, r2):
        'returns whitest line between y=r1 and y=r2'
        max_white = 0
        w,h = self.im.size
        h1 = r1
        h2 = r2
        for y1 in range(r1, r2):
            for y2 in range(r1, r2):
                ratio = self.whitespace((0,y1),(w,y2))
                if ratio > max_white:
                    max_white = ratio
                    h1 = y1
                    h2 = y2
        return h1, h2
        
    def blackest_line(self, r1=0, r2=-1):
        'returns blackest line of image between y=r1 and y=r2'
        min_whiteness=99999999
        w,h = self.im.size
        if r2 == -1:
            r2 = h
        for y1 in range(h):
            for y2 in range(h):
                r = self.whitespace((0,y1),(w,y2))
                if r < min_whiteness:
                    min_whiteness = r
                    h1 = y1
                    h2 = y2
        return h1, h2

    def trim(self, border):
        'Frederik Lundh: http://mail.python.org/pipermail/image-sig/2008-July/005092.html'
        bg = Image.new(self.im.mode, self.size, border)
        diff = ImageChops.difference(self.im, bg)
        bbox = diff.getbbox()
        if bbox:
            return graphic(self.im.crop(bbox))
        else:
            # found no content
            raise ValueError("cannot trim; image was empty")

    def getdata(self, *arg, **kwarg):
        self.data = self.im.getdata(*arg, **kwarg)
        return self.data
    def getpixel(self, *arg, **kwarg):
        return self.im.getpixel(*arg, **kwarg)
    def crop(self,*arg, **kwarg):
        g = graphic(self.im.crop(*arg, **kwarg))
        g.getdata()
        return g
    def show(self,*arg, **kwarg):
        self.im.show(*arg, **kwarg)
    def save(self, *arg, **kwarg):
        self.im.save(*arg, **kwarg)
    def thumbnail(self, *arg, **kwarg):
        self.im.thumbnail(*arg, **kwarg)
        self.w, self.h = self.im.size

    def rotate(self,degrees, *args, **kwargs):
        'rotate image'
        r = self.im.rotate(degrees, *args, **kwargs)
        return graphic(r)

        ## This old code put the rotated image on a light background.
        ## Ian says it hurst recognition.  We should sample the greyish
        ## background and reconstruct that, perhaps.
        n = Image.new(self.mode, self.size, 255)
        n = n.rotate(degrees, expand=1)
        n = ImageChops.lighter(ImageChops.invert(n), r)
        return graphic(n)
    def transpose(self, *args, **kwargs):
        return graphic(self.im.transpose(*args, **kwargs))
    def copy(self):
        return graphic(self.im.crop((0,0,self.w,self.h)))