Edit page History

Dithering

    Name

    Dithering

    Software

    ImageJ

    Author

    Gabriel Landini

    Maintainer

    Gabriel Landini (G.Landini at bham. ac. uk)

    File Dithering\_.txt
    Source

    See below

    Status

    active

    Purpose

    This macro takes an 8 bit greyscale or a 24 bit RGB image and performs dithering using one of the methods supported. Dithering is a type of half tone thresholding where greyscale (or RGB channel) intensity is converted into a local density of binary pixels. This is ideal for rendering images in devices with a binary output such as printers (greyscale) or with a small number of colours (colour dithering).

    In the case of 24 bit RGB images the macro lets you either apply dithering to the greyscale version of the image or to each of the RGB channels (colour dithering).

    Installation

    Copy the source code below and save it into a file called “Dithering_.txt” somewhere in the /Plugins folder. Restarting Fiji or ImageJ will show a new command called “Dithering”.

    Available methods

    The following methods have been implemented (there are several more): Floyd-Steinberg, Atkinson, Jarvis-Judice-Ninke, Stucki, Bayer_2x2, Bayer_4x4, Bayer_8x8, Clustered_4x4 and Random.
    Scaling dithered images by certain factors introduce numerous artifacts, so it is essential to apply dithering after image resizing.
    Dithering results are best rendered when the image is displayed or printed using small pixels.

    The following images have been magnified by a factor of 2 to show the individual pixels.

    Macro code

    // Dithering
    // G. Landini at bham. ac. uk
    // 17/ May 2009
    
    Dialog.create("Dithering");
    items=newArray("Floyd-Steinberg", "Atkinson", "Jarvis-Judice-Ninke","Stucki",
            "Bayer_2x2", "Bayer_4x4", "Bayer_8x8", "Clustered_4x4", "Random");
    doColour = false;
    
    Dialog.addChoice("Method", items);
    if (bitDepth==24) Dialog.addCheckbox("Colour dithering", false);
    Dialog.show() ;
    method=Dialog.getChoice();
    if (bitDepth==24)
       doColour=Dialog.getCheckbox();
    
    setBatchMode(true);
    w=getWidth();
    h=getHeight();
    run("Duplicate...", "title="+method);
    if (bitDepth == 24 && doColour) {
       run("RGB Stack");
       for (i=1; i<4; i++){
         showProgress(i/4);
         setSlice(i);
         dither(method);
       } 
       run("RGB Color");
    }
    else{
      if (bitDepth>8 && !doColour) run("8-bit");
      dither(method);
    }
    setBatchMode(false);
    
    function dither(method) {
     if (method=="Floyd-Steinberg"){
      w1=7/16;
      w2=3/16;
      w3=5/16;
      w4=1/16;
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          oldpixel = getPixel(x,y);
          if (oldpixel<128) newpixel=0; else newpixel=255;
          setPixel(x,y, newpixel);
          quant_error = oldpixel - newpixel;
          setPixel(x+1,y, getPixel(x+1,y) + w1 * quant_error);
          setPixel(x-1,y+1, getPixel(x-1,y+1) + w2 * quant_error);
          setPixel(x,y+1, getPixel(x,y+1) + w3 * quant_error);
          setPixel(x+1,y+1, getPixel(x+1,y+1) + w4 * quant_error);
        }
      }
     }
     else if (method=="Atkinson"){
      w1=1/8;
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          oldpixel = getPixel(x,y);
          if (oldpixel<128) newpixel = 0; else newpixel=255;
          setPixel(x,y, newpixel);
          quant_error = oldpixel - newpixel;
          setPixel(x+1,y, getPixel(x+1,y) + w1 * quant_error);
          setPixel(x+2,y, getPixel(x+2,y) + w1 * quant_error);
          setPixel(x-1,y+1, getPixel(x-1,y+1) + w1 * quant_error);
          setPixel(x,y+1, getPixel(x,y+1) + w1 * quant_error);
          setPixel(x+1,y+1, getPixel(x+1,y+1) + w1 * quant_error);
          setPixel(x,y+2, getPixel(x,y+2) + w1 * quant_error);
        }
      }
     }
     else if (method=="Jarvis-Judice-Ninke"){
      w7=7/48;
      w5=5/48;
      w3=3/48;
      w1=1/48;
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          oldpixel = getPixel(x,y);
          if (oldpixel<128) newpixel = 0; else newpixel=255;
          setPixel(x,y, newpixel);
          quant_error = oldpixel - newpixel;
          setPixel(x+1,y, getPixel(x+1,y) + w7 * quant_error);
          setPixel(x+2,y, getPixel(x+2,y) + w5 * quant_error);
          setPixel(x-2,y+1, getPixel(x-2,y+1) + w3 * quant_error);
          setPixel(x-1,y+1, getPixel(x-1,y+1) + w5 * quant_error);
          setPixel(x,y+1, getPixel(x,y+1) + w7 * quant_error);
          setPixel(x+1,y+1, getPixel(x+1,y+1) + w5 * quant_error);
          setPixel(x+2,y+1, getPixel(x+2,y+1) + w3 * quant_error);
          setPixel(x-2,y+2, getPixel(x-2,y+2) + w1 * quant_error);
          setPixel(x-1,y+2, getPixel(x-1,y+2) + w3 * quant_error);
          setPixel(x,y+2, getPixel(x,y+2) + w5 * quant_error);
          setPixel(x+1,y+2, getPixel(x+1,y+2) + w3 * quant_error);
          setPixel(x+2,y+2, getPixel(x+2,y+2) + w1 * quant_error);
        }
      }
     }
     else if (method=="Stucki"){
      w8= 8/42;
      w7=7/42;
      w5=5/42;
      w4= 4/42;
      w2=2/42;
      w1=1/42;
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          oldpixel = getPixel(x,y);
          if (oldpixel<128) newpixel = 0; else newpixel=255;
          setPixel(x,y, newpixel);
          quant_error = oldpixel - newpixel;
          setPixel(x+1,y, getPixel(x+1,y) + w7 * quant_error);
          setPixel(x+2,y, getPixel(x+2,y) + w5 * quant_error);
          setPixel(x-2,y+1, getPixel(x-2,y+1) + w2 * quant_error);
          setPixel(x-1,y+1, getPixel(x-1,y+1) + w4 * quant_error);
          setPixel(x,y+1, getPixel(x,y+1) + w8 * quant_error);
          setPixel(x+1,y+1, getPixel(x+1,y+1) + w4 * quant_error);
          setPixel(x+2,y+1, getPixel(x+2,y+1) + w2 * quant_error);
          setPixel(x-2,y+2, getPixel(x-2,y+2) + w1 * quant_error);
          setPixel(x-1,y+2, getPixel(x-1,y+2) + w2 * quant_error);
          setPixel(x,y+2, getPixel(x,y+2) + w4 * quant_error);
          setPixel(x+1,y+2, getPixel(x+1,y+2) + w2 * quant_error);
          setPixel(x+2,y+2, getPixel(x+2,y+2) + w1 * quant_error);
        }
      }
     }
     else if (method=="Bayer_2x2"){
      d= newArray(2,3,4,1);
      for (i=0; i<d.length; i++)
        d[i]=d[i] * 64 - 1;
    
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          if (getPixel(x,y)>=d[(y%2*2+x%2)])
              newpixel = 255; 
          else 
              newpixel=0;
          setPixel(x,y, newpixel);
        }
      }
     }
     else if (method=="Bayer_4x4"){
      d= newArray(0.1250, 1.0000, 0.1875, 0.8125, 0.6250, 0.3750, 0.6875,
         0.4375,0.2500, 0.8750, 0.0625, 0.9375, 0.7500, 0.5000, 0.5625, 0.3125);
      for (i=0; i<d.length; i++)
        d[i]*=255;
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          if (getPixel(x,y)>=d[(y%4*4+x%4)])
              newpixel = 255; 
          else 
              newpixel = 0;
          setPixel(x,y, newpixel);
        }
      }
     }
     else if (method=="Bayer_8x8"){
      d= newArray(1, 33,  9, 41, 3,  35, 11, 43, 49, 17, 57, 25, 51, 19, 59, 27, 13, 45, 5, 
        37, 15, 47, 7, 39, 61, 29, 53, 21, 63, 31, 55, 23, 4, 36, 12, 44, 2, 34, 10, 42, 52,
        20, 60, 28, 50, 18, 58, 26, 16, 48, 8, 40, 14, 46, 6, 38, 64, 32, 56, 24, 62, 30,
        54, 22);
    
      for (i=0; i<d.length; i++)
        d[i]=d[i] * 4 - 1;
    
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          if (getPixel(x,y)>=d[(y%8*8+x%8)])
              newpixel = 255; 
          else 
              newpixel = 0;
          setPixel(x,y, newpixel);
    
        }
      }
     }
     else if (method=="Clustered_4x4"){
      d= newArray(0.7500, 0.3750, 0.6250, 0.2500, 0.0625, 1.0000, 0.8750, 0.4375,
        0.5000, 0.8125, 0.9375, 0.1250, 0.1875, 0.5625, 0.3125, 0.6875);
      for (i=0; i<d.length; i++)
        d[i]*=255;
    
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          if (getPixel(x,y)>=d[(y%4*4+x%4)])
              newpixel = 255; 
          else 
              newpixel = 0;
          setPixel(x,y, newpixel);
        }
      }
     }
    else if (method=="Random"){
      w1=1/8;
      for (y=0; y<h; y++){
        for (x=0; x<w; x++){
          if(getPixel(x,y)> random*255)
            setPixel(x,y, 255);
          else   
            setPixel(x,y, 0);
        }
      }
     }
    }