Floyd-Steinberg dithering to DHGR colour palette. Doesn't yet take

into account the restrictions on neighbouring colours.
This commit is contained in:
kris 2020-12-29 18:24:29 +00:00
commit 5a6eb08db1
1 changed files with 155 additions and 0 deletions

155
dither.py Normal file
View File

@ -0,0 +1,155 @@
import argparse
from PIL import Image
from colormath.color_objects import sRGBColor, LabColor
from colormath.color_conversions import convert_color
from colormath import color_diff
import numpy as np
X_RES = 560
Y_RES = 192
# for each y from top to bottom
# for each x from left to right
# oldpixel := pixel[x][y]
# newpixel := find_closest_palette_color(oldpixel)
# pixel[x][y] := newpixel
# quant_error := oldpixel - newpixel
# pixel[x+1][y ] := pixel[x+1][y ] + quant_error * 7/16
# pixel[x-1][y+1] := pixel[x-1][y+1] + quant_error * 3/16
# pixel[x ][y+1] := pixel[x ][y+1] + quant_error * 5/16
# pixel[x+1][y+1] := pixel[x+1][y+1] + quant_error * 1/16
RGB = {
(False, False, False, False): np.array((0, 0, 0)), # Black
(False, False, False, True): np.array((148, 12, 125)), # Magenta
(False, False, True, False): np.array((99, 77, 0)), # Brown
(False, False, True, True): np.array((249, 86, 29)), # Orange
(False, True, False, False): np.array((51, 111, 0)), # Dark green
(False, True, False, True): np.array((126, 126, 126)), # Grey1
(False, True, True, False): np.array((67, 200, 0)), # Green
(False, True, True, True): np.array((221, 206, 23)), # Yellow
(True, False, False, False): np.array((32, 54, 212)), # Dark blue
(True, False, False, True): np.array((188, 55, 255)), # Violet
(True, False, True, False): np.array((126, 126, 126)), # Grey2
(True, False, True, True): np.array((255, 129, 236)), # Pink
(True, True, False, False): np.array((7, 168, 225)), # Med blue
(True, True, False, True): np.array((158, 172, 255)), # Light blue
(True, True, True, False): np.array((93, 248, 133)), # Aqua
(True, True, True, True): np.array((255, 255, 255)), # White
}
def find_closest_color(pixel):
least_diff = 1e9
best_colour = None
for v in RGB.values():
diff = np.sum(np.power(v - np.array(pixel), 2))
if diff < least_diff:
least_diff = diff
best_colour = v
return best_colour
def dither(filename):
im = Image.open(filename)
if im.mode != "RGB":
im = im.convert("RGB")
im.resize((X_RES, Y_RES), resample=Image.LANCZOS)
im.show()
for y in range(Y_RES):
print(y)
for x in range(X_RES):
# print(x)
oldpixel = im.getpixel((x, y))
newpixel = find_closest_color(oldpixel, newpixel, x)
quant_error = oldpixel - newpixel
# print(quant_error)
if x < (X_RES-1):
im.putpixel((x + 1, y), tuple((
np.array(im.getpixel((x + 1, y))) + quant_error * 7 /
16).astype(np.int)))
if x > 0 and y < Y_RES-1:
im.putpixel((x - 1, y + 1),
tuple((np.array(im.getpixel(
(x - 1, y + 1))) + quant_error * 3 /
16).astype(np.int)))
if y < Y_RES-1:
im.putpixel((x, y + 1),
tuple((np.array(im.getpixel(
(x, y + 1)) + quant_error * 5 / 16)).astype(
np.int)))
if x < (X_RES-1) and y < (Y_RES-1):
im.putpixel((x + 1, y + 1),
tuple((np.array(im.getpixel(
(x + 1, y + 1)) + quant_error * 1 /
16)).astype(np.int)))
im.show()
#
# c = {}
# for value in True, False:
# if value:
# s.set(x, y)
# else:
# s.unset(x, y)
#
# c[value] = convert_color(
# sRGBColor(*s.colours(x, y)[0], is_upscaled=True),
# LabColor)
#
# diffs = [
# (
# color_diff.delta_e_cie2000(oldpixel, newpixel),
# newpixel,
# value
# )
# for value, newpixel in c.items()]
#
# print(diffs)
# diff, newpixel, value = min(diffs)
# if value:
# s.set(x, y)
# else:
# s.unset(x, y)
#
# put(im, (x, y), np.array(newpixel.get_value_tuple()))
# yield x, y, value
# print(oldpixel, newpixel)
# quant_error = np.array(oldpixel.get_value_tuple()) - np.array(
# newpixel.get_value_tuple())
# print("qe = %s" % quant_error)
#
# if x < (screen.X_RES - 1):
# nr = (np.array(im.getpixel((x + 1, y)), dtype=np.float) / 256 +
# quant_error * 7 / 16)
# print(nr * 256)
# put(im, (x + 1, y), nr)
# print(im.getpixel((x+1, y)))
# if y < (screen.Y_RES - 1):
# put(im, (x - 1, y + 1),
# np.array(im.getpixel((x - 1, y + 1)), dtype=np.float) / 256 +
# quant_error * 3 / 16)
# put(im, (x, y + 1),
# np.array(im.getpixel((x, y + 1)), dtype=np.float) / 256 +
# quant_error * 5 / 16)
# put(im, (x + 1, y + 1),
# np.array(im.getpixel((x + 1, y + 1)), dtype=np.float) / 256 +
# quant_error * 1 / 16)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("input", type=str, help="Input file to process")
args = parser.parse_args()
dither(args.input)
#
# def put(image, xy, lab_value):
# # print(lab_value)
# image.putpixel(xy, tuple((lab_value * 256).astype(int)))
if __name__ == "__main__":
main()