le75
clone your own copy | download snapshot

Snapshots | iceberg

Inside this repository

__init__.py
text/x-python

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)))
        else:
            return (0,0,0)
    else:
       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')
    else:
        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:
                try:
                    px_in[x+c[0], y+c[1]] += err
                except IndexError:
                    pass

    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)
    im.save(args.output)