<?php header("Content-Type: text/html; charset=utf-8"); ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="GENERATOR" content="vim" /> <meta name="Author" content="sam@zoy.org (Sam Hocevar)" /> <meta name="Description" content="Libcaca study - 3. Error diffusion" /> <meta name="Keywords" content="libcaca, ASCII, ASCII ART, console, text mode, ncurses, slang, AAlib, dithering, thresholding" /> <title>Libcaca study - 3. Error diffusion</title> <link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" /> <link rel="stylesheet" type="text/css" href="/main.css" /> </head> <body> <?php include($_SERVER["DOCUMENT_ROOT"]."/header.inc"); ?> <p> <span style="color: #aa0000; font-weight: bold;">Warning</span>: this document is still work in progress. Feel free to send comments but do not consider it final material. </p> <div style="float: left;"> <a href="part2.html">Halftoning <<<</a> </div> <div style="float: right;"> <a href="part4.html">>>> Model-based dithering</a> </div> <div style="text-align: center;"> <a href="index.html">^^^ Index</a> </div> <h2> 3. Error diffusion </h2> <p> The idea behind error diffusion is to compute the error caused by thresholding a given pixel and propagate it to neighbour pixels, in order to compensate for the average intensity loss or gain. It is based upon the assumption that a slightly out-of-place pixel causes little visual harm. </p> <p> The error is computed by simply substracting the source value and the destination value. Destination value can be chosen by many means but does not impact the image a lot with most methods in comparison to the crucial choice of error distribution coefficients. </p> <p> This is the simplest error diffusion method. It thresholds the image to 0.5 and propagates 100% of the error to the next (right) pixel. It is quite impressive given its simplicity but causes important visual artifacts: </p> <p style="text-align: center;"> <img src="out/lena3-0-1.png" width="256" height="256" class="inline" alt="Simple error diffusion" /> <img src="out/grad3-0-1.png" width="32" height="256" class="inline" alt="Simple error diffusion gradient" /> </p> <h3> 3.1. Floyd-Steinberg and JaJuNi error diffusion </h3> <p> The most famous error diffusion method is the <b>Floyd-Steinberg</b> algorithm [5]. It propagates the error to more than one adjacent pixels using the following coefficients: </p> <p style="text-align: center;"> <img src="out/fig3-1-1.png" width="121" height="81" alt="Floyd-Steinberg" /> </p> <p> The result of this algorithm is rather impressive even compared to the best ordered dither results we could achieve: </p> <p style="text-align: center;"> <img src="out/lena3-1-1.png" width="256" height="256" class="inline" alt="Floyd-Steinberg error diffusion" /> <img src="out/grad3-1-1.png" width="32" height="256" class="inline" alt="Floyd-Steinberg error diffusion gradient" /> </p> <p> <b>Jarvis, Judice and Ninke dithering</b> [7] (sometimes nicknamed <b>JaJuNi</b>) was published almost at the same time as Floyd-Steinberg. It uses a much more complex error diffusion matrix: </p> <p style="text-align: center;"> <img src="out/fig3-1-3.png" width="201" height="121" class="matrix" alt="Jarvis, Judice and Ninke" /> <img src="out/lena3-1-3.png" width="256" height="256" class="inline" alt="Jarvis, Judice and Ninke error diffusion" /> <img src="out/grad3-1-3.png" width="32" height="256" class="inline" alt="Jarvis, Judice and Ninke error diffusion gradient" /> </p> <h3> 3.2. Floyd-Steinberg derivatives </h3> <p> Zhigang Fan came up with several Floyd-Steinberg derivatives. <b>Fan dithering</b> [8] just moves one coefficient around: </p> <p style="text-align: center;"> <img src="out/fig3-2-1.png" width="161" height="81" class="matrix" alt="Fan" /> <img src="out/lena3-2-1.png" width="256" height="256" class="inline" alt="Fan error diffusion" /> <img src="out/grad3-2-1.png" width="32" height="256" class="inline" alt="Fan error diffusion gradient" /> </p> <p> <b>Shiau-Fan dithering</b> use a family of matrices supposed to reduce the apparition of artifacts usually seen with Floyd-Steinberg: </p> <p style="text-align: center;"> <img src="out/fig3-2-1b.png" width="161" height="81" class="matrix" alt="Shiau-Fan" /> <img src="out/lena3-2-1b.png" width="256" height="256" class="inline" alt="Shiau-Fan error diffusion" /> <img src="out/grad3-2-1b.png" width="32" height="256" class="inline" alt="Shiau-Fan error diffusion gradient" /> </p> <p style="text-align: center;"> <img src="out/fig3-2-1c.png" width="201" height="81" class="matrix" alt="Shiau-Fan 2" /> <img src="out/lena3-2-1c.png" width="256" height="256" class="inline" alt="Shiau-Fan 2 error diffusion" /> <img src="out/grad3-2-1c.png" width="32" height="256" class="inline" alt="Shiau-Fan 2 error diffusion gradient" /> </p> <p> By the way, these matrices are covered by Shiau’s and Fan’s <a href="http://www.freepatentsonline.com/5353127.html">U.S. patent 5353127</a>. </p> <p> <b>Stucki dithering</b> [6] is a slight variation of Jarvis-Judice-Ninke dithering: </p> <p style="text-align: center;"> <img src="out/fig3-2-3.png" width="201" height="121" class="matrix" alt="Stucki" /> <img src="out/lena3-2-3.png" width="256" height="256" class="inline" alt="Stucki error diffusion" /> <img src="out/grad3-2-3.png" width="32" height="256" class="inline" alt="Stucki error diffusion gradient" /> </p> <p> <b>Burkes dithering</b> is yet another variation [10] which improves on Stucki dithering by removing a line and making the error coefficients fractions of powers of two: </p> <p style="text-align: center;"> <img src="out/fig3-2-4.png" width="201" height="81" class="matrix" alt="Burkes" /> <img src="out/lena3-2-4.png" width="256" height="256" class="inline" alt="Burkes error diffusion" /> <img src="out/grad3-2-4.png" width="32" height="256" class="inline" alt="Burkes error diffusion gradient" /> </p> <p> Frankie Sierra [11] came up with a few error diffusion matrices: <b>Sierra dithering</b> is a variation of Jarvis that is slightly faster because it propagates to fewer pixels, <b>Two-row Sierra</b> is a simplified version thereof, and <b>Filter Lite</b> is one of the simplest Floyd-Steinberg derivatives: </p> <p style="text-align: center;"> <img src="out/fig3-2-5.png" width="201" height="121" class="matrix" alt="Sierra" /> <img src="out/lena3-2-5.png" width="256" height="256" class="inline" alt="Sierra error diffusion" /> <img src="out/grad3-2-5.png" width="32" height="256" class="inline" alt="Sierra error diffusion gradient" /> </p> <p style="text-align: center;"> <img src="out/fig3-2-6.png" width="201" height="81" class="matrix" alt="Sierra" /> <img src="out/lena3-2-6.png" width="256" height="256" class="inline" alt="Sierra error diffusion" /> <img src="out/grad3-2-6.png" width="32" height="256" class="inline" alt="Sierra error diffusion gradient" /> </p> <p style="text-align: center;"> <img src="out/fig3-2-7.png" width="121" height="81" class="matrix" alt="Sierra" /> <img src="out/lena3-2-7.png" width="256" height="256" class="inline" alt="Sierra error diffusion" /> <img src="out/grad3-2-7.png" width="32" height="256" class="inline" alt="Sierra error diffusion gradient" /> </p> <p> <b>Atkinson dithering</b> [12] only propagates 75% of the error, leading to a loss of contrast around very dark and very light areas (also called <b>highlights and shadows</b>), but better contrast in the midtones. The original Macintosh software <i>HyperScan</i> used this dithering algorithm, still considered superior to other Floyd-Steinberg derivatives by many Mac zealots: </p> <p style="text-align: center;"> <img src="out/fig3-2-8.png" width="161" height="121" class="matrix" alt="Atkinson" /> <img src="out/lena3-2-8.png" width="256" height="256" class="inline" alt="Atkinson error diffusion" /> <img src="out/grad3-2-8.png" width="32" height="256" class="inline" alt="Atkinson error diffusion gradient" /> </p> <!-- XXX: Stevenson-Arce is for hexagonal cells! <p> <b>Stevenson-Arce dithering</b>: </p> <p style="text-align: center;"> <img src="fig3-2-9.png" width="280" height="160" class="matrix" alt="Stevenson-Arce" /> <img src="out/lena3-2-9.png" width="256" height="256" class="inline" alt="Stevenson-Arce error diffusion" /> <img src="out/grad3-2-9.png" width="32" height="256" class="inline" alt="Stevenson-Arce error diffusion gradient" /> </p> --> <h3> 3.3. Changing image parsing direction </h3> <p> While image parsing order does not matter with ordered dithering, it can actually be crucial with error diffusion. The reason is that once a pixel has been processed, standard error diffusion methods do not go back. </p> <p> The usual way to parse an image is one pixel after the other, following their order in memory. When reaching the end of a line, we automatically jump to the beginning of the next line. Error diffusion methods using this parsing order are called <b>raster error diffusion</b>: </p> <p style="text-align: center;"> <img src="fig3-3-1.png" width="260" height="110" class="matrix" alt="Regular parsing" /> </p> <p> Changing the parsing order can help prevent the apparition of artifacts in error diffusion algorithms. This is <b>serpentine parsing</b>, where every odd line is parsed in reverse order (right to left): </p> <p style="text-align: center;"> <img src="fig3-3-2.png" width="260" height="110" class="matrix" alt="Serpentine parsing" /> </p> <p> The major problem with Floyd-Steinberg is the <b>worm artifacts</b> it creates. Here is an example of an image made of grey 0.9 dithered with standard Floyd-Steinberg and with <b>serpentine Floyd-Steinberg</b> [13 pp.266—267]. Most of the worm artifacts have disappeared or were highly reduced: </p> <p style="text-align: center;"> <img src="out/lena3-3-1.png" width="256" height="256" class="inline" alt="Floyd-Steinberg on grey 90%" /> <img src="out/lena3-3-2.png" width="256" height="256" class="inline" alt="serpentine Floyd-Steinberg on grey 90%" /> </p> <p> And here are the results of serpentine Floyd-Steinberg on Lena. Only a very close look will show the differences with standard Floyd-Steinberg, but a few of the artifacts did disappear: </p> <p style="text-align: center;"> <img src="out/lena3-1-2.png" width="256" height="256" class="inline" alt="serpentine Floyd-Steinberg" /> <img src="out/grad3-1-2.png" width="32" height="256" class="inline" alt="serpentine Floyd-Steinberg gradient" /> </p> <p> <b>Riemersma dithering</b> [26] parses the image following a plane-filling <b>Hilbert curve</b> and only propagates the error of the last <i>q</i> pixels, weighting it with an exponential rule. The method is interesting and inventive, unfortunately the results are disappointing: structural artifacts are worse than with other error diffusion methods (shown here with <i>q = 16</i> and <i>r = 16</i>): </p> <p style="text-align: center;"> <img src="fig3-3-3.png" width="250" height="250" class="matrix" alt="Hilbert curve parsing" /> <img src="out/lena3-3-3.png" width="256" height="256" class="inline" alt="Riemersma dither on Hilbert curve" /> <img src="out/grad3-3-3.png" width="32" height="256" class="inline" alt="Riemersma dither on Hilbert curve gradient" /> </p> <p> A variation of Riemersma dithering uses a <b>Hilbert 2 curve</b>, giving slightly better results but still causing random artifacts here and there: </p> <p style="text-align: center;"> <img src="fig3-3-4.png" width="233" height="233" class="matrix" alt="Hilbert 2 curve parsing" /> <img src="out/lena3-3-4.png" width="256" height="256" class="inline" alt="Riemersma dither on Hilbert 2 curve" /> <img src="out/grad3-3-4.png" width="32" height="256" class="inline" alt="Riemersma dither on Hilbert 2 curve gradient" /> </p> <p> An inherent problem with plane-filling curves is that distances on the curve do not mean anything in image space. Riemersma dithering distributes error to pixels according to their distance on the curve rather than their distance in the image. </p> <p> We introduce <b>spatial Hilbert dithering</b> that addresses this issue by distributing the error according to spatial coordinates. We also get rid of the <i>r</i> parameter, choosing to distribute 100% of the error. </p> <p> This is spatial Hilbert dithering on a Hilbert curve and on a Hilbert 2 curve. The results show a clear improvement over the original Riemersma algorithm, with far less noise and smoother low-gradient areas: </p> <p style="text-align: center;"> <img src="out/lena3-3-5.png" width="256" height="256" class="inline" alt="spatial Hilbert dither on Hilbert curve" /> <img src="out/grad3-3-5.png" width="32" height="256" class="inline" alt="spatial Hilbert dither on Hilbert curve gradient" /> <img src="out/lena3-3-6.png" width="256" height="256" class="inline" alt="spatial Hilbert dither on Hilbert 2 curve" /> <img src="out/grad3-3-6.png" width="32" height="256" class="inline" alt="spatial Hilbert dither on Hilbert 2 curve gradient" /> </p> <p> <b>Dot diffusion</b> [14] is an error diffusion method by Donald E. Knuth that uses tileable matrices just like ordered dithering, except that the cell value order is taken into account for error propagation. Diagonal cells get half as much error as directly adjacent cells: </p> <p style="text-align: center;"> <img src="out/fig3-3-7b.png" width="121" height="121" class="matrix" alt="Dot diffusion" /> </p> <p> For instance, in the following example, cell 25’s error is propagated to cells 44, 36, 30, 34 and 49. Given the diagonal cells rule, cells 44, 30 and 49 each get 1/7 of the error and cells 36 and 34 each get 2/7 of the error. Similarly, cell 63 gets 100% of cell 61’s error. </p> <p style="text-align: center;"> <img src="fig3-3-7.png" width="240" height="240" class="matrix" alt="Dot diffusion matrix sample" /> <img src="out/lena3-3-7.png" width="256" height="256" class="inline" alt="Dot diffusion" /> <img src="out/grad3-3-7.png" width="32" height="256" class="inline" alt="Dot diffusion gradient" /> </p> <p> The initial result is not extraordinary. But Knuth suggests applying a sharpen filter to the original image before applying dot diffusion. He also introduces a <i>zeta</i> value to deal with the size of laser printer dots, pretty similar to what we’ll see later as <b>gamma correction</b>. The following two images had a sharpening value of 0.9 applied to them. The image on the right shows <i>zeta = 0.2</i>: </p> <p style="text-align: center;"> <img src="out/lena3-3-8.png" width="256" height="256" class="inline" alt="Dot diffusion sharpen 0.9" /> <img src="out/grad3-3-8.png" width="32" height="256" class="inline" alt="Dot diffusion sharpen 0.9 gradient" /> <img src="out/lena3-3-9.png" width="256" height="256" class="inline" alt="Dot diffusion sharpen 0.9 zeta 0.2" /> <img src="out/grad3-3-9.png" width="32" height="256" class="inline" alt="Dot diffusion sharpen 0.9 zeta 0.2 gradient" /> </p> <p> Do not get fooled by Knuth’s apparent good results. They specifically target dot printers and do not give terribly good results on a computer screen. Actually, a sharpening filter makes just any dithering method look better, even basic Floyd-Steinberg dithering (shown here with a sharpening value of 0.9, too): </p> <p style="text-align: center;"> <img src="out/lena3-3-10.png" width="256" height="256" class="inline" alt="FS with sharpening" /> <img src="out/grad3-3-10.png" width="32" height="256" class="inline" alt="FS with sharpening gradient" /> </p> <p> Dot diffusion was reinvented 14 years later by Arney, Anderson and Ganawan without even citing Knuth. They call their method <b>omni-directional error diffusion</b>. Instead of using a clustered dot matrix like Knuth recommends for dot diffusion, they use a dispersed dot matrix, which gives far better results on a computer display. This is a 16×12 portion of that matrix: </p> <p style="text-align: center;"> <img src="out/fig3-3-11b.png" width="320" height="240" class="matrix" alt="omni-directional ED matrix sample" /> </p> <p> The preferred implementation of omni-directional error diffusion uses a slightly different propagation matrix, where top and bottom neighbours get more error than the others: </p> <p style="text-align: center;"> <img src="out/fig3-3-11.png" width="121" height="121" class="matrix" alt="omni-directional ED" /> <img src="out/lena3-3-11.png" width="256" height="256" class="inline" alt="omni-directional ED" /> <img src="out/grad3-3-11.png" width="32" height="256" class="inline" alt="omni-directional ED gradient" /> </p> <h3> 3.4. Variable coefficients error diffusion </h3> <p> Small error diffusion matrices usually cause artifacts to appear because the error is not propagated in enough directions. At the same time, such matrices also reduce the sharpened aspect common in error diffusion techniques. </p> <p> Ostromoukhov suggests error diffusion values that vary according to the input value. The list of 256 discrete value triplets for <i>d1</i>, <i>d2</i> and <i>d3</i> he provides [1] give pretty good results with serpentine parsing: </p> <p style="text-align: center;"> <img src="out/fig3-4-1.png" width="121" height="81" class="matrix" alt="Ostromoukhov ED matrix" /> <img src="out/lena3-4-1.png" width="256" height="256" class="inline" alt="Ostromoukhov ED" /> <img src="out/grad3-4-1.png" width="32" height="256" class="inline" alt="Ostromoukhov ED gradient" /> </p> <h3> 3.5. Block error diffusion </h3> <p> Sometimes, due to physical restrictions of the target media, output is limited to some combinations of pixel blocks, such as the ones shown below: </p> <p style="text-align: center;"> <img src="fig3-5-1.png" width="613" height="80" class="matrix" alt="list of 2×2 pixel blocks" /> </p> <p> It is still possible to dither the image, by doing it 4 pixels at a time and simply choosing the block from the list that minimises the global error within the 2×2 block: </p> <p style="text-align: center;"> <img src="out/lena3-5-1.png" width="256" height="256" class="inline" alt="2×2 pixel block quantisation" /> <img src="out/grad3-5-1.png" width="32" height="256" class="inline" alt="2×2 pixel block quantisation gradient" /> </p> <p> Damera-Venkata and Evans introduce <b>block error diffusion</b> [23], which reuses traditional error diffusion methods such as Floyd-Steinberg but applies the same error value to all pixels of a given block. Only one error value is propagated, <i>a+b+c+d</i>, which is the global error within the block: </p> <p style="text-align: center; font-size: 2em;"> <img src="out/fig3-1-1.png" width="121" height="81" class="math" alt="Floyd-Steinberg" /> ⊗ <img src="out/fig3-5-2b.png" width="81" height="81" class="math" alt="2×2 balanced matrix" /> = <img src="out/fig3-5-2.png" width="241" height="161" class="math" alt="2×2-expanded Floyd-Steinberg" /> </p> <p> Here are the results using the previous pixel blocks: </p> <p style="text-align: center;"> <img src="out/lena3-5-2.png" width="256" height="256" class="inline" alt="2×2 block Floyd-Steinberg" /> <img src="out/grad3-5-2.png" width="32" height="256" class="inline" alt="2×2 block Floyd-Steinberg gradient" /> </p> <p> Carefully chosen blocks create constraints on the final picture that may be of artistic interest: </p> <p style="text-align: center;"> <img src="fig3-5-3.png" width="354" height="207" class="matrix" alt="artistic 3×3 blocks" /> <img src="out/lena3-5-3.png" width="256" height="256" class="inline" alt="3×3 block Floyd-Steinberg" /> <img src="out/grad3-5-3.png" width="32" height="256" class="inline" alt="3×3 block Floyd-Steinberg gradient" /> </p> <p> Using all possible pixel blocks is not equivalent to dithering the image pixel by pixel. This is due to both the block-choosing method, which only minimises the difference of mean values within blocks intead of the sum of local distances, and to the inefficient matrix coefficients, which propagate the error beyond immediate neighbours, causing the image to look sharpened. </p> <p> This example shows standard block Floyd-Steinberg using all possible 2×2 blocks: </p> <p style="text-align: center;"> <img src="fig3-5-4.png" width="200" height="200" class="matrix" alt="all possible 2×2 blocks" /> <img src="out/lena3-5-4.png" width="256" height="256" class="inline" alt="full 2×2 block Floyd-Steinberg" /> <img src="out/grad3-5-4.png" width="32" height="256" class="inline" alt="full 2×2 block Floyd-Steinberg gradient" /> </p> <p> The results on the vertical gradient indicate poor block-choosing. In order to improve it, we introduce a modified, weighted intra-block error distribution matrix, still based on the original Floyd-Steinberg matrix: </p> <p style="text-align: center; font-size: 2em;"> <img src="out/fig3-1-1.png" width="121" height="81" class="math" alt="Floyd-Steinberg" /> ⊗ <img src="out/fig3-5-5b.png" width="81" height="81" class="math" alt="weighted 2×2 matrix" /> = <img src="out/fig3-5-5.png" width="241" height="161" class="math" alt="weighted 2×2 propagation matrix" /> </p> <p> The result still looks sharpened, but shows considerably less noise: </p> <p style="text-align: center;"> <img src="out/lena3-5-5.png" width="256" height="256" class="inline" alt="weighted full 2×2 block Floyd-Steinberg" /> <img src="out/grad3-5-5.png" width="32" height="256" class="inline" alt="weighted full 2×2 block Floyd-Steinberg gradient" /> </p> <h3> 3.6. Sub-block error diffusion </h3> <p> We introduce <b>sub-block error diffusion</b>, a novel technique improving on block error diffusion. It addresses the following observations: </p> <ul> <li> it is not a requirement to propagate the error beyond the immediate neighbours; since it causes a sharpen effect, we decide not to do it. </li> <li> the individual subpixels’ error should be propagated, not the global block error. </li> <li> subpixel <b>a</b>’s error is harder to compensate than subpixel <b>d</b>’s because its immediate neighbours are already in the block being processed, so we weight the sub-block matching in order to prioritise pixel <b>a</b>’s matching. </li> </ul> <p> We use <i>m⋅n</i> error diffusion matrices, one for each of the current block’s pixels. Here are four error diffusion matrices for 2×2 blocks, generated from the standard Floyd-Steinberg matrix: </p> <p style="text-align: center;"> <img src="out/fig3-6-1a.png" width="161" height="121" class="math" alt="sub-block 0,0 Floyd-Steinberg" /> <img src="out/fig3-6-1b.png" width="161" height="121" class="math" alt="sub-block 1,0 Floyd-Steinberg" /> </p> <p style="text-align: center;"> <img src="out/fig3-6-1c.png" width="161" height="121" class="math" alt="sub-block 0,1 Floyd-Steinberg" /> <img src="out/fig3-6-1d.png" width="161" height="121" class="math" alt="sub-block 1,1 Floyd-Steinberg" /> </p> <p> The results are far better than with the original block error diffusion method. On the left, sub-block error diffusion with all possible 2×2 blocks. On the right, sub-block error diffusion restricted to the tiles seen in 3.5: </p> <p style="text-align: center;"> <img src="out/lena3-6-1.png" width="256" height="256" class="inline" alt="full 2×2 sub-block Floyd-Steinberg" /> <img src="out/grad3-6-1.png" width="32" height="256" class="inline" alt="full 2×2 sub-block Floyd-Steinberg gradient" /> <img src="out/lena3-6-2.png" width="256" height="256" class="inline" alt="2×2 lines sub-block Floyd-Steinberg" /> <img src="out/grad3-6-2.png" width="32" height="256" class="inline" alt="2×2 lines sub-block Floyd-Steinberg gradient" /> </p> <p> Similar error diffusion matrices can be generated for 3×3 blocks: </p> <p style="text-align: center;"> <img src="out/fig3-6-3a.png" width="150" height="120" class="math" alt="sub-block 0,0/3×3 Floyd-Steinberg" /> <img src="out/fig3-6-3b.png" width="150" height="120" class="math" alt="sub-block 1,0/3×3 Floyd-Steinberg" /> <img src="out/fig3-6-3c.png" width="150" height="120" class="math" alt="sub-block 2,0/3×3 Floyd-Steinberg" /> </p> <p style="text-align: center;"> <img src="out/fig3-6-3d.png" width="150" height="120" class="math" alt="sub-block 0,1/3×3 Floyd-Steinberg" /> <img src="out/fig3-6-3e.png" width="150" height="120" class="math" alt="sub-block 1,1/3×3 Floyd-Steinberg" /> <img src="out/fig3-6-3f.png" width="150" height="120" class="math" alt="sub-block 2,1/3×3 Floyd-Steinberg" /> </p> <p style="text-align: center;"> <img src="out/fig3-6-3g.png" width="150" height="120" class="math" alt="sub-block 0,2/3×3 Floyd-Steinberg" /> <img src="out/fig3-6-3h.png" width="150" height="120" class="math" alt="sub-block 1,2/3×3 Floyd-Steinberg" /> <img src="out/fig3-6-3i.png" width="150" height="120" class="math" alt="sub-block 2,2/3×3 Floyd-Steinberg" /> </p> <p> Here are the results with all the possible 3×3 blocks, and with the artistic 3×3 blocks seen in 3.5: </p> <p style="text-align: center;"> <img src="out/lena3-6-3.png" width="256" height="256" class="inline" alt="3×3 sub-block Floyd-Steinberg" /> <img src="out/grad3-6-3.png" width="32" height="256" class="inline" alt="3×3 sub-block Floyd-Steinberg gradient" /> <img src="out/lena3-6-4.png" width="256" height="256" class="inline" alt="3×3 artistic sub-block Floyd-Steinberg" /> <img src="out/grad3-6-4.png" width="32" height="256" class="inline" alt="3×3 artistic sub-block Floyd-Steinberg gradient" /> </p> <div style="float: left;"> <a href="part2.html">Halftoning <<<</a> </div> <div style="float: right;"> <a href="part4.html">>>> Model-based dithering</a> </div> <div style="text-align: center;"> <a href="index.html">^^^ Index</a> </div> <?php $rev = '$Id$'; include($_SERVER['DOCUMENT_ROOT'].'/footer.inc'); ?> </body> </html>