clone your own copy | download snapshot

Snapshots | iceberg

Inside this repository


Download raw (2.7 KB)

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image
import re

def color (color):
    if type(color) is str:
        m = re.match(r'(\d+),\s*(\d+),\s*(\d+)', color)

        if m:
            return (int(m.group(1)), int(m.group(2)), int(m.group(3)))
            return (0,0,0)
       return color

def dither(image, threshold=125, color=(0,0,0,255), scale=1, postscale=1, colormap=None):

    im = image.convert('L')

    im = im.resize((int(im.size[0] * scale), int(im.size[1] * scale)), Image.BICUBIC)
    if colormap:
        im_out = colormap.resize((im.size[0], im.size[1]), Image.BICUBIC).convert('RGBA')
        im_out = Image.new(mode='RGBA', size=(im.size[0], im.size[1]), color=color)

    px_in = im.load()
    px_out = im_out.load()

    neighbours = ((1,0), (2,0), (-1,1), (0,1), (1,1), (0,2))

    for y in range(im.size[1]):
        for x in range(im.size[0]):
            old = px_in[x, y]
            new = 255 if old > threshold else 0
            err = (old - new) >> 3

            if new == 255:
                px_out[x,y] = (color[0], color[1], color[2], 0)

            for c in neighbours:
                    px_in[x+c[0], y+c[1]] += err
                except IndexError:

    if postscale > 1:
        im_out = im_out.resize((im_out.size[0] * postscale, im_out.size[1] * postscale), Image.NEAREST)

    return im_out

if __name__ == '__main__':
    import argparse

    parser = argparse.ArgumentParser()
    parser.add_argument('-c', '--color', dest='color', default=(0,0,0), type=color, help="Color to dither the image.\n\nIn the format of a comma-separated list \d+,\d+,\d+ Defaults to black ( 0,0,0 ).")
    parser.add_argument('-t', '--threshold', dest='threshold', default=128, type=int, help="Threshold after which a pixel is either turned on or of.")
    parser.add_argument('-s', '--scale', dest='scale', default=1.0, type=float, help="Sets the amount the source-image is scaled before dithering")
    parser.add_argument('-ps', '--post-scale', dest='postscale', default=1, type=int, help="Sets the amount of scaling performed after the dithering. Given in integers. Uses neirest neighbour for the scaling")
    parser.add_argument('-cm', '--color-map', dest='colormap', default=None, type=str, help="Take colors from given image to color the the dithering pixels")
    parser.add_argument('source', help="The location of the image to dither.")
    parser.add_argument('output', help="The location to save the resulting image.")
    args = parser.parse_args()

    im = Image.open(args.source)
    im = dither(im, threshold=args.threshold, color=args.color, scale=args.scale, postscale=args.postscale, colormap=args.colormap)