From 02b288220d4381dc48277d4896bc11db35049e33 Mon Sep 17 00:00:00 2001 From: makeworld Date: Fri, 12 Feb 2021 20:54:25 -0500 Subject: [PATCH] Error diffusion strength --- CHANGELOG.md | 13 +++++++++ dither_test.go | 6 ++++ error_diffusers.go | 26 ++++++++++++++++++ .../edm_floyd-steinberg_strength_02.png | Bin 0 -> 2406 bytes pixelmappers.go | 2 +- 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md create mode 100644 images/output/edm_floyd-steinberg_strength_02.png diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2e55a82 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] +### Added +- Added `ErrorDiffusionStrength` to set the strength of error diffusion dithering (#4) + + +## [1.0.0] - 2021-02-11 +Initial release. \ No newline at end of file diff --git a/dither_test.go b/dither_test.go index d7043a4..103f0f7 100644 --- a/dither_test.go +++ b/dither_test.go @@ -210,6 +210,12 @@ func TestSerpentine(t *testing.T) { ditherAndCompareImage(gradient, "edm_floyd-steinberg_serpentine.png", d, t) } +func TestErrorDiffusionStrength(t *testing.T) { + d := NewDitherer(blackWhite) + d.Matrix = ErrorDiffusionStrength(FloydSteinberg, 0.5) + ditherAndCompareImage(gradient, "edm_floyd-steinberg_strength_02.png", d, t) +} + func TestErrorDiffusionColor(t *testing.T) { d := NewDitherer(redGreenBlack) diff --git a/error_diffusers.go b/error_diffusers.go index 800699e..8f4d4f3 100644 --- a/error_diffusers.go +++ b/error_diffusers.go @@ -34,6 +34,32 @@ func (e ErrorDiffusionMatrix) Offset(x, y, curPx int) (int, int) { return x - curPx, y } +// ErrorDiffusionStrength modifies an existing error diffusion matrix so that it will +// be applied with the specified strength. +// +// strength is usually a value from 0 to 1.0, where 1.0 means 100% strength, and will +// not modify the matrix at all. It is inversely proportional to contrast - reducing the +// strength increases the contrast. It can be useful at values like 0.8 for reducing +// noise in the dithered image. +// +// See the documentation for Bayer for more details. +func ErrorDiffusionStrength(edm ErrorDiffusionMatrix, strength float32) ErrorDiffusionMatrix { + if strength == 1 { + return edm + } + + dy := len(edm) + dx := len(edm[0]) + edm2 := make(ErrorDiffusionMatrix, dy) + for y := 0; y < dy; y++ { + edm2[y] = make([]float32, dx) + for x := 0; x < dx; x++ { + edm2[y][x] = edm[y][x] * strength + } + } + return edm2 +} + var Simple2D = ErrorDiffusionMatrix{ {0, 0.5}, {0.5, 0}, diff --git a/images/output/edm_floyd-steinberg_strength_02.png b/images/output/edm_floyd-steinberg_strength_02.png new file mode 100644 index 0000000000000000000000000000000000000000..efdfe73237880b8dc73105db05b49c989bb48912 GIT binary patch literal 2406 zcmV-s37PhZP)B3WQtt|9_cNXGRhYk}SznF*#*=X0wm%!ZEw|6J(phtsqgJP;42LGUh?nfNVyy_1;~sn z-Uvu5*Y<&01GSV{UtK=F5XMK78X5$;4XZx95D zqq1+|+imu3a>2L_g8aLCI88bf*<3~M9`$mBMD#Tx|y_ARGf+2dQoykG9M*K5*BckfV)H%#^0mSWzc-Voi8#Tx_B%B?n&AocER zUUqK7p-)y0MMI7VAU9NRQx7bRb)?<3t-~>*K^toq#LMJQ6C6OlS-b%#*J#+55~$yO z)mv*=z<4-!8=y&{ZbLQVE)*{`wPSQc7H<^vzr1m-@35`gYf6%Zd&zlbTW&-7Otcwf z#k_3X=@|8ynl~CZ|OxUV;7FoOjX#I(rC*;XU=CZxk zs|6<+b`GcUx9PHR>)uU%aj0EsW~%IMU`(WA!eLkS>YRhiM4rmt-r@zlJ7Hh#5QAs^ zH2J3HjRojj4VP_xrHzjLzAi^+*s#ww{^`czjZ(hJydep8*q#87mrnw{FgT2( z6q@C|#lJStd*wBli^~wjo6H*r7jU3M>IJnsNHxgW<_6r-VBZ|0*SR6wo6H-77Cu+* z9jo38$%~?w+?vRf%?&Nuz&_ABZ?9<(T zmAs+SH<>p8@PEu^k{GD>`azIuAohzJ+{)5jscTn9X-@b`@(M25%Uxw}K}|Cl?%kwb zs5-pPvZ2P)i#6)FcV^ zrexl~0xsFRre0HUuGKaeY};eq?s{*od69YH(;nyFWZn>%@Gg@HY4J+*TH@7Ij^ke5 z-kAfF6n4|z*m1BfFK(-P7|FZ==(yD;9F|}0PWD7J=iU>+>)u&=4dvZm{w;n%GH(Ff zQfaaYhn3Ika*--{nIOu0DtK}3P3PB!PA^Jc#v$m~f5*ggFt`6X7$EM6w|{***FdA4 zWgcFST#w{s14gm~Oy^T7rRTNRni8w|5BXM=jX zbG;|rJ65mp7#7As)5G|o80nkL8vt{;Lu2aQP`oO3%^4qy>4bA}e0VW7Y&36<0lf-l zhOJ$&;l9)92-_fzZc{l-u6~z5`AozDYa&N8Y+AzljF@}R;td4bg*jyI2vi;2xy6fZ zHQQqlKD!x^a~zoL%SiFs-?64WbB67O*tU87$$scE+=`cV`eTwRYoCuF$~^C@pdB{! z9J-BV3yZgN8~hTcecb>enQW?Tq4thQ zwt?R(Bh9PB!`WebA$Hg149tekV)$um4w*=>Q8>n|1 zZs}ezo`cMqY|=~K$l>8E-UyhMFm~3kf;-1SI~O2U-|Md~@1SBq@nU~C-G+BT9O-)M9d;zf+&i!uj=VC$z5cp&WGr~uC|)JqbQ@j+ zxQ&i4-I3&-v2OX(8@zO{zt;M4_O=xMY7yin0*T&%>b>?FY%kYj(T}u)?M^hG ztg7mPVODF{E^c@jS-g=jZo^RZUV9BJ&`Y?P$-x>HWA)CQ1~JJTDBJ9?y#hGAW^YO% z{g%kN$4f9!;ae4_YPY}b_S|VOgCD~>egs*(0T4*#2&T$#x@}AFlHi~ux(%nA*AHPr zu&j3)jO3Lt-H^o_0LY&Ap&S$Dy)t>BU=8<@o5I}FyeJ*Cy(U()bd3t)NXP1*00030 Y|4(c%^s=JpyZ`_I07*qoM6N<$f`tObE&u=k literal 0 HcmV?d00001 diff --git a/pixelmappers.go b/pixelmappers.go index 19d2e7b..b84f64b 100644 --- a/pixelmappers.go +++ b/pixelmappers.go @@ -209,7 +209,7 @@ func convThresholdToAddition(scale float32, value uint, max uint) float32 { // lighter ones just get quantized. // // You might also want to reduce the strength to reduce noise in the image, as dithering -// doesn't produce smooth colored areas. +// doesn't produce smooth colored areas. Usually a value around 0.8 is good for this. // // You can also make strength negative. If you know already that your image is dark, and so // you don't want it to be made bright, then this is a better approach then shrinking the