tfuxu / dither-go

Dither Go: A fast and the most correct image dithering library
https://pypi.org/project/dither-go
GNU General Public License v3.0
2 stars 0 forks source link
dithering error-diffusion golang image-processing ordered-dithering python

Dither Go!

PyPI - Version PyPI - Python Version Build Wheels

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.

Features:

Types of dithering supported

How to install?

To install Dither Go! from PyPI, setup venv and run:

$ pip install dither-go

And import library using import dither_go.

Usage

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)

What method should I use?

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

How to access built-in matrices?

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 in Matrix and to clear ordered dither matrix, use ClearMapper.

How do I get the palette?

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.

Scaling images

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.

Encoding and decoding

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.

Tips:

Notes:

License

This 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.