Ordered dithering

This example demonstrates dithering using a Bayer matrix.  Dithering is most commonly used for displaying images on hardware with a low colour depth.  The algorithm modifies the input values using an index matrix before quantising them.

+----+----+----+----+----+----+----+----+
|  0 | 32 |  8 | 40 |  2 | 34 | 10 | 42 |
+----+----+----+----+----+----+----+----+
| 48 | 16 | 56 | 24 | 50 | 18 | 58 | 26 |
+----+----+----+----+----+----+----+----+
| 12 | 44 |  4 | 36 | 14 | 46 |  6 | 38 |
+----+----+----+----+----+----+----+----+
| 60 | 28 | 52 | 20 | 62 | 30 | 54 | 22 |
+----+----+----+----+----+----+----+----+
|  3 | 35 | 11 | 43 |  1 | 33 |  9 | 41 |
+----+----+----+----+----+----+----+----+
| 51 | 19 | 59 | 27 | 49 | 17 | 57 | 25 |
+----+----+----+----+----+----+----+----+
| 15 | 47 |  7 | 39 | 13 | 45 |  5 | 37 |
+----+----+----+----+----+----+----+----+
| 63 | 31 | 55 | 23 | 61 | 29 | 53 | 21 |
+----+----+----+----+----+----+----+----+

See also

#!/usr/bin/env ruby
require 'hornetseye'
include Hornetseye
class MultiArray
  class << self
    def ramp( w, h )
      idx = int( w, h ).indgen!
      retval = MultiArray.int 2, w, h
      retval.roll[ 0 ] = idx % w
      retval.roll[ 1 ] = idx / w
      retval
    end
    def bayer( lsize )
      n = 1 << lsize
      idx = MultiArray.int( n, n ).indgen!
      result = MultiArray.int( n, n ).fill!
      m = Sequence[ 0, 2, 3, 1 ].to_int
      for i in 0 ... lsize
        q = idx.bit( i ) | idx.bit( i + lsize ) << 1
        result |= q.map( m ) << ( ( lsize - i - 1 ) << 1 )
      end
      result
    end
  end
end
class Sequence_
  def bit( i )
    ( self & ( 1 << i ) ) >> i
  end
  def dither( lsize = 4 )
    if typecode < RGB_
      result = self.class.float.new
      result.r, result.g, result.b = [ r, g, b ].collect do |c|
        c.dither lsize
      end
      result
    else
      bayer = MultiArray.bayer lsize
      idx = MultiArray.ramp( *shape ) & ( ( 1 << lsize ) - 1 )
      msk = ( 1 << lsize ) ** 2 - 1
      ( self & msk <= bayer.warp( idx ) ).conditional self & ~msk, self | msk
    end
  end
end
raise "Syntax: bayer.rb <input image> <output image>" if ARGV.size != 2
img = MultiArray.load_ubytergb ARGV[ 0 ]
img.dither( 3 ).save_ubytergb ARGV[1]
Close