Changeset 2007
- Timestamp:
- Nov 17, 2007, 12:36:04 PM (13 years ago)
- Location:
- www/study
- Files:
-
- 5 added
- 2 edited
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
www/study/part5.html
r2005 r2007 69 69 70 70 <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> 71 our 8-colour palette. The first one mixes black, blue and white pixels. The 72 second one mixes black, blue and yellow pixels. The third mixes black, blue, 73 cyan and red pixels. The last one mixes black, blue, green and red pixels. 74 All patterns visually blend to the same shade, but the last one is the 75 most appealing visually: </p> 75 76 76 77 <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" 78 79 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 83 than visual artifacts. Here is a close-up of the previous output’s top-left 84 corner. The slanted bright pixel lines that appear are typical Floyd-Steinberg 85 artifacts. They are rendered even worse by the fact that dithering is done on 86 three 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 96 dithering. 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" /> 79 110 </p> 80 111 -
www/study/study.py
r2005 r2007 11 11 else: 12 12 return gd.image.__new__(args) 13 def save(self, name): 14 print " * saving %s" % (name,) 15 self.writePng(name) 13 16 def getGray(self, x, y): 14 17 p = self.getPixel((x, y)) … … 30 33 self.setPixel((x, y), c) 31 34 35 ############################################################################## 36 print "Initialisation" 37 32 38 # Manipulate gamma values 33 39 class Gamma: … … 49 55 for y in range(256): 50 56 gradient256bw.setGray(x, 255 - y, y / 255.) 51 gradient256bw.writePng("gradient256bw.png") 57 gradient256bw.save("gradient256bw.png") 58 59 ############################################################################## 60 print "Chapter 1. Colour quantisation" 52 61 53 62 # Output 1.1.1: 50% threshold … … 63 72 return dest 64 73 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")74 test11x(lenna256bw, 0.5).save("out1-1-1.png") 75 test11x(lenna256bw, 0.4).save("out1-1-2.png") 76 test11x(lenna256bw, 0.6).save("out1-1-3.png") 77 test11x(gradient256bw, 0.5).save("grad1-1-1.png") 78 test11x(gradient256bw, 0.4).save("grad1-1-2.png") 79 test11x(gradient256bw, 0.6).save("grad1-1-3.png") 71 80 72 81 # Output 1.2.1: 3-colour threshold … … 84 93 return dest 85 94 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") 95 test12x(lenna256bw, 3).save("out1-2-1.png") 96 test12x(lenna256bw, 5).save("out1-2-2.png") 97 test12x(gradient256bw, 3).save("grad1-2-1.png") 98 test12x(gradient256bw, 5).save("grad1-2-2.png") 99 100 ############################################################################## 101 print "Chapter 2. Halftoning patterns" 90 102 91 103 # Pattern 2.1.1: a 50% halftone pattern with various block sizes … … 96 108 c = (x / d + y / d) & 1 97 109 dest.setGray(x, y, c) 98 dest. writePng("pat2-1-1.png")110 dest.save("pat2-1-1.png") 99 111 100 112 # Pattern 2.1.2: 25% and 75% halftone patterns with various block sizes … … 108 120 c = ((x / d + y / d) & 1) and (y / d & 1) 109 121 dest.setGray(x, y, c) 110 dest. writePng("pat2-1-2.png")122 dest.save("pat2-1-2.png") 111 123 112 124 # Output 2.1.1: 20/40/60/80% threshold with 25/50/75% patterns inbetween: … … 130 142 return dest 131 143 132 test211(lenna256bw). writePng("out2-1-1.png")133 test211(gradient256bw). writePng("grad2-1-1.png")144 test211(lenna256bw).save("out2-1-1.png") 145 test211(gradient256bw).save("grad2-1-1.png") 134 146 135 147 # Pattern 2.2.1: vertical, mixed and horizontal black-white halftones … … 145 157 c = y & 1 146 158 dest.setGray(x, y, c) 147 dest. writePng("pat2-2-1.png")159 dest.save("pat2-2-1.png") 148 160 149 161 # Pattern 2.2.2: two different 25% patterns … … 162 174 c = (x / 2 & 1) and ((y / 2 + x / 4) & 1) 163 175 dest.setGray(x, y, c) 164 dest. writePng("pat2-2-2.png")176 dest.save("pat2-2-2.png") 165 177 166 178 # Output 2.3.1: 4x4 Bayer dithering … … 195 207 return dest 196 208 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")209 test23x(lenna256bw, DITHER_BAYER44).save("out2-3-1.png") 210 test23x(gradient256bw, DITHER_BAYER44).save("grad2-3-1.png") 211 212 test23x(lenna256bw, DITHER_CLUSTER44).save("out2-3-2.png") 213 test23x(gradient256bw, DITHER_CLUSTER44).save("grad2-3-2.png") 214 215 test23x(lenna256bw, DITHER_LINE53).save("out2-3-3.png") 216 test23x(gradient256bw, DITHER_LINE53).save("grad2-3-3.png") 205 217 206 218 # Output 2.4.1: uniform random dithering … … 216 228 return dest 217 229 218 test241(lenna256bw). writePng("out2-4-1.png")219 test241(gradient256bw). writePng("grad2-4-1.png")230 test241(lenna256bw).save("out2-4-1.png") 231 test241(gradient256bw).save("grad2-4-1.png") 220 232 221 233 # Output 2.4.2: random dithering … … 231 243 return dest 232 244 233 test242(lenna256bw).writePng("out2-4-2.png") 234 test242(gradient256bw).writePng("grad2-4-2.png") 245 test242(lenna256bw).save("out2-4-2.png") 246 test242(gradient256bw).save("grad2-4-2.png") 247 248 ############################################################################## 249 print "Chapter 3. Error diffusion" 235 250 236 251 # Output 3.0.1: naive error diffusion … … 329 344 return dest 330 345 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") 346 test3xx(lenna256bw, ERROR_NAIVE, False).save("out3-0-1.png") 347 test3xx(gradient256bw, ERROR_NAIVE, False).save("grad3-0-1.png") 348 349 test3xx(lenna256bw, ERROR_FSTEIN, False).save("out3-1-1.png") 350 test3xx(gradient256bw, ERROR_FSTEIN, False).save("grad3-1-1.png") 351 test3xx(lenna256bw, ERROR_FSTEIN, True).save("out3-1-2.png") 352 test3xx(gradient256bw, ERROR_FSTEIN, True).save("grad3-1-2.png") 353 354 test3xx(lenna256bw, ERROR_FAN, False).save("out3-2-1.png") 355 test3xx(gradient256bw, ERROR_FAN, False).save("grad3-2-1.png") 356 357 test3xx(lenna256bw, ERROR_JAJUNI, False).save("out3-2-2.png") 358 test3xx(gradient256bw, ERROR_JAJUNI, False).save("grad3-2-2.png") 359 360 test3xx(lenna256bw, ERROR_STUCKI, False).save("out3-2-3.png") 361 test3xx(gradient256bw, ERROR_STUCKI, False).save("grad3-2-3.png") 362 363 test3xx(lenna256bw, ERROR_BURKES, False).save("out3-2-4.png") 364 test3xx(gradient256bw, ERROR_BURKES, False).save("grad3-2-4.png") 365 366 test3xx(lenna256bw, ERROR_SIERRA, False).save("out3-2-5.png") 367 test3xx(gradient256bw, ERROR_SIERRA, False).save("grad3-2-5.png") 368 369 test3xx(lenna256bw, ERROR_SIERRA2, False).save("out3-2-6.png") 370 test3xx(gradient256bw, ERROR_SIERRA2, False).save("grad3-2-6.png") 371 372 test3xx(lenna256bw, ERROR_FILTERLITE, False).save("out3-2-7.png") 373 test3xx(gradient256bw, ERROR_FILTERLITE, False).save("grad3-2-7.png") 374 375 test3xx(lenna256bw, ERROR_ATKINSON, False).save("out3-2-8.png") 376 test3xx(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 ############################################################################## 385 print "Chapter 4. Grayscale dithering" 368 386 369 387 # Output 4.0.1: 4x4 Bayer dithering, 3 colours … … 384 402 return dest 385 403 386 test401(lenna256bw, DITHER_BAYER44). writePng("out4-0-1.png")387 test401(gradient256bw, DITHER_BAYER44). writePng("grad4-0-1.png")404 test401(lenna256bw, DITHER_BAYER44).save("out4-0-1.png") 405 test401(gradient256bw, DITHER_BAYER44).save("grad4-0-1.png") 388 406 389 407 # Output 4.0.2: standard Floyd-Steinberg, 3 colours … … 425 443 return dest 426 444 427 test402(lenna256bw, ERROR_FSTEIN, False). writePng("out4-0-2.png")428 test402(gradient256bw, ERROR_FSTEIN, False). writePng("grad4-0-2.png")445 test402(lenna256bw, ERROR_FSTEIN, False).save("out4-0-2.png") 446 test402(gradient256bw, ERROR_FSTEIN, False).save("grad4-0-2.png") 429 447 430 448 # Pattern 4.1.1: gamma-corrected 50% gray, black-white halftone, 50% gray … … 438 456 for x in range(160, 240): 439 457 dest.setGray(x, y, 0.5) 440 dest. writePng("pat4-1-1.png")458 dest.save("pat4-1-1.png") 441 459 442 460 # Output 4.2.1: gamma-corrected 2-colour Floyd-Steinberg … … 492 510 return 1. 493 511 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") 512 test42x(lenna256bw, ERROR_FSTEIN, threshold_2).save("out4-2-1.png") 513 test42x(gradient256bw, ERROR_FSTEIN, threshold_2).save("grad4-2-1.png") 514 test42x(lenna256bw, ERROR_FSTEIN, threshold_3).save("out4-2-2.png") 515 test42x(gradient256bw, ERROR_FSTEIN, threshold_3).save("grad4-2-2.png") 516 test42x(lenna256bw, ERROR_FSTEIN, threshold_4).save("out4-2-3.png") 517 test42x(gradient256bw, ERROR_FSTEIN, threshold_4).save("grad4-2-3.png") 518 519 ############################################################################## 520 print "Chapter 5. Colour dithering" 500 521 501 522 # Pattern 5.1.1: 8-colour palette … … 508 529 for y in range(64): 509 530 dest.setRgb(x, y, r, g, b) 510 dest. writePng("pat5-1-1.png")531 dest.save("pat5-1-1.png") 511 532 512 533 # Load the 256x256 colour Lenna image … … 515 536 # Output 5.1.1: 8-colour Floyd-Steinberg RGB dithering 516 537 # Output 5.1.2: 8-colour gamma-corrected Floyd-Steinberg RGB dithering 517 def test5 01(src, mat, func):538 def test51x(src, mat, func): 518 539 (w, h) = src.size() 519 540 dest = Image((w, h)) … … 532 553 return dest 533 554 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 555 test51x(lenna256, ERROR_FSTEIN, test3xx).save("out5-1-1.png") 556 out512 = test51x(lenna256, ERROR_FSTEIN, test42x) 557 out512.save("out5-1-2.png") 558 559 # Pattern 5.2.1: different colours give the same result 538 560 dest = Image((320, 160)) 539 561 for x in range(80): … … 542 564 g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13 543 565 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) 545 567 for y in range(80, 160): 546 568 r = DITHER_BAYER44[y % 4][x % 4] > 7 547 569 g = DITHER_BAYER44[y % 4][x % 4] > 13 548 570 b = DITHER_BAYER44[y % 4][x % 4] > 13 549 dest.setRgb(x, y, r, b, g)571 dest.setRgb(x, y, b, g, r) 550 572 for x in range(80, 160): 551 573 for y in range(80): … … 553 575 g = DITHER_BAYER44[(y / 8) % 4][(x / 8 + 1) % 4] > 13 554 576 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) 556 578 for y in range(80, 160): 557 579 r = DITHER_BAYER44[y % 4][x % 4] > 7 558 580 g = DITHER_BAYER44[y % 4][(x + 1) % 4] > 13 559 581 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) 561 583 for x in range(160, 240): 562 584 for y in range(80): … … 564 586 g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13 565 587 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) 567 589 for y in range(80, 160): 568 590 r = DITHER_BAYER44[(y + 1) % 4][(x + 1) % 4] > 7 569 591 g = DITHER_BAYER44[y % 4][x % 4] > 13 570 592 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) 572 594 for x in range(240, 320): 573 595 for y in range(80): … … 575 597 g = DITHER_BAYER44[(y / 8) % 4][(x / 8) % 4] > 13 576 598 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) 578 600 for y in range(80, 160): 579 601 r = DITHER_BAYER44[(y + 1) % 4][x % 4] > 7 580 602 g = DITHER_BAYER44[y % 4][x % 4] > 13 581 603 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) 605 dest.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 609 def 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 619 test52x(out512, 20, 70, 32, 32, 1).save("out5-2-1.png") 620 test52x(out512, 20, 70, 32, 32, 6).save("out5-2-2.png") 621 622 out523 = test51x(lenna256, ERROR_STUCKI, test42x) 623 out523.save("out5-2-3.png") 624 test52x(out523, 20, 70, 32, 32, 1).save("out5-2-4.png") 625 test52x(out523, 20, 70, 32, 32, 6).save("out5-2-5.png") 584 626 585 627 ############################################################################## 628 print "Finished" 629 586 630 # Place temporary cruft below this 587 631 import sys; sys.exit(0)
Note: See TracChangeset
for help on using the changeset viewer.