Author Topic: Floyd–Steinberg dithering  (Read 3034 times)

0 Members and 1 Guest are viewing this topic.

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
Floyd–Steinberg dithering
« on: July 16, 2018, 04:30:08 am »
implementation of https://en.wikipedia.org/wiki/Floyd%E2%80%93Steinberg_dithering

Code: QB64: [Select]
  1. deflng a-z
  2.  
  3. img1 = _loadimage("img/zmago.jpg", 32)
  4.  
  5. w = _width(img1)
  6. h = _height(img1)
  7.  
  8. img2 = _newimage(w, h, 32)
  9. img3 = _newimage(w, h, 32)
  10. img4 = _newimage(w, h, 32)
  11.  
  12. screen _newimage(w*2, h*2, 32)
  13.  
  14. dither_bw img1, img2, 0.1
  15.  
  16. dither img1, img3, 2
  17. dither img1, img4, 4
  18.  
  19. _putimage (0, 0), img1
  20. _putimage (w, 0), img2
  21. _putimage (0, h), img3
  22. _putimage (w, h), img4
  23.  
  24.  
  25. 'colour dither
  26. 'source image, destination image, number of colours per channel
  27. sub dither(img1, img2, num)
  28.         w = _width(img1)
  29.         h = _height(img1)
  30.  
  31.         _dest img2
  32.         _source img2
  33.  
  34.         _putimage , img1
  35.  
  36.         for y=0 to h-1
  37.         for x=0 to w-1
  38.  
  39.                 z = point(x, y)
  40.  
  41.                 r = (_red(z)*num\255)*255\num
  42.                 g = (_green(z)*num\255)*255\num
  43.                 b = (_blue(z)*num\255)*255\num
  44.  
  45.                 pset (x, y), _rgb(r, g, b)
  46.  
  47.                 qr = _red(z) - r
  48.                 qg = _green(z) - g
  49.                 qb = _blue(z) - b
  50.  
  51.                 r = _red(point(x + 1, y)) +   qr*7/16
  52.                 g = _green(point(x + 1, y)) + qg*7/16
  53.                 b = _blue(point(x + 1, y)) +  qb*7/16
  54.                 pset (x + 1, y), _rgb(r, g, b)
  55.  
  56.                 r = _red(point(x - 1, y + 1)) +   qr*3/16
  57.                 g = _green(point(x - 1, y + 1)) + qg*3/16
  58.                 b = _blue(point(x - 1, y + 1)) +  qb*3/16
  59.                 pset (x - 1, y + 1), _rgb(r, g, b)
  60.  
  61.                 r = _red(point(x, y + 1)) +   qr*5/16
  62.                 g = _green(point(x, y + 1)) + qg*5/16
  63.                 b = _blue(point(x, y + 1)) +  qb*5/16
  64.                 pset (x, y + 1), _rgb(r, g, b)
  65.  
  66.                 r = _red(point(x + 1, y + 1)) +   qr*1/16
  67.                 g = _green(point(x + 1, y + 1)) + qg*1/16
  68.                 b = _blue(point(x + 1, y + 1)) +  qb*1/16
  69.                 pset (x + 1, y + 1), _rgb(r, g, b)
  70.         next
  71.         next
  72.  
  73. 'black and white dither
  74. 'source image, destination image, bw threshold percent
  75. sub dither_bw(img1, img2, t as double)
  76.         w = _width(img1)
  77.         h = _height(img1)
  78.  
  79.         _dest img2
  80.         _source img2
  81.  
  82.         _putimage , img1
  83.  
  84.         for y=0 to h-1
  85.         for x=0 to w-1
  86.  
  87.                 z = point(x, y)
  88.  
  89.                 c = -((_red(z)+_green(z)+_blue(z))/3 > 255*t)*255
  90.  
  91.                 pset (x, y), _rgb(c, c, c)
  92.  
  93.                 qr = _red(z) - c
  94.                 qg = _green(z) - c
  95.                 qb = _blue(z) - c
  96.  
  97.                 r = _red(point(x + 1, y)) +   qr*7/16
  98.                 g = _green(point(x + 1, y)) + qg*7/16
  99.                 b = _blue(point(x + 1, y)) +  qb*7/16
  100.                 pset (x + 1, y), _rgb(r, g, b)
  101.  
  102.                 r = _red(point(x - 1, y + 1)) +   qr*3/16
  103.                 g = _green(point(x - 1, y + 1)) + qg*3/16
  104.                 b = _blue(point(x - 1, y + 1)) +  qb*3/16
  105.                 pset (x - 1, y + 1), _rgb(r, g, b)
  106.  
  107.                 r = _red(point(x, y + 1)) +   qr*5/16
  108.                 g = _green(point(x, y + 1)) + qg*5/16
  109.                 b = _blue(point(x, y + 1)) +  qb*5/16
  110.                 pset (x, y + 1), _rgb(r, g, b)
  111.  
  112.                 r = _red(point(x + 1, y + 1)) +   qr*1/16
  113.                 g = _green(point(x + 1, y + 1)) + qg*1/16
  114.                 b = _blue(point(x + 1, y + 1)) +  qb*1/16
  115.                 pset (x + 1, y + 1), _rgb(r, g, b)
  116.         next
  117.         next
  118.  
dither.png
* dither.png (Filesize: 101.11 KB, Dimensions: 400x512, Views: 405)
* dither.bas (Filesize: 2.63 KB, Downloads: 200)

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
Re: Floyd–Steinberg dithering
« Reply #1 on: July 16, 2018, 09:13:53 am »
Hi v,

This is very interesting. Can an image be so modified that it is recognizable with just the black dot pattern? or would the dot sizes have to be increased?

Offline RhoSigma

  • QB64 Developer
  • Forum Resident
  • Posts: 565
Re: Floyd–Steinberg dithering
« Reply #2 on: July 16, 2018, 10:20:23 am »
Maybe some interresting reading for you to dive deeper into dithering: http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/
« Last Edit: June 15, 2019, 04:18:18 pm by RhoSigma »
My Projects:   https://qb64forum.alephc.xyz/index.php?topic=809
GuiTools - A graphic UI framework (can do multiple UI forms/windows in one program)
Libraries - ImageProcess, StringBuffers (virt. files), MD5/SHA2-Hash, LZW etc.
Bonus - Blankers, QB64/Notepad++ setup pack

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
Re: Floyd–Steinberg dithering
« Reply #3 on: July 16, 2018, 10:19:35 pm »
Maybe some interresting reading for you to dive deeper into dithering: http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/

I have added support for arbitrary error diffusion matrices so you can now use 9 out of those in the page.  Or try to invent your own error diffusion matrix.  Usage should be clear from the code. Screenshots shows each matrix for black&white, 2*3 colors, and 4*3 colors.

Code: QB64: [Select]
  1. deflng a-z
  2.  
  3. img1 = _loadimage("img/zmago.jpg", 32)
  4.  
  5. w = _width(img1)
  6. h = _height(img1)
  7.  
  8. img2 = _newimage(w, h, 32)
  9. img3 = _newimage(w, h, 32)
  10. img4 = _newimage(w, h, 32)
  11.  
  12. img5 = _newimage(w, h, 32)
  13. img6 = _newimage(w, h, 32)
  14. img7 = _newimage(w, h, 32)
  15.  
  16. img8 = _newimage(w, h, 32)
  17. img9 = _newimage(w, h, 32)
  18. img10 = _newimage(w, h, 32)
  19.  
  20. screen _newimage(w*3, h*3, 32)
  21.  
  22. redim h(2, 1) as single
  23. h(0,0)=0:h(1,0)=-1:h(2,0)=7/16
  24. h(0,1)=3/16:h(1,1)=5/16:h(2,1)=1/16
  25.  
  26. dither_bw img1, img2, 0.1, h()
  27. dither img1, img3, 2, h()
  28. dither img1, img4, 4, h()
  29.  
  30. redim h(4, 2) as single
  31. h(0,0)=0:h(1,0)=0:h(2,0)=-1:h(3,0)=7/48:h(4,0)=5/48
  32. h(0,1)=3/48:h(1,1)=5/48:h(2,1)=7/48:h(3,1)=5/48:h(4,1)=3/48
  33. h(0,2)=1/48:h(1,2)=3/48:h(2,2)=5/48:h(3,2)=3/48:h(4,2)=1/48
  34.  
  35. dither_bw img1, img5, 0.1, h()
  36. dither img1, img6, 2, h()
  37. dither img1, img7, 4, h()
  38.  
  39. redim h(3, 2) as single
  40. h(0,0)=0:h(1,0)=-1:h(2,0)=1/8:h(3,0)=1/8
  41. h(0,1)=1/8:h(1,1)=1/8:h(2,1)=1/8:h(3,1)=0
  42. h(0,2)=0:h(1,2)=1/8:h(2,2)=0:h(3,2)=0
  43.  
  44. dither_bw img1, img8, 0.1, h()
  45. dither img1, img9, 2, h()
  46. dither img1, img10, 4, h()
  47.  
  48. _putimage (0, 0), img2
  49. _putimage (w, 0), img3
  50. _putimage (2*w, 0), img4
  51. _printstring (0,0),"Floyd-Steinberg"
  52.  
  53. _putimage (0, h), img5
  54. _putimage (w, h), img6
  55. _putimage (2*w, h), img7
  56. _printstring (0,h),"Jarvis, Judice, and Ninke"
  57.  
  58. _putimage (0, 2*h), img8
  59. _putimage (w, 2*h), img9
  60. _putimage (2*w, 2*h), img10
  61. _printstring (0,2*h),"Atkinson"
  62.  
  63.  
  64.  
  65. 'colour dither
  66. 'source image, destination image, number of colours per channel, diffusion matrix
  67. sub dither(img1, img2, num, h() as single)
  68.         w = _width(img1)
  69.         h = _height(img1)
  70.  
  71.         _dest img2
  72.         _source img2
  73.  
  74.         _putimage , img1
  75.  
  76.         for y=0 to h-1
  77.         for x=0 to w-1
  78.  
  79.                 z = point(x, y)
  80.  
  81.                 r = (_red(z)*num\255)*255\num
  82.                 g = (_green(z)*num\255)*255\num
  83.                 b = (_blue(z)*num\255)*255\num
  84.  
  85.                 pset (x, y), _rgb(r, g, b)
  86.  
  87.                 qr = _red(z) - r
  88.                 qg = _green(z) - g
  89.                 qb = _blue(z) - b
  90.  
  91.                 conv_ed img2, x, y, h(), qr, qg, qb
  92.         next
  93.         next
  94.  
  95. 'black and white dither
  96. 'source image, destination image, bw threshold percent, diffusion matrix
  97. sub dither_bw(img1, img2, t as double, h() as single)
  98.         w = _width(img1)
  99.         h = _height(img1)
  100.  
  101.         _dest img2
  102.         _source img2
  103.  
  104.         _putimage , img1
  105.  
  106.         for y=0 to h-1
  107.         for x=0 to w-1
  108.  
  109.                 z = point(x, y)
  110.  
  111.                 c = -((_red(z)+_green(z)+_blue(z))/3 > 255*t)*255
  112.  
  113.                 pset (x, y), _rgb(c, c, c)
  114.  
  115.                 qr = _red(z) - c
  116.                 qg = _green(z) - c
  117.                 qb = _blue(z) - c
  118.  
  119.                 conv_ed img2, x, y, h(), qr, qg, qb
  120.         next
  121.         next
  122.  
  123. sub conv_ed(img, x0, y0, h() as single, qr, qg, qb)
  124.         for y=0 to ubound(h,2)
  125.         for x=0 to ubound(h,1)
  126.                 if h(x,y)=-1 then
  127.                         xx = x
  128.                         yy = y
  129.                 end if
  130.         next
  131.         next
  132.  
  133.         _source img
  134.         _dest img
  135.  
  136.         for y=0 to ubound(h,2)
  137.         for x=0 to ubound(h,1)
  138.                 if h(x,y)<>-1 and h(x,y)<>0 then
  139.                         r = _red(point(x0-xx+x, y0-yy+y)) + qr*h(x,y)
  140.                         g = _green(point(x0-xx+x, y0-yy+y)) + qg*h(x,y)
  141.                         b = _blue(point(x0-xx+x, y0-yy+y)) + qb*h(x,y)
  142.  
  143.                         pset (x0-xx+x, y0-yy+y), _rgb(r, g, b)
  144.                 end if
  145.         next
  146.         next
  147.  
dither2.png
* dither2.png (Filesize: 90.44 KB, Dimensions: 600x768, Views: 391)
* dither2.bas (Filesize: 3.03 KB, Downloads: 208)

Offline _vince

  • Seasoned Forum Regular
  • Posts: 422
Re: Floyd–Steinberg dithering
« Reply #4 on: July 16, 2018, 10:35:04 pm »
FYI, here are the non-dithered downsampled versions of the image.
dither3.png
* dither3.png (Filesize: 14.18 KB, Dimensions: 600x256, Views: 378)