<?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 - 4. Model-based dithering" />
   <meta name="Keywords" content="libcaca, ASCII, ASCII ART, console, text mode, ncurses, slang, AAlib, dithering, thresholding" />
   <title>Libcaca study - 4. Model-based dithering</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" />
   <style type="text/css">
     .score td, .score th { padding: 2px 8px; }
     .score th { background-color: #ddf; text-align: center; }
     .score tr { background-color: white; padding: 10px; }
     td.hi { background-color: #dfd; }
     table.score { padding: 0px; border: 0px; background-color: black;
                   margin-left: auto; margin-right: auto; }
   </style>
</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="part3.html">Error diffusion &lt;&lt;&lt;</a>
</div>
<div style="float: right;">
   <a href="part5.html">&gt;&gt;&gt; Greyscale dithering</a>
</div>
<div style="text-align: center;">
   <a href="index.html">^^^ Index</a>
</div>

<h2> 4. Model-based dithering </h2>

<p> So far all dithering methods have relied on the assumption that carefully
positioned black and white pixels gave the illusion of greyscales, but we
have not stated exactly how the human eye reacted. Also, we do not have an
automated way to compare the results of two different algorithms: judging that
an algorithm is “better” has mostly been a subjective process so far. </p>

<p> In order to figure out what the human eye really sees, we need a <b>human
visual system</b> (HVS) model. Countless models exist, and we are only going to
focus on simple ones for now. </p>

<h3> 4.1. Gaussian human visual system model </h3>

<p> One of the simplest models is the <b>gaussian HVS model</b>. It states
that the human eye acts like a gaussian convolution filter, slightly blurring
pixel neighbourhoods and therefore simulating what the eye sees as distance
from the image increases. This model, a simplified <b>luminance spatial
frequency response formula</b>, can be expressed using the two-dimensional
<i>e<small><sup> -(x²+y²)/2σ²</sup></small></i> function. The <i>σ</i>
parameter sort of introduces the viewing distance into the model. </p>

<p> Here are the results of applying a gaussian HVS to an 8×8 Bayer dithered
image, simply by convoluting the image with a gaussian blur filter. This
process is also known as <b>inverse halftoning</b>. Different values of
<i>σ</i> simulate what the eye sees from different distances. On the left
<i>σ = 1</i>, on the right <i>σ = 2</i>: </p>

<p style="text-align: center;">
  <img src="out/lena4-1-1.png" width="256" height="256"
       class="inline" alt="8×8 Bayer dither, gaussian HVS, σ = 1" />
  <img src="out/grad4-1-1.png" width="32" height="256"
       class="inline" alt="8×8 Bayer dither, gaussian HVS, σ = 1 gradient" />
  <img src="out/lena4-1-2.png" width="256" height="256"
       class="inline" alt="8×8 Bayer dither, gaussian HVS, σ = 2" />
  <img src="out/grad4-1-2.png" width="32" height="256"
       class="inline" alt="8×8 Bayer dither, gaussian HVS, σ = 2 gradient" />
</p>

<h3> 4.2. Direct binary search </h3>

<p> We have already seen that standard error diffusion methods do not go back
to pixels that have been set. <b>Direct binary search</b> [4] (DBS) is an
iterative method that processes the image a fixed number of times, or until the
error can no longer be minimised: </p>

<ul>
  <li> Generate an initial dithered image </li>
  <li> Repeat until no changes can be applied:
    <ul>
      <li> Compute the global error between the original and the dithered
           images </li>
      <li> For each pixel in the dithered image:
        <ul>
          <li> Compute the effect on the error of toggling the value of the
               current pixel </li>
          <li> Compute the effect on the error of swapping the current pixel
               with one of its immediate neighbours </li>
          <li> If the error can be reduced, perform the corresponding
               action </li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

<p> The efficiency and quality of DBS depend on many implementation details,
starting with the HVS model it uses to compute the error. Also, the initial
image used as iteration zero will give poor results if pattern artifacts are
already present. The order in which pixels are processed is important, too.
Unfortunately, despite its very high-quality results, DBS is usually a very
slow algorithm. </p>

<p> Below is an example of the algorithm results. The HVS uses a 11×11
convolution kernel of the <i>e<small><sup> -sqrt(x²+y²)</sup></small></i>
function. The initial image is randomly thresholded, and pixels are processed
in raster order. Iterations 1, 2 and 5 are shown: </p>

<p style="text-align: center;">
  <img src="out/lena4-2-1.png" width="256" height="256"
       class="inline" alt="direct binary search, iteration 0" />
  <img src="out/grad4-2-1.png" width="32" height="256"
       class="inline" alt="direct binary search, iteration 0 gradient" />
  <img src="out/lena4-2-2.png" width="256" height="256"
       class="inline" alt="direct binary search, iteration 1" />
  <img src="out/grad4-2-2.png" width="32" height="256"
       class="inline" alt="direct binary search, iteration 1 gradient" />
</p>

<p style="text-align: center;">
  <img src="out/lena4-2-3.png" width="256" height="256"
       class="inline" alt="direct binary search, iteration 2" />
  <img src="out/grad4-2-3.png" width="32" height="256"
       class="inline" alt="direct binary search, iteration 2 gradient" />
  <img src="out/lena4-2-4.png" width="256" height="256"
       class="inline" alt="direct binary search, iteration 5" />
  <img src="out/grad4-2-4.png" width="32" height="256"
       class="inline" alt="direct binary search, iteration 5 gradient" />
</p>

<p> Other HVS models can be used, giving very high quality results. Below are
the results of DBS with the following HVS functions: </p>

<ul>
  <li> <i>e<small><sup> -(x²+y²)/2</sup></small></i> </li>
  <li> <i>e<small><sup> -(x²+y²)/4.5</sup></small></i> </li>
  <li> <i>e<small><sup> -(x²+y²)/8</sup></small></i> </li>
  <li> <i>2e<small><sup> -(x²+y²)/1.5</sup></small> + e<small><sup>
       -(x²+y²)/8</sup></small></i> </li>
</ul>

<p> The iteration shown is number 5. More iterations would have improved
the results even slightly more, but that would have been at the expense of
performance: </p>

<p style="text-align: center;">
  <img src="out/lena4-2-5.png" width="256" height="256"
       class="inline" alt="direct binary search, sigma = 1, iteration 5" />
  <img src="out/grad4-2-5.png" width="32" height="256"
       class="inline" alt="direct binary search, sigma = 1, iteration 5 gradient" />
  <img src="out/lena4-2-6.png" width="256" height="256"
       class="inline" alt="direct binary search, sigma = 1.5, iteration 5" />
  <img src="out/grad4-2-6.png" width="32" height="256"
       class="inline" alt="direct binary search, sigma = 1.5, iteration 5 gradient" />
</p>

<p style="text-align: center;">
  <img src="out/lena4-2-7.png" width="256" height="256"
       class="inline" alt="direct binary search, sigma = 2, iteration 5" />
  <img src="out/grad4-2-7.png" width="32" height="256"
       class="inline" alt="direct binary search, sigma = 2, iteration 5 gradient" />
  <img src="out/lena4-2-8.png" width="256" height="256"
       class="inline" alt="direct binary search, best HVS, iteration 5" />
  <img src="out/grad4-2-8.png" width="32" height="256"
       class="inline" alt="direct binary search, best HVS, iteration 5 gradient" />
</p>

<h3> 4.3 Comparing dithering algorithms </h3>

<p> Using an HVS model, it is possible to classify previously seen algorithms
simply by applying the model to both the source and the destination images,
and computing their per-pixel <b>mean squared error</b>. This gives a better
estimation of the dithering algorithm’s quality than simply computing a
local, pixel-bound error. </p>

<p> The following table shows that different HVS models give very different
error values depending on the value of <i>σ</i>. Also, in the case of
the DBS algorithm, a carefully crafted HVS function such as the last one,
which combines two different HVS models, gives low error values at every
<i>σ</i>, which means the dithered image is close to the original image at all
corresponding viewing distances: </p>

<table class="score" cellspacing="1">
  <tr><th>Chapter 1: Thresholding</th>
      <th>Error %<br />σ = 1</th><th>Error %<br />σ = 1.5</th><th>Error %<br />σ = 2</th></tr>
  <tr><td>0.5 thresholding</td>
      <td>8.60303</td><td>7.93554</td><td>7.42669</td></tr>
  <tr><td>0.4 thresholding</td>
      <td>9.78990</td><td>9.10994</td><td>8.61878</td></tr>
  <tr><td>0.6 thresholding</td>
      <td>10.77689</td><td>10.33373</td><td>10.00462</td></tr>
  <tr><td>dynamic thresholding</td>
      <td>10.31568</td><td>9.60749</td><td>9.09760</td></tr>
  <tr><td>uniform random dithering</td>
      <td class="hi">1.57895</td><td class="hi">0.69762</td><td class="hi">0.39713</td></tr>
  <tr><td>gaussian random dithering</td>
      <td>2.95590</td><td>2.34461</td><td>2.08792</td></tr>

  <tr><th>Chapter 2: Halftoning</th>
      <th>Error %<br />σ = 1</th><th>Error %<br />σ = 1.5</th><th>Error %<br />σ = 2</th></tr>
  <tr><td>25/50/75% patterns dithering</td>
      <td>0.56642</td><td>0.46490</td><td>0.41816</td></tr>
  <tr><td>2×2 Bayer dithering</td>
      <td>0.56694</td><td>0.46622</td><td>0.41956</td></tr>
  <tr><td>4×4 Bayer dithering</td>
      <td class="hi">0.20538</td><td>0.08060</td><td>0.05457</td></tr>
  <tr><td>8×8 Bayer dithering</td>
      <td class="hi">0.19691</td><td class="hi">0.06352</td><td class="hi">0.03354</td></tr>
  <tr><td>4×4 cluster dot dithering</td>
      <td>1.20026</td><td>0.14863</td><td>0.07100</td></tr>
  <tr><td>8×8 cluster dot dithering</td>
      <td>3.86236</td><td>0.85236</td><td>0.14443</td></tr>
  <tr><td>5×3 artistic line dithering</td>
      <td>2.75590</td><td>0.49420</td><td>0.11578</td></tr>
  <tr><td>perturbated 8×8 Bayer dithering</td>
      <td>0.40550</td><td>0.16971</td><td>0.09804</td></tr>
  <tr><td>random 3×3 dithering matrix selection</td>
      <td>0.60329</td><td>0.25055</td><td>0.16521</td></tr>
  <tr><td>“plus” pattern dithering</td>
      <td>0.43061</td><td>0.33540</td><td>0.29851</td></tr>
  <tr><td>“hex” pattern dithering</td>
      <td>0.26747</td><td>0.16119</td><td>0.13068</td></tr>
  <tr><td>“doubleplus” pattern dithering</td>
      <td>0.42852</td><td>0.12232</td><td>0.08904</td></tr>
  <tr><td>“hex2” pattern dithering</td>
      <td>0.35057</td><td>0.13046</td><td>0.09533</td></tr>
  <tr><td>4-wise supercell “plus” pattern dithering</td>
      <td class="hi">0.21903</td><td>0.07494</td><td>0.04423</td></tr>
  <tr><td>3-wise supercell “hex” pattern dithering</td>
      <td class="hi">0.21621</td><td>0.07356</td><td>0.04193</td></tr>
  <tr><td>4-wise supercell “doubleplus” pattern dithering</td>
      <td>0.40883</td><td>0.07051</td><td class="hi">0.03609</td></tr>
  <tr><td>6th order dragon curve dithering</td>
      <td class="hi">0.20503</td><td class="hi">0.06719</td><td class="hi">0.03539</td></tr>
  <tr><td>3-wise supercell “hex2” pattern dithering</td>
      <td>0.32643</td><td>0.07861</td><td>0.04061</td></tr>
  <tr><td>9-wise supercell “hex2” pattern dithering</td>
      <td>0.32336</td><td>0.07775</td><td>0.04087</td></tr>
  <tr><td>14×14 void-and-cluster dithering</td>
      <td class="hi">0.19726</td><td class="hi">0.06748</td><td class="hi">0.03663</td></tr>
  <tr><td>25×25 void-and-cluster dithering</td>
      <td class="hi">0.19248</td><td class="hi">0.06267</td><td class="hi">0.03325</td></tr>

  <tr><th>Chapter 3: Error diffusion</th>
      <th>Error %<br />σ = 1</th><th>Error %<br />σ = 1.5</th><th>Error %<br />σ = 2</th></tr>
  <tr><td>naïve error diffusion</td>
      <td>0.33352</td><td>0.06372</td><td>0.01961</td></tr>
  <tr><td>Floyd-Steinberg error diffusion</td>
      <td class="hi">0.09249</td><td class="hi">0.01847</td><td class="hi">0.00859</td></tr>
  <tr><td>serpentine Floyd-Steinberg error diffusion</td>
      <td class="hi">0.09891</td><td class="hi">0.01849</td><td class="hi">0.00749</td></tr>
  <tr><td>Jarvis, Judice and Ninke dithering</td>
      <td>0.27198</td><td>0.06257</td><td>0.03472</td></tr>
  <tr><td>Fan dithering</td>
      <td class="hi">0.10220</td><td class="hi">0.01728</td><td class="hi">0.00705</td></tr>
  <tr><td>4-cell Shiau-Fan dithering</td>
      <td class="hi">0.11618</td><td class="hi">0.01785</td><td class="hi">0.00660</td></tr>
  <tr><td>5-cell Shiau-Fan dithering</td>
      <td class="hi">0.11550</td><td class="hi">0.01950</td><td class="hi">0.00708</td></tr>
  <tr><td>Stucki dithering</td>
      <td>0.19765</td><td>0.05373</td><td>0.03012</td></tr>
  <tr><td>Burkes dithering</td>
      <td>0.15831</td><td>0.04014</td><td>0.02178</td></tr>
  <tr><td>Sierra dithering</td>
      <td>0.23498</td><td>0.05415</td><td>0.03011</td></tr>
  <tr><td>Sierra 2 dithering</td>
      <td>0.22249</td><td>0.05121</td><td>0.02812</td></tr>
  <tr><td>Filter Lite</td>
      <td class="hi">0.09740</td><td class="hi">0.01605</td><td class="hi">0.00638</td></tr>
  <tr><td>Atkinson dithering</td>
      <td>0.47209</td><td>0.31012</td><td>0.27339</td></tr>
  <tr><td>Riemersma dithering on a Hilbert curve</td>
      <td>0.28224</td><td>0.06529</td><td>0.02693</td></tr>
  <tr><td>Riemersma dithering on a Hilbert 2 curve</td>
      <td>0.26622</td><td>0.06003</td><td>0.02492</td></tr>
  <tr><td>spatial Hilbert dithering on a Hilbert curve</td>
      <td>0.27289</td><td>0.06124</td><td>0.02184</td></tr>
  <tr><td>spatial Hilbert dithering on a Hilbert 2 curve</td>
      <td>0.22821</td><td>0.05246</td><td>0.01911</td></tr>
  <tr><td>dot diffusion</td>
      <td>0.38301</td><td>0.08063</td><td>0.01918</td></tr>
  <tr><td>dot diffusion with sharpen = 0.9</td>
      <td>0.78546</td><td>0.34941</td><td>0.23479</td></tr>
  <tr><td>dot diffusion with sharpen = 0.9 and zeta = 0.2</td>
      <td>2.91287</td><td>2.19398</td><td>1.99858</td></tr>
  <tr><td>serpentine Floyd-Steinberg with sharpen = 0.9</td>
      <td>0.62615</td><td>0.30851</td><td>0.22111</td></tr>
  <tr><td>omni-directional error diffusion</td>
      <td>0.23860</td><td>0.06821</td><td>0.03498</td></tr>
  <tr><td>Ostromoukhov’s variable error diffusion</td>
      <td class="hi">0.11203</td><td class="hi">0.01772</td><td class="hi">0.00593</td></tr>
  <tr><td>nearest block matching with 2×2 line tiles</td>
      <td>1.92430</td><td>1.34822</td><td>1.13685</td></tr>
  <tr><td>block error diffusion with 2×2 line tiles</td>
      <td>0.86807</td><td>0.22411</td><td>0.08901</td></tr>
  <tr><td>block error diffusion with 3×3 artistic tiles</td>
      <td>1.78626</td><td>0.56957</td><td>0.23000</td></tr>
  <tr><td>block error diffusion with all 2×2 tiles</td>
      <td>0.56466</td><td>0.15910</td><td>0.06946</td></tr>
  <tr><td>block error diffusion with all 2×2 tiles and<br />weighted intra-block error distribution</td>
      <td>0.37738</td><td>0.10434</td><td>0.04643</td></tr>
  <tr><td>sub-block error diffusion with all 2×2 tiles</td>
      <td>0.14697</td><td>0.02605</td><td class="hi">0.00894</td></tr>
  <tr><td>sub-block error diffusion with 2×2 line tiles</td>
      <td>0.35759</td><td>0.06081</td><td>0.01855</td></tr>
  <tr><td>sub-block error diffusion with all 3×3 tiles</td>
      <td>0.32802</td><td>0.06516</td><td>0.02165</td></tr>
  <tr><td>sub-block error diffusion with 3×3 artistic tiles</td>
      <td>1.03963</td><td>0.21839</td><td>0.06678</td></tr>
  <tr><th>Chapter 4: Model-based dithering</th>
      <th>Error %<br />σ = 1</th><th>Error %<br />σ = 1.5</th><th>Error %<br />σ = 2</th></tr>
  <tr><td>DBS with HVS <i>e<small><sup> -sqrt(x²+y²)</sup></small></i>, iteration 1</td>
      <td>0.25810</td><td>0.06780</td><td>0.03489</td></tr>
  <tr><td>DBS with HVS <i>e<small><sup> -sqrt(x²+y²)</sup></small></i>, iteration 2</td>
      <td>0.13762</td><td>0.03234</td><td>0.01939</td></tr>
  <tr><td>DBS with HVS <i>e<small><sup> -sqrt(x²+y²)</sup></small></i>, iteration 5</td>
      <td class="hi">0.10452</td><td>0.02745</td><td>0.01918</td></tr>
  <tr><td>DBS with HVS <i>e<small><sup> -(x²+y²)/2</sup></small></i>, iteration 5</td>
      <td class="hi">0.10344</td><td>0.03860</td><td>0.02669</td></tr>
  <tr><td>DBS with HVS <i>e<small><sup> -(x²+y²)/4.5</sup></small></i>, iteration 5</td>
      <td>0.18034</td><td class="hi">0.00984</td><td class="hi">0.00219</td></tr>
  <tr><td>DBS with HVS <i>e<small><sup> -(x²+y²)/8</sup></small></i>, iteration 5</td>
      <td>0.37804</td><td>0.02866</td><td class="hi">0.00295</td></tr>
  <tr><td>DBS with HVS <i>2e<small><sup> -(x²+y²)/1.5</sup></small> + e<small><sup> -(x²+y²)/8</sup></small></i>, iteration 5</td>
      <td class="hi">0.09838</td><td class="hi">0.00884</td><td class="hi">0.00263</td></tr>
</table>

<!--
DBS, sqrt:
  1.55543 0.68523 0.39111
  0.25810 0.06780 0.03489
  0.13762 0.03234 0.01939
  0.10452 0.02745 0.01918
DBS, sigma = 1:
  1.55543 0.68523 0.39111
  0.25379 0.09531 0.05629
  0.13447 0.04785 0.03057
  0.10344 0.03860 0.02669
DBS, sigma = 1.5:
  1.55543 0.68523 0.39111
  0.34373 0.05932 0.02545
  0.22412 0.01906 0.00560
  0.18034 0.00984 0.00219
DBS, sigma = 2:
  1.55543 0.68523 0.39111
  0.54240 0.09412 0.03133
  0.41340 0.04324 0.00804
  0.37804 0.02866 0.00295
-->

<p></p>

<div style="float: left;">
   <a href="part3.html">Error diffusion &lt;&lt;&lt;</a>
</div>
<div style="float: right;">
   <a href="part5.html">&gt;&gt;&gt; Greyscale 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>