Blue noise dithering

              This is translated from  an  earlier  article  of  mine
         written  in  Chinese.  This  article  talked about the void-
         cluster method used to generate a dither matrix.


         1.  Introduction

              Unlike Floyd or  Atkinson  dithering,  the  blue  noise
         dithering  is  essentially  ordered dithering with a special
         matrix, which can be expressed as:

           |dither←{(⊢>(⍺⌷⍨(⍴⍺∘⊣)|⊢)¨∘⍳∘⍴)⍵}            [1]
           |dither←{
           |    x y←⍴⍵
           |    a b←⍴⍺
         5 |    ⍵>(,⍺)[(x y⍴y⍴⍳b)+⍉y x⍴x⍴b×⍳a]
           |}

          [1] Notice ⎕io←0.


              This essentially means the left threshold matrix  argu‐
         ment  is  spanned  over  the right matrix that represents an
         image, and values lower than the threshold is discarded.

             (2 2⍴2 1 3 0)⊂⍤{(⊢>(⍺⌷⍨(⍴⍺∘⊣)|⊢)¨∘⍳∘⍴)⍵}⊃⎕←⊂?5 5⍴4

         ┌─────────┐
         │0 2 1 3 1│
         │2 2 1 2 0│
         │2 0 0 0 3│
         │3 3 1 2 2│
         │3 2 0 2 1│
         └─────────┘
         ┌─────────┐
         │0 1 0 1 0│
         │0 1 0 1 0│
         │0 0 0 0 1│
         │0 1 0 1 0│
         │1 1 0 1 0│
         └─────────┘


              Then   we   need   a   spherical    Gaussian    filter,
         e^(−sq(r)/2sq(σ)) that σ=1.5 which gives ideal result.


              Then it is so in APL:

           |gauss←{*-4.5÷⍨(⌽,1∘↓⍤1)(⊖⍪1∘↓)⍵ ⍵⍴(+/2*⍨⊢)¨⍸⍵ ⍵⍴1}


              The essential void-cluster method is  convolution  over
         torus space. Which can then be described so with code:

           |cluster←{
           |    s←a+~2|a←⍴⍵
           |    g←gauss⊃⌈s÷2 ⋄ (¯1 1×a)↓(1 ¯1×a)↓({+/,g×⍵}⌺s)(⍪⍨⍪⊢)(,⍨,⊢)⍵
           |}


         2.  Putting up all together

              The two functions, mkbp creates bit-pattern, mkda  then
         creates dither array.

            |∇r←mkbp hm;m;l;v
            | m←2×hm
            | r←?m m⍴2
            |loop:
          5 | l←imax cluster r
            | (l⌷r)←0
            | v←imax void r
            | (v⌷r)←0
            | →(l≡v)/0
         10 |∇
            |
            |∇r←mkda hm;bp;pt;m;all;rank;loc
            | m←2×hm
            | r←m m⍴0
         15 | bp←mkbp hm
            | pt←bp
            | rank←¯1++/,bp
            | :While rank≥0
            |     loc←imax cluster pt
         20 |     ⍞←'.'
            |     (loc⌷pt)←0
            |     (loc⌷r)←rank
            |     rank-←1
            | :EndWhile
         25 | pt←bp
            | rank←+/,bp
            | all←m*2
            | :While rank<all
            |     loc←imax void pt
         30 |     ⍞←'*'
            |     (loc⌷pt)←1
            |     (loc⌷r)←rank
            |     rank+←1
            | :EndWhile
         35 |∇


         3.  Bonus

              A Common Lisp prototype I've  written  before  the  APL
         one:

            |(defun make-bp (hm)
            |  (let* ((m (* 2 hm))
            |        (bp (make-noise m))
            |        last
          5 |        void)
            |    (loop
            |       (setf last (find-location 1 bp))
            |       (setf (apply #'aref bp last) 0)
            |       (setf void (find-location 0 bp))
         10 |       (setf (apply #'aref bp void) 1)
            |       (if (equal void last)
            |           (return bp)))))
            |
            |(defun make-da (bp m hm)
         15 |  (declare (optimize (speed 3)))
            |  (let ((da (make-array (list m m) :element-type 'fixnum))
            |        (hf (* hm m))
            |        (all (* m m)))
            |    (let ((pattern (alexandria:copy-array bp))
         20 |          (rank (1- (ones bp m))))
            |      (loop while (>= rank 0)
            |            do (let ((loc (cluster pattern)))
            |                 (setf (apply #'aref pattern loc) 0)
            |                 (setf (apply #'aref da loc) rank)
         25 |                 (decf rank))))
            |    (let ((pattern (alexandria:copy-array bp))
            |          (rank (ones bp m)))
            |      (loop while (< rank hf)
            |            do (let ((loc (void pattern)))
         30 |                 (setf (apply #'aref pattern loc) 1)
            |                 (setf (apply #'aref da loc) rank)
            |                 (incf rank)))
            |      (loop while (< rank all)
            |            do (let ((loc (cluster pattern)))
         35 |                 (setf (apply #'aref pattern loc) 1)
            |                 (setf (apply #'aref da loc) rank)
            |                 (incf rank))))
            |    da))