mirror of https://github.com/cybernesto/VBMP.git
217 lines
8.6 KiB
Python
Executable File
217 lines
8.6 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# Error Diffusion Dither script for the GIMP
|
|
# Version 1.0
|
|
# Author: Mario Patino - cybernesto@gmail.com
|
|
# based on the dither script of Andy Hefner - ahefner@gmail.com
|
|
# dithering algorithms based on the information contained here:
|
|
# http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/
|
|
# Copyright Mario Patino, 2014
|
|
|
|
import math
|
|
from gimpfu import *
|
|
from array import array
|
|
|
|
|
|
def change_pixel(dim, src, x, y, px_d):
|
|
w, h, p = dim
|
|
if x > w:
|
|
return
|
|
if y > h:
|
|
return
|
|
i = (x + y * w)*p
|
|
src[i:i+p] = array("B",[max(min((a+b),255),0) for a,b in zip(px_d,src[i:i+p])])
|
|
|
|
def floyd_stein(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*7/16 for a in px_d]
|
|
change_pixel(dim, src_pixels, x+1, y , px_delta)
|
|
px_delta = [a*5/16 for a in px_d]
|
|
change_pixel(dim, src_pixels, x , y+1, px_delta)
|
|
px_delta = [a*3/16 for a in px_d]
|
|
change_pixel(dim, src_pixels, x-1, y+1, px_delta)
|
|
px_delta = [a*1/16 for a in px_d]
|
|
change_pixel(dim, src_pixels, x+1, y+1, px_delta)
|
|
|
|
def jarvis(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*7/48 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+1 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+1, px_delta)
|
|
px_delta = [a*5/48 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+2 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x-1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+2, px_delta)
|
|
px_delta = [a*3/48 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-2 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x-1 ,y+2, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+2, px_delta)
|
|
px_delta = [a*1/48 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-2 ,y+2, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y+2, px_delta)
|
|
|
|
def stucki(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*8/42 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+1 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+1, px_delta)
|
|
px_delta = [a*4/42 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+2 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x-1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+2, px_delta)
|
|
px_delta = [a*2/42 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-2 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x-1 ,y+2, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+2, px_delta)
|
|
px_delta = [a*1/42 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-2 ,y+2, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y+2, px_delta)
|
|
|
|
def atkinson(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*1/8 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+1 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x-1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+2, px_delta)
|
|
|
|
def burkes(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*8/32 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+1 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+1, px_delta)
|
|
px_delta = [a*4/32 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+2 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x-1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+1, px_delta)
|
|
px_delta = [a*2/32 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-2 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y+1, px_delta)
|
|
|
|
def sierra3(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*5/32 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+1 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+1, px_delta)
|
|
px_delta = [a*4/32 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+1, px_delta)
|
|
px_delta = [a*3/32 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+2 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+2, px_delta)
|
|
px_delta = [a*2/42 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-2 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x-1 ,y+2, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+2, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y+1, px_delta)
|
|
|
|
|
|
def sierra2(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*4/16 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+1 ,y, px_delta)
|
|
px_delta = [a*3/16 for a in px_d]
|
|
change_pixel(dim, src_pixels,x+2 ,y, px_delta)
|
|
change_pixel(dim, src_pixels,x ,y+1, px_delta)
|
|
px_delta = [a*2/16 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-1 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+1 ,y+1, px_delta)
|
|
px_delta = [a*1/16 for a in px_d]
|
|
change_pixel(dim, src_pixels,x-2 ,y+1, px_delta)
|
|
change_pixel(dim, src_pixels,x+2 ,y+1, px_delta)
|
|
|
|
def sierra1(dim, src_pixels, x, y, px_d):
|
|
px_delta = [a*2/4 for a in px_d]
|
|
change_pixel(dim, src_pixels, x+1, y , px_delta)
|
|
px_delta = [a*1/4 for a in px_d]
|
|
change_pixel(dim, src_pixels, x , y+1, px_delta)
|
|
change_pixel(dim, src_pixels, x-1, y+1, px_delta)
|
|
|
|
def px_dist(p1,p2):
|
|
return sum([(a-b)**2 for a,b in zip(p1,p2)])
|
|
|
|
def retro_dither(timg, tdrawable, mode, palette, alg):
|
|
dither = {0 : floyd_stein,
|
|
1 : jarvis,
|
|
2 : stucki,
|
|
3 : atkinson,
|
|
4 : burkes,
|
|
5 : sierra3,
|
|
6 : sierra2,
|
|
7 : sierra1,
|
|
}
|
|
pdb.gimp_image_undo_group_start(timg)
|
|
try:
|
|
layer = tdrawable.copy()
|
|
timg.add_layer(layer, 0)
|
|
width = layer.width
|
|
height = layer.height
|
|
px_size = pdb.gimp_drawable_bpp(tdrawable)
|
|
dim = width, height, px_size
|
|
rgn = layer.get_pixel_rgn(0, 0, width, height, TRUE, FALSE)
|
|
src_pixels = array("B", rgn[0:width, 0:height])
|
|
|
|
i=0
|
|
total=px_size*width*height
|
|
if mode == 1:
|
|
num_colors, colors = pdb.gimp_palette_get_colors(palette)
|
|
for y in range(height):
|
|
for x in range(width):
|
|
pixel = src_pixels[i:i+px_size]
|
|
dist = [px_dist(colors[c],pixel) for c in range(num_colors)]
|
|
nearest = colors[dist.index(min(dist))][0:px_size]
|
|
src_pixels[i:i+px_size] = array("B",nearest)
|
|
px_delta = [(a-b) for a,b in zip(pixel,src_pixels[i:i+px_size])]
|
|
dither[alg](dim, src_pixels, x, y, px_delta)
|
|
gimp.progress_update(i*1.0/total)
|
|
i = i + px_size
|
|
|
|
rgn[0:width, 0:height] = src_pixels.tostring()
|
|
layer.flush()
|
|
layer.update(0,0,width,height)
|
|
pdb.gimp_image_convert_indexed(timg, NO_DITHER, CUSTOM_PALETTE, 0, FALSE, FALSE, palette)
|
|
else:
|
|
white = [255] * px_size
|
|
black = [0] * px_size
|
|
for y in range(height):
|
|
for x in range(width):
|
|
pixel = src_pixels[i:i+px_size]
|
|
dist = sum(pixel)/px_size
|
|
if dist > 127:
|
|
nearest = white
|
|
else:
|
|
nearest = black
|
|
src_pixels[i:i+px_size] = array("B",nearest)
|
|
px_delta = [(a-b) for a,b in zip(pixel,src_pixels[i:i+px_size])]
|
|
dither[alg](dim, src_pixels, x, y, px_delta)
|
|
gimp.progress_update(i*1.0/total)
|
|
i = i + px_size
|
|
|
|
rgn[0:width, 0:height] = src_pixels.tostring()
|
|
layer.flush()
|
|
layer.update(0,0,width,height)
|
|
pdb.gimp_image_convert_indexed(timg, NO_DITHER, MONO_PALETTE, 0, FALSE, FALSE, palette)
|
|
|
|
pdb.gimp_image_undo_group_end(timg)
|
|
|
|
except Exception, err:
|
|
pdb.gimp_message("ERR: " + str(err))
|
|
pdb.gimp_image_undo_group_end(timg)
|
|
|
|
register(
|
|
"retro_dither",
|
|
"Error-diffusion dithering algorithms to create low resolution pictures in B&W or with a limited palette",
|
|
"Error-diffusion dithering algorithms",
|
|
"Mario Patino",
|
|
"Mario Patino",
|
|
"2014",
|
|
"<Image>/Colors/Dither...",
|
|
"RGB*, GRAY*",
|
|
[
|
|
(PF_RADIO, "mode", "Dithering mode:", 0, (("Monochrome",0),("Color", 1))),
|
|
(PF_PALETTE, "palette", "Palette:", "Default"),
|
|
(PF_OPTION,"alg", "Dithering algorithm:", 0, ["Floyd-Steinberg","Jarvis, Judice, and Ninke","Stucki","Atkinson", "Burkes", "Sierra", "Two-Row Sierra", "Sierra Lite"])
|
|
],
|
|
[],
|
|
retro_dither)
|
|
|
|
main() |