Changeset 2007


Ignore:
Timestamp:
11/17/07 12:36:04 (6 years ago)
Author:
sam
Message:
  • Explain the problem with dimension separation.
Location:
www/study
Files:
5 added
2 edited
1 moved

Legend:

Unmodified
Added
Removed
  • www/study/part5.html

    r2005 r2007  
    6969 
    7070<p> The following patterns show four ways to dither the same colour using 
    71 our 8-colour palette. The first one mixes black, red and white pixels. The 
    72 second one mixes black, red and cyan pixels. The third mixes black, red, green 
    73 and magenta pixels. The last one mixes black, red, green and blue pixels. But 
    74 all patterns visually blend to the same shade: </p> 
     71our 8-colour palette. The first one mixes black, blue and white pixels. The 
     72second one mixes black, blue and yellow pixels. The third mixes black, blue, 
     73cyan and red pixels. The last one mixes black, blue, green and red pixels. 
     74All patterns visually blend to the same shade, but the last one is the 
     75most appealing visually: </p> 
    7576 
    7677<p style="text-align: center;"> 
    77   <img src="pat5-1-2.png" width="320" height="160" 
     78  <img src="pat5-2-1.png" width="320" height="160" 
    7879       class="inline" alt="3 ways to dither the same colour" /> 
     80</p> 
     81 
     82<p> It is therefore quite clear that the exact pixel values matter much less 
     83than visual artifacts. Here is a close-up of the previous output’s top-left 
     84corner. The slanted bright pixel lines that appear are typical Floyd-Steinberg 
     85artifacts. They are rendered even worse by the fact that dithering is done on 
     86three different dimensions that do not take the others into account: </p> 
     87 
     88<p style="text-align: center;"> 
     89  <img src="out5-2-1.png" width="32" height="32" 
     90       class="inlinetop" alt="Floyd-Steinberg, 8 colours, gamma-corrected, cropped" /> 
     91  <img src="out5-2-2.png" width="192" height="192" 
     92       class="inline" alt="Floyd-Steinberg, 8 colours, gamma-corrected, cropped, zoomed" /> 
     93</p> 
     94 
     95<p> Some algorithms perform a bit better in this area. This is Stucki 
     96dithering. The Floyd-Steinberg artifacts are much less visible: </p> 
     97 
     98<p style="text-align: center;"> 
     99  <img src="out5-2-3.png" width="256" height="256" 
     100       class="inline" alt="Stucki, 8 colours, gamma-corrected" /> 
     101</p> 
     102 
     103<p> And this is a close-up of the same area: </p> 
     104 
     105<p style="text-align: center;"> 
     106  <img src="out5-2-4.png" width="32" height="32" 
     107       class="inlinetop" alt="Stucki, 8 colours, gamma-corrected, cropped" /> 
     108  <img src="out5-2-5.png" width="192" height="192" 
     109       class="inline" alt="Stucki, 8 colours, gamma-corrected, cropped, zoomed" /> 
    79110</p> 
    80111 
  • www/study/study.py

    r2005 r2007  
    1111        else: 
    1212            return gd.image.__new__(args) 
     13    def save(self, name): 
     14        print " * saving %s" % (name,) 
     15        self.writePng(name) 
    1316    def getGray(self, x, y): 
    1417        p = self.getPixel((x, y)) 
     
    3033        self.setPixel((x, y), c) 
    3134 
     35############################################################################## 
     36print "Initialisation" 
     37 
    3238# Manipulate gamma values 
    3339class Gamma: 
     
    4955    for y in range(256): 
    5056        gradient256bw.setGray(x, 255 - y, y / 255.) 
    51 gradient256bw.writePng("gradient256bw.png") 
     57gradient256bw.save("gradient256bw.png") 
     58 
     59############################################################################## 
     60print "Chapter 1. Colour quantisation" 
    5261 
    5362# Output 1.1.1: 50% threshold 
     
    6372    return dest 
    6473 
    65 test11x(lenna256bw, 0.5).writePng("out1-1-1.png") 
    66 test11x(lenna256bw, 0.4).writePng("out1-1-2.png") 
    67 test11x(lenna256bw, 0.6).writePng("out1-1-3.png") 
    68 test11x(gradient256bw, 0.5).writePng("grad1-1-1.png") 
    69 test11x(gradient256bw, 0.4).writePng("grad1-1-2.png") 
    70 test11x(gradient256bw, 0.6).writePng("grad1-1-3.png") 
     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") 
    7180 
    7281# Output 1.2.1: 3-colour threshold 
     
    8493    return dest 
    8594 
    86 test12x(lenna256bw, 3).writePng("out1-2-1.png") 
    87 test12x(lenna256bw, 5).writePng("out1-2-2.png") 
    88 test12x(gradient256bw, 3).writePng("grad1-2-1.png") 
    89 test12x(gradient256bw, 5).writePng("grad1-2-2.png") 
     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" 
    90102 
    91103# Pattern 2.1.1: a 50% halftone pattern with various block sizes 
     
    96108        c = (x / d + y / d) & 1 
    97109        dest.setGray(x, y, c) 
    98 dest.writePng("pat2-1-1.png") 
     110dest.save("pat2-1-1.png") 
    99111 
    100112# Pattern 2.1.2: 25% and 75% halftone patterns with various block sizes 
     
    108120        c = ((x / d + y / d) & 1) and (y / d & 1) 
    109121        dest.setGray(x, y, c) 
    110 dest.writePng("pat2-1-2.png") 
     122dest.save("pat2-1-2.png") 
    111123 
    112124# Output 2.1.1: 20/40/60/80% threshold with 25/50/75% patterns inbetween: 
     
    130142    return dest 
    131143 
    132 test211(lenna256bw).writePng("out2-1-1.png") 
    133 test211(gradient256bw).writePng("grad2-1-1.png") 
     144test211(lenna256bw).save("out2-1-1.png") 
     145test211(gradient256bw).save("grad2-1-1.png") 
    134146 
    135147# Pattern 2.2.1: vertical, mixed and horizontal black-white halftones 
     
    145157        c = y & 1 
    146158        dest.setGray(x, y, c) 
    147 dest.writePng("pat2-2-1.png") 
     159dest.save("pat2-2-1.png") 
    148160 
    149161# Pattern 2.2.2: two different 25% patterns 
     
    162174        c = (x / 2 & 1) and ((y / 2 + x / 4) & 1) 
    163175        dest.setGray(x, y, c) 
    164 dest.writePng("pat2-2-2.png") 
     176dest.save("pat2-2-2.png") 
    165177 
    166178# Output 2.3.1: 4x4 Bayer dithering 
     
    195207    return dest 
    196208 
    197 test23x(lenna256bw, DITHER_BAYER44).writePng("out2-3-1.png") 
    198 test23x(gradient256bw, DITHER_BAYER44).writePng("grad2-3-1.png") 
    199  
    200 test23x(lenna256bw, DITHER_CLUSTER44).writePng("out2-3-2.png") 
    201 test23x(gradient256bw, DITHER_CLUSTER44).writePng("grad2-3-2.png") 
    202  
    203 test23x(lenna256bw, DITHER_LINE53).writePng("out2-3-3.png") 
    204 test23x(gradient256bw, DITHER_LINE53).writePng("grad2-3-3.png") 
     209test23x(lenna256bw, DITHER_BAYER44).save("out2-3-1.png") 
     210test23x(gradient256bw, DITHER_BAYER44).save("grad2-3-1.png") 
     211 
     212test23x(lenna256bw, DITHER_CLUSTER44).save("out2-3-2.png") 
     213test23x(gradient256bw, DITHER_CLUSTER44).save("grad2-3-2.png") 
     214 
     215test23x(lenna256bw, DITHER_LINE53).save("out2-3-3.png") 
     216test23x(gradient256bw, DITHER_LINE53).save("grad2-3-3.png") 
    205217 
    206218# Output 2.4.1: uniform random dithering 
     
    216228    return dest 
    217229 
    218 test241(lenna256bw).writePng("out2-4-1.png") 
    219 test241(gradient256bw).writePng("grad2-4-1.png") 
     230test241(lenna256bw).save("out2-4-1.png") 
     231test241(gradient256bw).save("grad2-4-1.png") 
    220232 
    221233# Output 2.4.2: random dithering 
     
    231243    return dest 
    232244 
    233 test242(lenna256bw).writePng("out2-4-2.png") 
    234 test242(gradient256bw).writePng("grad2-4-2.png") 
     245test242(lenna256bw).save("out2-4-2.png") 
     246test242(gradient256bw).save("grad2-4-2.png") 
     247 
     248############################################################################## 
     249print "Chapter 3. Error diffusion" 
    235250 
    236251# Output 3.0.1: naive error diffusion 
     
    329344    return dest 
    330345 
    331 test3xx(lenna256bw, ERROR_NAIVE, False).writePng("out3-0-1.png") 
    332 test3xx(gradient256bw, ERROR_NAIVE, False).writePng("grad3-0-1.png") 
    333  
    334 test3xx(lenna256bw, ERROR_FSTEIN, False).writePng("out3-1-1.png") 
    335 test3xx(gradient256bw, ERROR_FSTEIN, False).writePng("grad3-1-1.png") 
    336 test3xx(lenna256bw, ERROR_FSTEIN, True).writePng("out3-1-2.png") 
    337 test3xx(gradient256bw, ERROR_FSTEIN, True).writePng("grad3-1-2.png") 
    338  
    339 test3xx(lenna256bw, ERROR_FAN, False).writePng("out3-2-1.png") 
    340 test3xx(gradient256bw, ERROR_FAN, False).writePng("grad3-2-1.png") 
    341  
    342 test3xx(lenna256bw, ERROR_JAJUNI, False).writePng("out3-2-2.png") 
    343 test3xx(gradient256bw, ERROR_JAJUNI, False).writePng("grad3-2-2.png") 
    344  
    345 test3xx(lenna256bw, ERROR_STUCKI, False).writePng("out3-2-3.png") 
    346 test3xx(gradient256bw, ERROR_STUCKI, False).writePng("grad3-2-3.png") 
    347  
    348 test3xx(lenna256bw, ERROR_BURKES, False).writePng("out3-2-4.png") 
    349 test3xx(gradient256bw, ERROR_BURKES, False).writePng("grad3-2-4.png") 
    350  
    351 test3xx(lenna256bw, ERROR_SIERRA, False).writePng("out3-2-5.png") 
    352 test3xx(gradient256bw, ERROR_SIERRA, False).writePng("grad3-2-5.png") 
    353  
    354 test3xx(lenna256bw, ERROR_SIERRA2, False).writePng("out3-2-6.png") 
    355 test3xx(gradient256bw, ERROR_SIERRA2, False).writePng("grad3-2-6.png") 
    356  
    357 test3xx(lenna256bw, ERROR_FILTERLITE, False).writePng("out3-2-7.png") 
    358 test3xx(gradient256bw, ERROR_FILTERLITE, False).writePng("grad3-2-7.png") 
    359  
    360 test3xx(lenna256bw, ERROR_ATKINSON, False).writePng("out3-2-8.png") 
    361 test3xx(gradient256bw, ERROR_ATKINSON, False).writePng("grad3-2-8.png") 
    362  
    363 #test3xx(lenna256bw, ERROR_STAR, False).writePng("out3-2-9.png") 
    364 #test3xx(gradient256bw, ERROR_STAR, False).writePng("grad3-2-9.png") 
    365  
    366 #test3xx(lenna256bw, ERROR_STAR, False).writePng("out3-2-9.png") 
    367 #test3xx(gradient256bw, ERROR_STAR, False).writePng("grad3-2-9.png") 
     346test3xx(lenna256bw, ERROR_NAIVE, False).save("out3-0-1.png") 
     347test3xx(gradient256bw, ERROR_NAIVE, False).save("grad3-0-1.png") 
     348 
     349test3xx(lenna256bw, ERROR_FSTEIN, False).save("out3-1-1.png") 
     350test3xx(gradient256bw, ERROR_FSTEIN, False).save("grad3-1-1.png") 
     351test3xx(lenna256bw, ERROR_FSTEIN, True).save("out3-1-2.png") 
     352test3xx(gradient256bw, ERROR_FSTEIN, True).save("grad3-1-2.png") 
     353 
     354test3xx(lenna256bw, ERROR_FAN, False).save("out3-2-1.png") 
     355test3xx(gradient256bw, ERROR_FAN, False).save("grad3-2-1.png") 
     356 
     357test3xx(lenna256bw, ERROR_JAJUNI, False).save("out3-2-2.png") 
     358test3xx(gradient256bw, ERROR_JAJUNI, False).save("grad3-2-2.png") 
     359 
     360test3xx(lenna256bw, ERROR_STUCKI, False).save("out3-2-3.png") 
     361test3xx(gradient256bw, ERROR_STUCKI, False).save("grad3-2-3.png") 
     362 
     363test3xx(lenna256bw, ERROR_BURKES, False).save("out3-2-4.png") 
     364test3xx(gradient256bw, ERROR_BURKES, False).save("grad3-2-4.png") 
     365 
     366test3xx(lenna256bw, ERROR_SIERRA, False).save("out3-2-5.png") 
     367test3xx(gradient256bw, ERROR_SIERRA, False).save("grad3-2-5.png") 
     368 
     369test3xx(lenna256bw, ERROR_SIERRA2, False).save("out3-2-6.png") 
     370test3xx(gradient256bw, ERROR_SIERRA2, False).save("grad3-2-6.png") 
     371 
     372test3xx(lenna256bw, ERROR_FILTERLITE, False).save("out3-2-7.png") 
     373test3xx(gradient256bw, ERROR_FILTERLITE, False).save("grad3-2-7.png") 
     374 
     375test3xx(lenna256bw, ERROR_ATKINSON, False).save("out3-2-8.png") 
     376test3xx(gradient256bw, ERROR_ATKINSON, False).save("grad3-2-8.png") 
     377 
     378#test3xx(lenna256bw, ERROR_STAR, False).save("out3-2-9.png") 
     379#test3xx(gradient256bw, ERROR_STAR, False).save("grad3-2-9.png") 
     380 
     381#test3xx(lenna256bw, ERROR_STAR, False).save("out3-2-9.png") 
     382#test3xx(gradient256bw, ERROR_STAR, False).save("grad3-2-9.png") 
     383 
     384############################################################################## 
     385print "Chapter 4. Grayscale dithering" 
    368386 
    369387# Output 4.0.1: 4x4 Bayer dithering, 3 colours 
     
    384402    return dest 
    385403 
    386 test401(lenna256bw, DITHER_BAYER44).writePng("out4-0-1.png") 
    387 test401(gradient256bw, DITHER_BAYER44).writePng("grad4-0-1.png") 
     404test401(lenna256bw, DITHER_BAYER44).save("out4-0-1.png") 
     405test401(gradient256bw, DITHER_BAYER44).save("grad4-0-1.png") 
    388406 
    389407# Output 4.0.2: standard Floyd-Steinberg, 3 colours 
     
    425443    return dest 
    426444 
    427 test402(lenna256bw, ERROR_FSTEIN, False).writePng("out4-0-2.png") 
    428 test402(gradient256bw, ERROR_FSTEIN, False).writePng("grad4-0-2.png") 
     445test402(lenna256bw, ERROR_FSTEIN, False).save("out4-0-2.png") 
     446test402(gradient256bw, ERROR_FSTEIN, False).save("grad4-0-2.png") 
    429447 
    430448# Pattern 4.1.1: gamma-corrected 50% gray, black-white halftone, 50% gray 
     
    438456    for x in range(160, 240): 
    439457        dest.setGray(x, y, 0.5) 
    440 dest.writePng("pat4-1-1.png") 
     458dest.save("pat4-1-1.png") 
    441459 
    442460# Output 4.2.1: gamma-corrected 2-colour Floyd-Steinberg 
     
    492510    return 1. 
    493511 
    494 test42x(lenna256bw, ERROR_FSTEIN, threshold_2).writePng("out4-2-1.png") 
    495 test42x(gradient256bw, ERROR_FSTEIN, threshold_2).writePng("grad4-2-1.png") 
    496 test42x(lenna256bw, ERROR_FSTEIN, threshold_3).writePng("out4-2-2.png") 
    497 test42x(gradient256bw, ERROR_FSTEIN, threshold_3).writePng("grad4-2-2.png") 
    498 test42x(lenna256bw, ERROR_FSTEIN, threshold_4).writePng("out4-2-3.png") 
    499 test42x(gradient256bw, ERROR_FSTEIN, threshold_4).writePng("grad4-2-3.png") 
     512test42x(lenna256bw, ERROR_FSTEIN, threshold_2).save("out4-2-1.png") 
     513test42x(gradient256bw, ERROR_FSTEIN, threshold_2).save("grad4-2-1.png") 
     514test42x(lenna256bw, ERROR_FSTEIN, threshold_3).save("out4-2-2.png") 
     515test42x(gradient256bw, ERROR_FSTEIN, threshold_3).save("grad4-2-2.png") 
     516test42x(lenna256bw, ERROR_FSTEIN, threshold_4).save("out4-2-3.png") 
     517test42x(gradient256bw, ERROR_FSTEIN, threshold_4).save("grad4-2-3.png") 
     518 
     519############################################################################## 
     520print "Chapter 5. Colour dithering" 
    500521 
    501522# Pattern 5.1.1: 8-colour palette 
     
    508529    for y in range(64): 
    509530        dest.setRgb(x, y, r, g, b) 
    510 dest.writePng("pat5-1-1.png") 
     531dest.save("pat5-1-1.png") 
    511532 
    512533# Load the 256x256 colour Lenna image 
     
    515536# Output 5.1.1: 8-colour Floyd-Steinberg RGB dithering 
    516537# Output 5.1.2: 8-colour gamma-corrected Floyd-Steinberg RGB dithering 
    517 def test501(src, mat, func): 
     538def test51x(src, mat, func): 
    518539    (w, h) = src.size() 
    519540    dest = Image((w, h)) 
     
    532553    return dest 
    533554 
    534 test501(lenna256, ERROR_FSTEIN, test3xx).writePng("out5-1-1.png") 
    535 test501(lenna256, ERROR_FSTEIN, test42x).writePng("out5-1-2.png") 
    536  
    537 # Pattern 5.1.2: different colours give the same result 
     555test51x(lenna256, ERROR_FSTEIN, test3xx).save("out5-1-1.png") 
     556out512 = test51x(lenna256, ERROR_FSTEIN, test42x) 
     557out512.save("out5-1-2.png") 
     558 
     559# Pattern 5.2.1: different colours give the same result 
    538560dest = Image((320, 160)) 
    539561for x in range(80): 
     
    542564        g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13 
    543565        b = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13 
    544         dest.setRgb(x, y, r, b, g) 
     566        dest.setRgb(x, y, b, g, r) 
    545567    for y in range(80, 160): 
    546568        r = DITHER_BAYER44[y % 4][x % 4] > 7 
    547569        g = DITHER_BAYER44[y % 4][x % 4] > 13 
    548570        b = DITHER_BAYER44[y % 4][x % 4] > 13 
    549         dest.setRgb(x, y, r, b, g) 
     571        dest.setRgb(x, y, b, g, r) 
    550572for x in range(80, 160): 
    551573    for y in range(80): 
     
    553575        g = DITHER_BAYER44[(y / 8) % 4][(x / 8 + 1) % 4] > 13 
    554576        b = DITHER_BAYER44[(y / 8) % 4][(x / 8 + 1) % 4] > 13 
    555         dest.setRgb(x, y, r, b, g) 
     577        dest.setRgb(x, y, b, g, r) 
    556578    for y in range(80, 160): 
    557579        r = DITHER_BAYER44[y % 4][x % 4] > 7 
    558580        g = DITHER_BAYER44[y % 4][(x + 1) % 4] > 13 
    559581        b = DITHER_BAYER44[y % 4][(x + 1) % 4] > 13 
    560         dest.setRgb(x, y, r, b, g) 
     582        dest.setRgb(x, y, b, g, r) 
    561583for x in range(160, 240): 
    562584    for y in range(80): 
     
    564586        g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13 
    565587        b = DITHER_BAYER44[(y / 8 + 1) % 4][(x / 8) % 4] > 13 
    566         dest.setRgb(x, y, r, b, g) 
     588        dest.setRgb(x, y, b, g, r) 
    567589    for y in range(80, 160): 
    568590        r = DITHER_BAYER44[(y + 1) % 4][(x + 1) % 4] > 7 
    569591        g = DITHER_BAYER44[y % 4][x % 4] > 13 
    570592        b = DITHER_BAYER44[(y + 1) % 4][x % 4] > 13 
    571         dest.setRgb(x, y, r, b, g) 
     593        dest.setRgb(x, y, b, g, r) 
    572594for x in range(240, 320): 
    573595    for y in range(80): 
     
    575597        g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13 
    576598        b = DITHER_BAYER44[(y / 8) % 4][(x / 8 + 2) % 4] > 13 
    577         dest.setRgb(x, y, r, b, g) 
     599        dest.setRgb(x, y, b, g, r) 
    578600    for y in range(80, 160): 
    579601        r = DITHER_BAYER44[(y + 1) % 4][x % 4] > 7 
    580602        g = DITHER_BAYER44[y % 4][x % 4] > 13 
    581603        b = DITHER_BAYER44[y % 4][(x + 2) % 4] > 13 
    582         dest.setRgb(x, y, r, b, g) 
    583 dest.writePng("pat5-1-2.png") 
     604        dest.setRgb(x, y, b, g, r) 
     605dest.save("pat5-2-1.png") 
     606 
     607# Output 5.2.1: cropped 5.1.2 
     608# Output 5.2.2: close-up of cropped 5.1.2 
     609def test52x(src, x, y, w, h, z): 
     610    dest = Image((w * z, h * z)) 
     611    for j in range(h): 
     612        for i in range(w): 
     613            (r, g, b) = src.getRgb(x + i, y + j) 
     614            for v in range(z): 
     615                for u in range(z): 
     616                    dest.setRgb(i * z + u, j * z + v, r, g, b) 
     617    return dest 
     618 
     619test52x(out512, 20, 70, 32, 32, 1).save("out5-2-1.png") 
     620test52x(out512, 20, 70, 32, 32, 6).save("out5-2-2.png") 
     621 
     622out523 = test51x(lenna256, ERROR_STUCKI, test42x) 
     623out523.save("out5-2-3.png") 
     624test52x(out523, 20, 70, 32, 32, 1).save("out5-2-4.png") 
     625test52x(out523, 20, 70, 32, 32, 6).save("out5-2-5.png") 
    584626 
    585627############################################################################## 
     628print "Finished" 
     629 
    586630# Place temporary cruft below this 
    587631import sys; sys.exit(0) 
Note: See TracChangeset for help on using the changeset viewer.