Changeset 1894 for www


Ignore:
Timestamp:
Nov 6, 2007, 12:09:15 AM (13 years ago)
Author:
Sam Hocevar
Message:
  • Updated study with new stuff about gamma.
  • Renamed and regenerated output files.
Location:
www/study
Files:
10 added
7 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • www/study/index.html

    r1892 r1894  
    3636
    3737<p style="text-align: center;">
    38   <img src="lenna256bw.png" width="256" height="256" alt="Lenna (256x256)" />
     38  <img src="lenna256.png" width="256" height="256"
     39       class="inline" alt="Lenna (256x256)" />
     40  <img src="lenna256bw.png" width="256" height="256"
     41       class="inline" alt="Lenna (256x256BW)" />
    3942</p>
    4043
    4144<p> This document makes a lot of assumptions, such as the fact that input
    42 images are made of pixels that have either one (gray) or three (red, green and
    43 blue) intensity values comprised between 0 and 1. Real life is even more
    44 complicated than that, but this is beyond the scope of this document for now.
    45 </p>
     45images are made of pixels that have either one (luminance) or three (red,
     46green and blue) values uniformly spread between 0 and 1 (with regards to
     47human contrast perception). Real life is more complicated than that, but
     48that is beyond the scope of this document for now. </p>
    4649
    4750<p> All the algorithms explained in this document can be found in
     
    5053run the script. </p>
    5154
    52 <h2> 1. Dithering grayscale to black and white </h2>
     55<h2> 1. Colour quantisation </h2>
    5356
    54 <p> Converting a grayscale image to black and white is a complex problem that
    55 can be done in many different ways and depends on many parameters: </p>
     57<p> Colour quantisation is a very old and common computer graphics problem.
     58Many methods exist to do the task, and their efficiency depends on several
     59parameters: </p>
    5660
    5761<ul>
     
    6468  <li> the quality requirements: for instance, can contrast be raised for
    6569       a more appealing result at the expense of accuracy?
    66   <li> the allowed computation time: do we need 50fps or is a 10-second
    67        computation acceptable? </li>
     70  <li> the allowed computation time: do we need 50fps or can we afford to
     71       wait 10 seconds for a better result? </li>
    6872</ul>
    6973
    70 <h3> 1.1. The naïve approach: thresholding </h3>
     74<h3> 1.1. Black and white thresholding </h3>
    7175
    7276<p> Since a grayscale pixel has a value between 0 and 1, a fast method
     
    7680
    7781<p style="text-align: center;">
    78   <img src="test1.png" width="256" height="256" alt="50% threshold" />
     82  <img src="out001.png" width="256" height="256"
     83       class="inline" alt="50% threshold" />
    7984</p>
    8085
     
    8590
    8691<p style="text-align: center;">
    87   <img src="test2.png" width="256" height="256" alt="40% threshold" />
    88   <img src="test3.png" width="256" height="256" alt="60% threshold" />
     92  <img src="out002.png" width="256" height="256"
     93       class="inline" alt="40% threshold" />
     94  <img src="out003.png" width="256" height="256"
     95       class="inline" alt="60% threshold" />
    8996</p>
    9097
    9198<p> Still not very good. Obviously something better is needed. </p>
    9299
    93 <h3> 1.2. Halftone patterns </h3>
     100<h3> 1.2. Grayscale thresholding </h3>
     101
     102<p> Better results can be achieved with a slightly bigger palette. Here is
     103thresholding applied to a 3-colour and to a 5-colour palette: </p>
     104
     105<p style="text-align: center;">
     106  <img src="out004.png" width="256" height="256"
     107       class="inline" alt="2-bit threshold" />
     108  <img src="out005.png" width="256" height="256"
     109       class="inline" alt="3-bit threshold" />
     110</p>
     111
     112<h2> 2. Halftoning patterns </h2>
     113
     114<h3> 2.1. Overview </h3>
    94115
    95116<p> Observe the following patterns. From a certain distance or assuming small
     
    98119
    99120<p style="text-align: center;">
    100   <img src="pattern50.png" width="512" height="128" alt="50% pattern" />
     121  <img src="pat001.png" width="320" height="80"
     122       class="inline" alt="50% pattern" />
    101123</p>
    102124
    103 <p> This looks promising. Let’s try immediately on Lenna! We shall leave
    104 pixels below 0.33 to black and pixels above 0.66 to white, but pixels
    105 inbetween will be painted with the finest pattern: </p>
     125<p> We can do even better using additional patterns such as these 25% and
     126the 75% halftone patterns: </p>
    106127
    107128<p style="text-align: center;">
    108   <img src="test4.png" width="256" height="256" alt="33/66% threshold and 50% halftone" />
     129  <img src="pat002.png" width="320" height="80"
     130       class="inline" alt="25% and 75% patterns" />
    109131</p>
    110132
    111 <p> Already better. But we can do even better using additional patterns such
    112 as the 25% and the 75% halftone patterns: </p>
     133<p> This looks promising. Let’s try immediately on Lenna! We will use the
     1345-colour thresholding picture and replace the 0.25, 0.5 and 0.75 gray values
     135with the above patterns: </p>
    113136
    114137<p style="text-align: center;">
    115   <img src="pattern25-75.png" width="512" height="128" alt="25% and 75% patterns" />
     138  <img src="out006.png" width="256" height="256"
     139       class="inline" alt="25%, 50% and 75% halftoning" />
    116140</p>
    117141
     142<h3> 2.2. Gamma considerations </h3>
     143
     144<p> If your display is not very good, you might see slightly different
     145shades of gray for the following patterns, despite being made of 50%
     146black and 50% white pixels: </p>
     147
     148<p style="text-align: center;">
     149  <img src="pat003.png" width="240" height="80"
     150       class="inline" alt="introducing gamma" />
     151</p>
     152
     153<p> But more importantly, if you are reading this document on a computer
     154screen, you may have noticed that the above 50% pattern was closer to a 0.7
     155grayscale (left) than to the expected 0.5 value (right). If you are reading
     156a printed copy, it might be a different matter. </p>
     157
     158<p style="text-align: center;">
     159  <img src="pat004.png" width="240" height="80"
     160       class="inline" alt="introducing gamma" />
     161</p>
     162
     163<!--
    118164<p> Here is the application to Lenna, using the 0-0.2, 0.2-0.4, 0.4-0.6,
    1191650.6-0.8 and 0.8-1 ranges for black, white and the three patterns: </p>
    120166
    121167<p style="text-align: center;">
    122   <img src="test5.png" width="256" height="256" alt="20/40/60/80% threshold and 25/50/75% halftones" />
     168  <img src="out005.png" width="256" height="256"
     169       class="inline" alt="20/40/60/80% threshold and 25/50/75% halftones" />
    123170</p>
     171-->
    124172
    125173<?php $rev = '$Id$';
  • www/study/study.py

    r1891 r1894  
    11#!/usr/bin/env python
    22
     3import math, gd
     4
    35# Tiny image class to make examples short and readable
    4 import gd
    56class Image(gd.image):
     7    def __init__(self, args):
     8        gd.gdMaxColors = 256 * 256 * 256
     9        gd.image.__init__(self, args)
    610    def getGray(self, x, y):
    7         c = self.getPixel((x, y))
    8         return self.colorComponents(c)[0] / 255.0
     11        p = self.getPixel((x, y))
     12        c = self.colorComponents(p)[0] / 255.0
     13        return c
    914    def setGray(self, x, y, t):
    1015        p = (int)(t * 255.999)
    11         self.colorAllocate((p, p, p))
    12         c = self.colorExact((p, p, p))
     16        c = self.colorResolve((p, p, p))
    1317        self.setPixel((x, y), c)
    1418
     
    2327        c = src.getGray(x, y) > 0.5
    2428        dest.setGray(x, y, c)
    25 dest.writePng("test1.png")
     29dest.writePng("out001.png")
     30del dest
    2631
    2732# Test 2: 40% threshold
     
    3136        c = src.getGray(x, y) > 0.4
    3237        dest.setGray(x, y, c)
    33 dest.writePng("test2.png")
     38dest.writePng("out002.png")
     39del dest
    3440
    3541# Test 3: 60% threshold
     
    3945        c = src.getGray(x, y) > 0.6
    4046        dest.setGray(x, y, c)
    41 dest.writePng("test3.png")
     47dest.writePng("out003.png")
     48del dest
    4249
    43 # Create a 50% halftone pattern with various block sizes
    44 dest = Image((512, 128))
    45 for x in range(512):
    46     d = 8 >> (x / 128)
    47     for y in range(128):
     50# Test 4: 3-colour threshold
     51dest = Image((w, h))
     52for y in range(h):
     53    for x in range(w):
     54        c = src.getGray(x, y)
     55        c = math.floor(c * 2.999) / 2
     56        dest.setGray(x, y, c)
     57dest.writePng("out004.png")
     58
     59# Test 5: 4-colour threshold
     60dest = Image((w, h))
     61for y in range(h):
     62    for x in range(w):
     63        c = src.getGray(x, y)
     64        c = math.floor(c * 3.999) / 3
     65        dest.setGray(x, y, c)
     66dest.writePng("out005.png")
     67
     68# Pattern 1: a 50% halftone pattern with various block sizes
     69dest = Image((320, 80))
     70for x in range(320):
     71    d = 8 >> (x / 80)
     72    for y in range(80):
    4873        c = (x / d + y / d) & 1
    4974        dest.setGray(x, y, c)
    50 dest.writePng("pattern50.png")
     75dest.writePng("pat001.png")
     76
     77# Pattern 2: 25% and 75% halftone patterns with various block sizes
     78dest = Image((320, 80))
     79for x in range(320):
     80    d = 8 >> (x / 80)
     81    for y in range(40):
     82        c = ((x / d + y / d) & 1) or (y / d & 1)
     83        dest.setGray(x, y, c)
     84    for y in range(40, 80):
     85        c = ((x / d + y / d) & 1) and (y / d & 1)
     86        dest.setGray(x, y, c)
     87dest.writePng("pat002.png")
     88
     89# Test 6: 20/40/60/80% threshold with 25/50/75% halftone patterns inbetween:
     90dest = Image((w, h))
     91for y in range(h):
     92    for x in range(w):
     93        c = src.getGray(x, y)
     94        if c < 0.2:
     95            c = 0.
     96        elif c < 0.4:
     97            c = ((x + y) & 1) and (y & 1)
     98        elif c < 0.6:
     99            c = (x + y) & 1
     100        elif c < 0.8:
     101            c = ((x + y) & 1) or (y & 1)
     102        else:
     103            c = 1.
     104        dest.setGray(x, y, c)
     105dest.writePng("out006.png")
     106
     107# Pattern 3: vertical, mixed and horizontal black-white halftones
     108dest = Image((240, 80))
     109for y in range(80):
     110    for x in range(80):
     111        c = x & 1
     112        dest.setGray(x, y, c)
     113    for x in range(80, 160):
     114        c = (x / d + y / d) & 1
     115        dest.setGray(x, y, c)
     116    for x in range(160, 240):
     117        c = y & 1
     118        dest.setGray(x, y, c)
     119dest.writePng("pat003.png")
     120
     121# Pattern 4: gamma-corrected 50% gray, black-white halftone, 50% gray
     122dest = Image((240, 80))
     123for y in range(80):
     124    for x in range(80):
     125        dest.setGray(x, y, math.pow(0.5, 1 / 2.2))
     126    for x in range(80, 160):
     127        c = (x + y) & 1
     128        dest.setGray(x, y, c)
     129    for x in range(160, 240):
     130        dest.setGray(x, y, 0.5)
     131dest.writePng("pat004.png")
     132
     133##############################################################################
     134# Only temporary cruft below this
     135
     136import sys
     137sys.exit(0)
    51138
    52139# Test 4: 33/66% threshold with 50% halftone pattern inbetween:
     
    64151dest.writePng("test4.png")
    65152
    66 # Create 25% and 75% halftone patterns with various block sizes
    67 dest = Image((512, 128))
    68 for x in range(512):
    69     d = 8 >> (x / 128)
     153# Create a dot-matrix pattern
     154mat = [[0, 2, 2, 3, 1],
     155       [2, 2, 0, 2, 3],
     156       [2, 0, 1, 1, 3],
     157       [3, 2, 1, 0, 3],
     158       [1, 3, 3, 3, 3]]
     159dest = Image((320, 64))
     160for x in range(320):
     161    d = x / 64
    70162    for y in range(64):
    71         c = ((x / d + y / d) & 1) or (y / d & 1)
     163        c = mat[y % 5][x % 5] >= d
    72164        dest.setGray(x, y, c)
    73     for y in range(64, 128):
    74         c = ((x / d + y / d) & 1) and (y / d & 1)
    75         dest.setGray(x, y, c)
    76 dest.writePng("pattern25-75.png")
    77 
    78 # Test 4: 20/40/60/80% threshold with 25/50/75% halftone patterns inbetween:
    79 dest = Image((w, h))
    80 for y in range(h):
    81     for x in range(w):
    82         c = src.getGray(x, y)
    83         if c < 0.2:
    84             c = 0.
    85         elif c < 0.4:
    86             c = ((x + y) & 1) and (y & 1)
    87         elif c < 0.6:
    88             c = (x + y) & 1
    89         elif c < 0.8:
    90             c = ((x + y) & 1) or (y & 1)
    91         else:
    92             c = 1.
    93         dest.setGray(x, y, c)
    94 dest.writePng("test5.png")
     165dest.writePng("pat003.png")
    95166
    96167
Note: See TracChangeset for help on using the changeset viewer.