Dither Go! is a fast image processing library, making use of dither
Golang library to provide the most correct image dithering.
[!IMPORTANT] Keep in mind, that this library is currently in alpha state, and although there probably won't be that many breaking changes (unless there will be in
dither
), there might be some bugs laying around in the less tested parts of the library. If you find some, please report them to the bug tracker.
SetOrdered
, this library can dither using any matrix (under construction)To install Dither Go! from PyPI, setup venv
and run:
$ pip install dither-go
And import library using import dither_go
.
Here's a simple example using Floyd-Steinberg dithering:
try:
img = dither_go.open_image("input.jpg")
except Exception as e:
print(f"Couldn't load the requested image. Exception: {e}")
# This is the color palette used in output image
palette = dither_go.create_palette([
[0, 0, 0],
[255, 255, 255],
# You can put here any color you want
])
# Create new `Ditherer` object using a constructor
ditherer = dither_go.new_ditherer(palette)
ditherer.Matrix = dither_go.ErrorDiffusers.FloydSteinberg
# Dither the image, attempting to modify the existing image
# If it can't then a dithered copy will be returned.
img = ditherer.Dither(img)
dither_go.save_image(img, "dither_go.png", "png")
If you always want to dither a copy of the image, you can use DitherCopy
instead.
Here's how you create a Ditherer
that does Bayer dithering. Note that ditherer.SetBayer
is used instead of ditherer.Matrix
.
ditherer = dither_go.new_ditherer(palette)
ditherer.SetBayer(8, 8, 1.0) # 8x8 Bayer matrix at 100% strength
Here's how you create a Ditherer
that does clustered-dot dithering - dithering with a predefined matrix.
ditherer = dither_go.new_ditherer(palette)
ditherer.SetOrdered(dither_go.OrderedDitherers.ClusteredDotDiagonal8x8, 1.0)
Generally, using Floyd-Steinberg serpentine dithering will produce the best results. The code would be:
ditherer = dither_go.new_ditherer(palette)
ditherer.Matrix = dither_go.ErrorDiffusers.FloydSteinberg
ditherer.Serpentine = True
Playing with the strength of the matrix might also be useful. The example above is at full strength, but sometimes that's too noisy. The code for 80% strength looks like this:
ditherer.Matrix = dither_go.error_diffusion_strength(dither_go.ErrorDiffusers.FloydSteinberg, 0.8)
The main reason for using any other dithering algorithm would be
Bayer
, will use all available CPUs, which is much faster.Built-in error diffusion and ordered dither matrices are located in ErrorDiffusers
and OrderedDitherers
data classes.
In order to apply error diffusion matrix to Ditherer
, first construct a new instance by using new_ditherer()
constructor and set Matrix
variable value to any of the ErrorDiffusers
matrices.
To apply ordered dither matrix, use SetOrdered
method instead.
[!WARNING] You can't have both types of dither applied at the same time, so in order to change dithering type, you'll need to clear currently used dither type.
To clear error diffusion matrix, set
None
as a value inMatrix
and to clear ordered dither matrix, useClearMapper
.
Sometimes the palette isn't an option, as it might determined by the hardware. Many e-ink screens can only display black and white for example, and so your palette is chosen for you.
But in most cases you have all the colors available, and so you have to pick the ones that represent your image best. This is called color quantization.
There isn't currently any built-in color quantization functionality available in this library. You'll instead need to use a library for that, like color-thief which is the most popular, or something like colorgram.py or Pylette. There is also pywal which you can use as a module.
A dithered output image will only look right at 100% size. As you scale down, the image will immediately get darker, and strange grid-like artifacts will appear, known as a moiré pattern. This is due to how dithered images work, and is not something this library can fix.
The best thing to do is to scale the input image to the exact size you want before using this library. But sometimes you want to scale the image up after dithering, to make the dithering effect more obvious for aesthetic purposes.
So for scaling the dithered output image up (above 100%), that will only look fine if you use nearest-neighbor scaling - the kind of scaling that produces pixelated results. Otherwise the dither pixel values will be blurred and averaged, which will mess things up. And even once you're using that, it will still produce moiré patterns, unless you're scaling by a multiple of the original dimensions. So when scaling up, you should be scaling by 2x or 3x, rather than a non-integer like 1.34x.
Due to how Golang's image
library works, there is only support for the most basic image formats built-in into the standard library.
To add more, you need to use custom decoders/encoders, of which Dither Go! already has several added and a couple planned to be added. You can check the image format matrix to see which are available and planned to be added.
If you want support for a format which isn't currently on that list, submit a feature request in the bug tracker.
[][]uint
matrices are supposed to be applied with PixelMapperFromMatrix
.MatrixUtils().generate_matrices_list()
method.dither
project to provide sufficient information about features of this libraryThis repository is licensed under the terms of the GNU GPLv3 license. You can find a copy of the license in the COPYING file.
The dither
library which this project is making use of, is the terms of Mozilla Public License 2.0. The copy of the license can be found here.