makew0rld / dither Goto Github PK
View Code? Open in Web Editor NEWA fast, correct image dithering library in Go.
License: Mozilla Public License 2.0
A fast, correct image dithering library in Go.
License: Mozilla Public License 2.0
Thanks to @/Shrinks99
This will not do dithering in the alpha channel, but it will properly handle images with non-opaque pixels.
DitherPaletted
wouldn't support this, as this would require breaking the guarantee that the returned image has the same palette as the Ditherer
.
As demonstrated here, it's possible to have a strength value for error diffusion too. This can be added as a struct field in Ditherer
, where the default value of 0 indicates 100% strength. This will not break compatibility.
"unexpected" is in quotes because the math is sound, but the result is almost never what you want. This is a property of dithering, and is not related to the library code.
The bad output can be very severe with error diffusion dithering, but ordered dithering still can have issues too.
The solution is to always make an image grayscale before dithering with a grayscale palette. The library should have a convenience function for this, as well as explain it in the README. Should the library detect grayscale palettes and make the image grayscale automatically when dithering? Probably not, the user should still have the option to.
Original image:
Original image but made to black and white:
Floyd-Steinberg dithering the color image with an 8-bit sRGB grayscale palette of 0, 156, 213, 255. 100% strength:
And the same, but dithering the black and white image:
I've noticed recently that dither
uses ~500MB of RAM when dithering an example 3414x1920 image and it doesn't appear to run through a garbage collection after dithering.
Is this an expected behavior? If yes, could it be somehow reduced?
package main
import (
"fmt"
"image"
"image/color"
"image/png"
"os"
"github.com/makeworld-the-better-one/dither/v2"
)
func Dither() {
reader, err := os.Open("/home/tfuxc/Pictures/wallhaven-m9rqlk.png")
if err != nil {
panic(err)
}
defer reader.Close()
img, _, err := image.Decode(reader)
if err != nil {
panic(fmt.Sprintln("decode err:", err))
}
palette := []color.Color{
color.Black,
color.White,
color.RGBA{255, 0, 0, 255}, // Red
color.RGBA{0, 255, 0, 255}, // Green
color.RGBA{0, 0, 255, 255}, // Blue
}
d := dither.NewDitherer(palette)
d.Matrix = dither.FloydSteinberg
img = d.Dither(img)
writer, err := os.Create("dithered.png")
if err != nil {
panic(err)
}
encode_err := png.Encode(writer, img)
if encode_err != nil {
writer.Close()
return
}
if err := writer.Close(); err != nil {
panic(err)
}
}
func main() {
for i := 0; i < 4; i++ {
Dither()
}
var input string
fmt.Println("Done! Press a key and enter to close.")
fmt.Scan(&input)
}
Minimal reproducer does a dithering operation four times to show that the allocated memory is exponentially higher in subsequent iterations.
It's odd this type of dithering is referred to using a full name of its creator when all other dithering types are referred to only by the surname of the creator.
How about just Pigeon?
This is mentioned/proposed in the comments of the page that you link to.
This issue was originally made for the didder repo, so didder commands are referenced.
This is a strange and important bug.
These are the two commands:
didder -i input.png -o test.png -p 'black red white' edm FloydSteinberg
didder -i input.png -o test2.png -p 'black white red' edm FloydSteinberg
input.png
is this image:
Here are the respective outputs:
Obviously, they should be exactly the same. This only occurs with edm
and does not depend on the matrix used. This does not seem to occur with the upstream dither library, indicating the problem is with the didder code.
Hey there, love the project, but the burkes dithering matrix here appears to be wrong:
{0, 0, 0, 8.0 / 32, 8.0 / 32},
Which I believe should be {0, 0, 0, 8.0 / 32, 4.0 / 32}, according to libcaca/study/3, and Image Dithering: Eleven Algorithms and Source Code
From the Bayer
docs:
As for color images, after my own experimentation, I've determined that everything I said above about grayscale images still applies. Stick to 1.0 and Bayer sizes above 4x4 if you can, and changing the strength still changes contrast as described above.
This is false.
For example, here is a color image:
Here is 16x16 Bayer with an 8-bit sRGB grayscale palette of 0, 156, 213, 255. 100% strength.
The second image is obviously being made lighter despite being above 4x4 in the recommendations.
https://surma.dev/things/ditherpunk/ talks about this a bit, and he implemented it as well.
In code it will likely be called blue noise dithering.
Hi, I saw the post in your blog re dithering, where you asked if one know how to do clustered dithering to contact you. Dithering you mentioned is usally called halftone dithering and have long history in silk printing and offset printing. You can find many resources on it on the net, but what you have to do is sample your image withe some frequency in 256 levels of gray, and then replace each sample by 16x16 matrix of pixels representing 256 levels of gray. It can be also be done with lines instead of dots resulting in line raster, where you vary width of the line with intensity. It is also not bad idea to do bluring and unsharp masking before sampling which is equivalent of applying lowpas 2d filter before sampling as in Niquist theorem where you first filter the signal before sampling to avoid artifacts. If you want to do quick experiment, just replace each pixel with 8x8 pixels representing black dots on white background for levels below 50% and white dots on black background for leves above 50%. If you need any help feel free to contact me at robert.aleksic at gmail.com. cheers robby
p.s. if you want to do it in color, you separate your image to components like CMYK for ofset printing and then apply all four images one on on top of the other, with varying angles of raster to obtaine nice rosette like here: https://cdn2.hubspot.net/hubfs/2296165/Imported_Blog_Media/CMKdot-1.jpg. Black component is created to avoid usage of to much ink and preserve gray balance, but that is another topic - color calibration.
Another SpecialDither
https://bisqwit.iki.fi/story/howto/dither/jy/
There are three versions of this to be implemented.
This would be a SpecialDither
.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.