source: www/study/study.py @ 2011

Revision 2011, 21.7 KB checked in by sam, 6 years ago (diff)
  • Slightly reworked the 4×4 Bayer matrix so that it respects the recurrence relation.
  • Added a (yet unused) 8×8 Bayer matrix to study.py.
  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2
3import math, gd, random
4
5# Tiny image class to make examples short and readable
6class Image(gd.image):
7    def __new__(args, truecolor = False):
8        gd.gdMaxColors = 256 * 256 * 256
9        if truecolor:
10            return gd.image.__new__(args, True)
11        else:
12            return gd.image.__new__(args)
13    def save(self, name):
14        print " * saving %s" % (name,)
15        self.writePng(name)
16    def getGray(self, x, y):
17        p = self.getPixel((x, y))
18        c = self.colorComponents(p)[0] / 255.0
19        return c
20    def getRgb(self, x, y):
21        p = self.getPixel((x, y))
22        rgb = self.colorComponents(p)
23        return [rgb[0] / 255.0, rgb[1] / 255.0, rgb[2] / 255.0]
24    def setGray(self, x, y, t):
25        p = (int)(t * 255.999)
26        c = self.colorResolve((p, p, p))
27        self.setPixel((x, y), c)
28    def setRgb(self, x, y, r, g, b):
29        r = (int)(r * 255.999)
30        g = (int)(g * 255.999)
31        b = (int)(b * 255.999)
32        c = self.colorResolve((r, g, b))
33        self.setPixel((x, y), c)
34
35##############################################################################
36print "Initialisation"
37
38# Manipulate gamma values
39class Gamma:
40    def CtoI(x):
41        return math.pow(x, 2.2)
42    def ItoC(x):
43        if x < 0:
44            return -math.pow(-x, 1 / 2.2)
45        return math.pow(x, 1 / 2.2)
46    CtoI = staticmethod(CtoI)
47    ItoC = staticmethod(ItoC)
48
49# Load the 256x256 grayscale Lenna image
50lenna256bw = Image("lenna256bw.png")
51
52# Create a 32x256 grayscale gradient
53gradient256bw = Image((32, 256))
54for x in range(32):
55    for y in range(256):
56        gradient256bw.setGray(x, 255 - y, y / 255.)
57gradient256bw.save("gradient256bw.png")
58
59##############################################################################
60print "Chapter 1. Colour quantisation"
61
62# Output 1.1.1: 50% threshold
63# Output 1.1.2: 40% threshold
64# Output 1.1.3: 60% threshold
65def test11x(src, threshold):
66    (w, h) = src.size()
67    dest = Image((w, h))
68    for y in range(h):
69        for x in range(w):
70            c = src.getGray(x, y) > threshold
71            dest.setGray(x, y, c)
72    return dest
73
74test11x(lenna256bw, 0.5).save("out1-1-1.png")
75test11x(lenna256bw, 0.4).save("out1-1-2.png")
76test11x(lenna256bw, 0.6).save("out1-1-3.png")
77test11x(gradient256bw, 0.5).save("grad1-1-1.png")
78test11x(gradient256bw, 0.4).save("grad1-1-2.png")
79test11x(gradient256bw, 0.6).save("grad1-1-3.png")
80
81# Output 1.2.1: 3-colour threshold
82# Output 1.2.2: 5-colour threshold
83def test12x(src, colors):
84    (w, h) = src.size()
85    dest = Image((w, h))
86    p = -0.0001 + colors
87    q = colors - 1
88    for y in range(h):
89        for x in range(w):
90            c = src.getGray(x, y)
91            c = math.floor(c * p) / q
92            dest.setGray(x, y, c)
93    return dest
94
95test12x(lenna256bw, 3).save("out1-2-1.png")
96test12x(lenna256bw, 5).save("out1-2-2.png")
97test12x(gradient256bw, 3).save("grad1-2-1.png")
98test12x(gradient256bw, 5).save("grad1-2-2.png")
99
100##############################################################################
101print "Chapter 2. Halftoning patterns"
102
103# Pattern 2.1.1: a 50% halftone pattern with various block sizes
104dest = Image((320, 80))
105for x in range(320):
106    d = 8 >> (x / 80)
107    for y in range(80):
108        c = (x / d + y / d) & 1
109        dest.setGray(x, y, c)
110dest.save("pat2-1-1.png")
111
112# Pattern 2.1.2: 25% and 75% halftone patterns with various block sizes
113dest = Image((320, 80))
114for x in range(320):
115    d = 8 >> (x / 80)
116    for y in range(40):
117        c = ((x / d + y / d) & 1) or (y / d & 1)
118        dest.setGray(x, y, c)
119    for y in range(40, 80):
120        c = ((x / d + y / d) & 1) and (y / d & 1)
121        dest.setGray(x, y, c)
122dest.save("pat2-1-2.png")
123
124# Output 2.1.1: 20/40/60/80% threshold with 25/50/75% patterns inbetween:
125def test211(src):
126    (w, h) = src.size()
127    dest = Image((w, h))
128    for y in range(h):
129        for x in range(w):
130            c = src.getGray(x, y)
131            if c < 0.2:
132                c = 0.
133            elif c < 0.4:
134                c = ((x + y) & 1) and (y & 1)
135            elif c < 0.6:
136                c = (x + y) & 1
137            elif c < 0.8:
138                c = ((x + y) & 1) or (y & 1)
139            else:
140                c = 1.
141            dest.setGray(x, y, c)
142    return dest
143
144test211(lenna256bw).save("out2-1-1.png")
145test211(gradient256bw).save("grad2-1-1.png")
146
147# Pattern 2.2.1: vertical, mixed and horizontal black-white halftones
148dest = Image((240, 80))
149for y in range(80):
150    for x in range(80):
151        c = x & 1
152        dest.setGray(x, y, c)
153    for x in range(80, 160):
154        c = (x / d + y / d) & 1
155        dest.setGray(x, y, c)
156    for x in range(160, 240):
157        c = y & 1
158        dest.setGray(x, y, c)
159dest.save("pat2-2-1.png")
160
161# Pattern 2.2.2: two different 25% patterns
162dest = Image((320, 80))
163for y in range(80):
164    for x in range(80):
165        c = (x / 2 & 1) and (y / 2 & 1)
166        dest.setGray(x, y, c)
167    for x in range(80, 160):
168        c = (x & 1) and (y & 1)
169        dest.setGray(x, y, c)
170    for x in range(160, 240):
171        c = (x & 1) and ((y + x / 2) & 1)
172        dest.setGray(x, y, c)
173    for x in range(240, 320):
174        c = (x / 2 & 1) and ((y / 2 + x / 4) & 1)
175        dest.setGray(x, y, c)
176dest.save("pat2-2-2.png")
177
178# Output 2.3.1: 4x4 Bayer dithering
179# Output 2.3.2: 4x4 cluster dot
180# Output 2.3.3: 5x3 line dithering
181DITHER_BAYER44 = \
182    [[  0, 12,  3, 15],
183     [  8,  4, 11,  7],
184     [  2, 14,  1, 13],
185     [ 10,  6,  9,  5]]
186DITHER_BAYER88 = \
187    [[  0, 48, 12, 60,  3, 51, 15, 63],
188     [ 32, 16, 44, 28, 35, 19, 47, 31],
189     [  8, 56,  4, 52, 11, 59,  7, 55],
190     [ 40, 24, 36, 20, 43, 27, 39, 23],
191     [  2, 50, 14, 62,  1, 49, 13, 61],
192     [ 34, 18, 46, 30, 33, 17, 45, 29],
193     [ 10, 58,  6, 54,  9, 57,  5, 53],
194     [ 42, 26, 38, 22, 41, 25, 37, 21]]
195DITHER_CLUSTER44 = \
196    [[ 12,  5,  6, 13],
197     [  4,  0,  1,  7],
198     [ 11,  3,  2,  8],
199     [ 15, 10,  9, 14]]
200DITHER_LINE53 = \
201    [[ 13,  7,  0,  4, 10],
202     [  9,  3,  1,  8, 14],
203     [ 11,  5,  2,  6, 12],]
204
205def test23x(src, mat):
206    (w, h) = src.size()
207    dest = Image((w, h))
208    dx = len(mat[0])
209    dy = len(mat)
210    for y in range(h):
211        for x in range(w):
212            c = src.getGray(x, y)
213            threshold = (1. + mat[y % dy][x % dx]) / (dx * dy + 1)
214            c = c > threshold
215            dest.setGray(x, y, c)
216    return dest
217
218test23x(lenna256bw, DITHER_BAYER44).save("out2-3-1.png")
219test23x(gradient256bw, DITHER_BAYER44).save("grad2-3-1.png")
220
221test23x(lenna256bw, DITHER_CLUSTER44).save("out2-3-2.png")
222test23x(gradient256bw, DITHER_CLUSTER44).save("grad2-3-2.png")
223
224test23x(lenna256bw, DITHER_LINE53).save("out2-3-3.png")
225test23x(gradient256bw, DITHER_LINE53).save("grad2-3-3.png")
226
227# Output 2.4.1: uniform random dithering
228def test241(src):
229    random.seed(0)
230    (w, h) = src.size()
231    dest = Image((w, h))
232    for y in range(h):
233        for x in range(w):
234            c = src.getGray(x, y)
235            d = c > random.random()
236            dest.setGray(x, y, d)
237    return dest
238
239test241(lenna256bw).save("out2-4-1.png")
240test241(gradient256bw).save("grad2-4-1.png")
241
242# Output 2.4.2: gaussian random dithering
243def test242(src):
244    random.seed(0)
245    (w, h) = src.size()
246    dest = Image((w, h))
247    for y in range(h):
248        for x in range(w):
249            c = src.getGray(x, y)
250            d = c > random.gauss(0.5, 0.15)
251            dest.setGray(x, y, d)
252    return dest
253
254test242(lenna256bw).save("out2-4-2.png")
255test242(gradient256bw).save("grad2-4-2.png")
256
257# Output 2.4.3: random dither matrices
258def test243(src, mat):
259    random.seed(0)
260    (w, h) = src.size()
261    dest = Image((w, h))
262    dx = len(mat[0])
263    dy = len(mat)
264    randmat = [[(int)(random.random() * 16) for x in range(w / dx)] for y in range(h / dy)]
265    for y in range(h):
266        for x in range(w):
267            c = src.getGray(x, y)
268            val = (mat[y % dy][x % dx] + randmat[y / dy][x / dx]) % (dx * dy)
269            threshold = (1. + val) / (dx * dy + 1)
270            c = c > threshold
271            dest.setGray(x, y, c)
272    return dest
273
274test243(lenna256bw, DITHER_BAYER44).save("out2-4-3.png")
275test243(gradient256bw, DITHER_BAYER44).save("grad2-4-3.png")
276
277##############################################################################
278print "Chapter 3. Error diffusion"
279
280# Output 3.0.1: naive error diffusion
281# Output 3.1.1: standard Floyd-Steinberg
282# Output 3.1.2: serpentine Floyd-Steinberg
283# FIXME: serpentine only works if rows == offset * 2 + 1
284# Output 3.2.1: Fan (modified Floyd-Steinberg)
285# Output 3.2.2: Jarvis, Judice and Ninke
286# Output 3-2-3: Stucki
287# Output 3-2-4: Burkes
288# Output 3-2-5: Sierra
289# Output 3.2.6: Two-line Sierra
290# Output 3.2.7: Sierra's Filter Lite
291# Output 3-2-8: Atkinson
292ERROR_NAIVE = \
293    [[ -1, 1]]
294ERROR_FSTEIN = \
295    [[    0.,    -1, 7./16],
296     [ 3./16, 5./16, 1./16]]
297ERROR_FAN = \
298    [[    0.,    0.,    -1, 7./16],
299     [ 1./16, 3./16, 5./16,    0.]]
300ERROR_JAJUNI = \
301    [[    0.,    0.,    -1, 7./48, 5./48],
302     [ 3./48, 5./48, 7./48, 5./48, 3./48],
303     [ 1./48, 3./48, 5./48, 3./48, 1./48]]
304ERROR_STUCKI = \
305    [[    0.,    0.,    -1, 8./42, 4./42],
306     [ 2./42, 4./42, 8./42, 4./42, 2./42],
307     [ 1./42, 2./42, 4./42, 2./42, 1./42]]
308ERROR_BURKES = \
309    [[    0.,    0.,    -1, 8./32, 4./32],
310     [ 2./32, 4./32, 8./32, 4./32, 2./32]]
311ERROR_SIERRA = \
312    [[    0.,    0.,    -1, 5./32, 3./32],
313     [ 2./32, 4./32, 5./32, 4./32, 2./32],
314     [    0., 2./32, 3./32, 2./32,    0.]]
315ERROR_SIERRA2 = \
316    [[    0.,    0.,    -1, 4./16, 3./16],
317     [ 1./16, 2./16, 3./16, 2./16, 1./16]]
318ERROR_FILTERLITE = \
319    [[   0.,   -1, 2./4],
320     [ 1./4, 1./4,   0.]]
321ERROR_ATKINSON = \
322    [[   0.,   -1, 1./8, 1./8],
323     [ 1./8, 1./8, 1./8,   0.],
324     [   0., 1./8,   0.,   0.]]
325## This is Stevenson-Arce in hex lattice
326#ERROR_STAR = \
327#    [[      0.,      0.,      0.,      -1,      0.,  32./200,      0.],
328#     [ 12./200,      0., 26./200,      0., 30./200,       0., 16./200],
329#     [      0., 12./200,      0., 26./200,      0.,  12./200,      0.],
330#     [  5./200,      0., 12./200,      0., 12./200,       0.,  5./200]]
331## This is an attempt at implementing Stevenson-Arce in square lattice
332#ERROR_STAR = \
333#    [[      0.,      0.,      -1, 32./200,      0.],
334#     [  6./200, 19./200, 28./200, 23./200,  8./200],
335#     [      0., 12./200, 26./200, 12./200,      0.],
336#     [  2./200,  9./200, 12./200,  9./200,  2./200]]
337
338def test3xx(src, mat, serpentine):
339    (w, h) = src.size()
340    dest = Image((w, h))
341    lines = len(mat)
342    rows = len(mat[0])
343    offset = mat[0].index(-1)
344    ey = [[0.] * (w + rows - 1) for x in range(lines)]
345    for y in range(h):
346        ex = [0.] * (rows - offset)
347        if serpentine and y & 1:
348            xrange = range(w - 1, -1, -1)
349        else:
350            xrange = range(w)
351        for x in xrange:
352            # Set pixel
353            c = src.getGray(x, y) + ex[0] + ey[0][x + offset]
354            d = c > 0.5
355            dest.setGray(x, y, d)
356            error = c - d
357            # Propagate first line of error
358            for dx in range(rows - offset - 2):
359                ex[dx] = ex[dx + 1] + error * mat[0][offset + 1 + dx]
360            ex[rows - offset - 2] = error * mat[0][rows - 1]
361            # Propagate next lines
362            if serpentine and y & 1:
363                for dy in range(1, lines):
364                    for dx in range(rows):
365                        ey[dy][x + dx] += error * mat[dy][rows - 1 - dx]
366            else:
367                for dy in range(1, lines):
368                    for dx in range(rows):
369                        ey[dy][x + dx] += error * mat[dy][dx]
370        for dy in range(lines - 1):
371            ey[dy] = ey[dy + 1]
372        ey[lines - 1] = [0.] * (w + rows - 1)
373    return dest
374
375test3xx(lenna256bw, ERROR_NAIVE, False).save("out3-0-1.png")
376test3xx(gradient256bw, ERROR_NAIVE, False).save("grad3-0-1.png")
377
378test3xx(lenna256bw, ERROR_FSTEIN, False).save("out3-1-1.png")
379test3xx(gradient256bw, ERROR_FSTEIN, False).save("grad3-1-1.png")
380test3xx(lenna256bw, ERROR_FSTEIN, True).save("out3-1-2.png")
381test3xx(gradient256bw, ERROR_FSTEIN, True).save("grad3-1-2.png")
382
383test3xx(lenna256bw, ERROR_FAN, False).save("out3-2-1.png")
384test3xx(gradient256bw, ERROR_FAN, False).save("grad3-2-1.png")
385
386test3xx(lenna256bw, ERROR_JAJUNI, False).save("out3-2-2.png")
387test3xx(gradient256bw, ERROR_JAJUNI, False).save("grad3-2-2.png")
388
389test3xx(lenna256bw, ERROR_STUCKI, False).save("out3-2-3.png")
390test3xx(gradient256bw, ERROR_STUCKI, False).save("grad3-2-3.png")
391
392test3xx(lenna256bw, ERROR_BURKES, False).save("out3-2-4.png")
393test3xx(gradient256bw, ERROR_BURKES, False).save("grad3-2-4.png")
394
395test3xx(lenna256bw, ERROR_SIERRA, False).save("out3-2-5.png")
396test3xx(gradient256bw, ERROR_SIERRA, False).save("grad3-2-5.png")
397
398test3xx(lenna256bw, ERROR_SIERRA2, False).save("out3-2-6.png")
399test3xx(gradient256bw, ERROR_SIERRA2, False).save("grad3-2-6.png")
400
401test3xx(lenna256bw, ERROR_FILTERLITE, False).save("out3-2-7.png")
402test3xx(gradient256bw, ERROR_FILTERLITE, False).save("grad3-2-7.png")
403
404test3xx(lenna256bw, ERROR_ATKINSON, False).save("out3-2-8.png")
405test3xx(gradient256bw, ERROR_ATKINSON, False).save("grad3-2-8.png")
406
407#test3xx(lenna256bw, ERROR_STAR, False).save("out3-2-9.png")
408#test3xx(gradient256bw, ERROR_STAR, False).save("grad3-2-9.png")
409
410#test3xx(lenna256bw, ERROR_STAR, False).save("out3-2-9.png")
411#test3xx(gradient256bw, ERROR_STAR, False).save("grad3-2-9.png")
412
413##############################################################################
414print "Chapter 4. Grayscale dithering"
415
416# Output 4.0.1: 4x4 Bayer dithering, 3 colours
417def test401(src, mat):
418    (w, h) = src.size()
419    dest = Image((w, h))
420    dx = len(mat[0])
421    dy = len(mat)
422    for y in range(h):
423        for x in range(w):
424            c = src.getGray(x, y)
425            threshold = (1. + mat[y % dy][x % dx]) / (dx * dy + 1)
426            if c < 0.5:
427                c = 0.5 * (c > threshold / 2)
428            else:
429                c = 0.5 + 0.5 * (c > 0.5 + threshold / 2)
430            dest.setGray(x, y, c)
431    return dest
432
433test401(lenna256bw, DITHER_BAYER44).save("out4-0-1.png")
434test401(gradient256bw, DITHER_BAYER44).save("grad4-0-1.png")
435
436# Output 4.0.2: standard Floyd-Steinberg, 3 colours
437def test402(src, mat, serpentine):
438    (w, h) = src.size()
439    dest = Image((w, h))
440    lines = len(mat)
441    rows = len(mat[0])
442    offset = mat[0].index(-1)
443    ey = [[0.] * (w + rows - 1) for x in range(lines)]
444    for y in range(h):
445        ex = [0.] * (rows - offset)
446        if serpentine and y & 1:
447            xrange = range(w - 1, -1, -1)
448        else:
449            xrange = range(w)
450        for x in xrange:
451            # Set pixel
452            c = src.getGray(x, y) + ex[0] + ey[0][x + offset]
453            d = 0.5 * (c > 0.25) + 0.5 * (c > 0.75)
454            dest.setGray(x, y, d)
455            error = c - d
456            # Propagate first line of error
457            for dx in range(rows - offset - 2):
458                ex[dx] = ex[dx + 1] + error * mat[0][offset + 1 + dx]
459            ex[rows - offset - 2] = error * mat[0][rows - 1]
460            # Propagate next lines
461            if serpentine and y & 1:
462                for dy in range(1, lines):
463                    for dx in range(rows):
464                        ey[dy][x + dx] += error * mat[dy][rows - 1 - dx]
465            else:
466                for dy in range(1, lines):
467                    for dx in range(rows):
468                        ey[dy][x + dx] += error * mat[dy][dx]
469        for dy in range(lines - 1):
470            ey[dy] = ey[dy + 1]
471        ey[lines - 1] = [0.] * (w + rows - 1)
472    return dest
473
474test402(lenna256bw, ERROR_FSTEIN, False).save("out4-0-2.png")
475test402(gradient256bw, ERROR_FSTEIN, False).save("grad4-0-2.png")
476
477# Pattern 4.1.1: gamma-corrected 50% gray, black-white halftone, 50% gray
478dest = Image((240, 80))
479for y in range(80):
480    for x in range(80):
481        dest.setGray(x, y, Gamma.ItoC(0.5))
482    for x in range(80, 160):
483        c = (x + y) & 1
484        dest.setGray(x, y, c)
485    for x in range(160, 240):
486        dest.setGray(x, y, 0.5)
487dest.save("pat4-1-1.png")
488
489# Output 4.2.1: gamma-corrected 2-colour Floyd-Steinberg
490# Output 4.2.2: gamma-corrected 3-colour Floyd-Steinberg
491# Output 4.2.3: gamma-corrected 4-colour Floyd-Steinberg
492def test42x(src, mat, threshold):
493    (w, h) = src.size()
494    dest = Image((w, h))
495    lines = len(mat)
496    rows = len(mat[0])
497    offset = mat[0].index(-1)
498    ey = [[0.] * (w + rows - 1) for x in range(lines)]
499    for y in range(h):
500        ex = [0.] * (rows - offset)
501        for x in range(w):
502            # Set pixel
503            c = Gamma.CtoI(src.getGray(x, y)) + ex[0] + ey[0][x + offset]
504            d = threshold(c)
505            dest.setGray(x, y, Gamma.ItoC(d))
506            error = c - d
507            # Propagate first line of error
508            for dx in range(rows - offset - 2):
509                ex[dx] = ex[dx + 1] + error * mat[0][offset + 1 + dx]
510            ex[rows - offset - 2] = error * mat[0][rows - 1]
511            # Propagate next lines
512            for dy in range(1, lines):
513                for dx in range(rows):
514                    ey[dy][x + dx] += error * mat[dy][dx]
515        for dy in range(lines - 1):
516            ey[dy] = ey[dy + 1]
517        ey[lines - 1] = [0.] * (w + rows - 1)
518    return dest
519
520def threshold_2(x):
521    if x < Gamma.CtoI(0.50):
522        return 0.
523    return 1.
524
525def threshold_3(x):
526    if x < Gamma.CtoI(0.25):
527        return 0.
528    elif x < Gamma.CtoI(0.75):
529        return Gamma.CtoI(0.5)
530    return 1.
531
532def threshold_4(x):
533    if x < Gamma.CtoI(0.17):
534        return 0.
535    elif x < Gamma.CtoI(0.50):
536        return Gamma.CtoI(0.3333)
537    elif x < Gamma.CtoI(0.83):
538        return Gamma.CtoI(0.6666)
539    return 1.
540
541test42x(lenna256bw, ERROR_FSTEIN, threshold_2).save("out4-2-1.png")
542test42x(gradient256bw, ERROR_FSTEIN, threshold_2).save("grad4-2-1.png")
543test42x(lenna256bw, ERROR_FSTEIN, threshold_3).save("out4-2-2.png")
544test42x(gradient256bw, ERROR_FSTEIN, threshold_3).save("grad4-2-2.png")
545test42x(lenna256bw, ERROR_FSTEIN, threshold_4).save("out4-2-3.png")
546test42x(gradient256bw, ERROR_FSTEIN, threshold_4).save("grad4-2-3.png")
547
548##############################################################################
549print "Chapter 5. Colour dithering"
550
551# Pattern 5.1.1: 8-colour palette
552dest = Image((512, 64))
553for x in range(512):
554    d = x / 64
555    r = (d & 2) >> 1
556    g = (d & 4) >> 2
557    b = d & 1
558    for y in range(64):
559        dest.setRgb(x, y, r, g, b)
560dest.save("pat5-1-1.png")
561
562# Load the 256x256 colour Lenna image
563lenna256 = Image("lenna256.png")
564
565# Output 5.1.1: 8-colour Floyd-Steinberg RGB dithering
566# Output 5.1.2: 8-colour gamma-corrected Floyd-Steinberg RGB dithering
567def test51x(src, mat, func):
568    (w, h) = src.size()
569    dest = Image((w, h))
570    tmp = [Image((w, h)), Image((w, h)), Image((w, h))]
571    for y in range(h):
572        for x in range(w):
573            rgb = src.getRgb(x, y)
574            for i in range(3):
575                tmp[i].setGray(x, y, rgb[i])
576    for i in range(3):
577        tmp[i] = func(tmp[i], mat, threshold_2)
578    for y in range(h):
579        for x in range(w):
580            (r, g, b) = [tmp[i].getGray(x, y) for i in range(3)]
581            dest.setRgb(x, y, r, g, b)
582    return dest
583
584test51x(lenna256, ERROR_FSTEIN, test3xx).save("out5-1-1.png")
585out512 = test51x(lenna256, ERROR_FSTEIN, test42x)
586out512.save("out5-1-2.png")
587
588# Pattern 5.2.1: different colours give the same result
589dest = Image((320, 160))
590for x in range(80):
591    for y in range(80):
592        r = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 7
593        g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13
594        b = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13
595        dest.setRgb(x, y, b, g, r)
596    for y in range(80, 160):
597        r = DITHER_BAYER44[y % 4][x % 4] > 7
598        g = DITHER_BAYER44[y % 4][x % 4] > 13
599        b = DITHER_BAYER44[y % 4][x % 4] > 13
600        dest.setRgb(x, y, b, g, r)
601for x in range(80, 160):
602    for y in range(80):
603        r = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 7
604        g = DITHER_BAYER44[(y / 8) % 4][(x / 8 + 1) % 4] > 13
605        b = DITHER_BAYER44[(y / 8) % 4][(x / 8 + 1) % 4] > 13
606        dest.setRgb(x, y, b, g, r)
607    for y in range(80, 160):
608        r = DITHER_BAYER44[y % 4][x % 4] > 7
609        g = DITHER_BAYER44[y % 4][(x + 1) % 4] > 13
610        b = DITHER_BAYER44[y % 4][(x + 1) % 4] > 13
611        dest.setRgb(x, y, b, g, r)
612for x in range(160, 240):
613    for y in range(80):
614        r = DITHER_BAYER44[(y / 8 + 1) % 4][(x / 8 + 1) % 4] > 7
615        g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13
616        b = DITHER_BAYER44[(y / 8 + 1) % 4][(x / 8) % 4] > 13
617        dest.setRgb(x, y, b, g, r)
618    for y in range(80, 160):
619        r = DITHER_BAYER44[(y + 1) % 4][(x + 1) % 4] > 7
620        g = DITHER_BAYER44[y % 4][x % 4] > 13
621        b = DITHER_BAYER44[(y + 1) % 4][x % 4] > 13
622        dest.setRgb(x, y, b, g, r)
623for x in range(240, 320):
624    for y in range(80):
625        r = DITHER_BAYER44[(y / 8 + 1) % 4][(x / 8) % 4] > 7
626        g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13
627        b = DITHER_BAYER44[(y / 8) % 4][(x / 8 + 2) % 4] > 13
628        dest.setRgb(x, y, b, g, r)
629    for y in range(80, 160):
630        r = DITHER_BAYER44[(y + 1) % 4][x % 4] > 7
631        g = DITHER_BAYER44[y % 4][x % 4] > 13
632        b = DITHER_BAYER44[y % 4][(x + 2) % 4] > 13
633        dest.setRgb(x, y, b, g, r)
634dest.save("pat5-2-1.png")
635
636# Output 5.2.1: cropped 5.1.2
637# Output 5.2.2: close-up of cropped 5.1.2
638def test52x(src, x, y, w, h, z):
639    dest = Image((w * z, h * z))
640    for j in range(h):
641        for i in range(w):
642            (r, g, b) = src.getRgb(x + i, y + j)
643            for v in range(z):
644                for u in range(z):
645                    dest.setRgb(i * z + u, j * z + v, r, g, b)
646    return dest
647
648test52x(out512, 20, 70, 32, 32, 1).save("out5-2-1.png")
649test52x(out512, 20, 70, 32, 32, 6).save("out5-2-2.png")
650
651out523 = test51x(lenna256, ERROR_STUCKI, test42x)
652out523.save("out5-2-3.png")
653test52x(out523, 20, 70, 32, 32, 1).save("out5-2-4.png")
654test52x(out523, 20, 70, 32, 32, 6).save("out5-2-5.png")
655
656##############################################################################
657print "Finished"
658
659# Place temporary cruft below this
660import sys; sys.exit(0)
661
Note: See TracBrowser for help on using the repository browser.