mirror of
https://github.com/KrisKennaway/ii-pix.git
synced 2024-12-26 18:29:29 +00:00
Limit colour choices to the two available at each pixel.
This commit is contained in:
parent
5a6eb08db1
commit
da420b66c8
159
dither.py
159
dither.py
@ -1,32 +1,19 @@
|
|||||||
import argparse
|
import argparse
|
||||||
from PIL import Image
|
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
|
import numpy as np
|
||||||
|
|
||||||
X_RES = 560
|
X_RES = 560
|
||||||
Y_RES = 192
|
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 = {
|
RGB = {
|
||||||
(False, False, False, False): np.array((0, 0, 0)), # Black
|
(False, False, False, False): np.array((0, 0, 0)), # Black
|
||||||
(False, False, False, True): np.array((148, 12, 125)), # Magenta
|
(False, False, False, True): np.array((148, 12, 125)), # Magenta
|
||||||
(False, False, True, False): np.array((99, 77, 0)), # Brown
|
(False, False, True, False): np.array((99, 77, 0)), # Brown
|
||||||
(False, False, True, True): np.array((249, 86, 29)), # Orange
|
(False, False, True, True): np.array((249, 86, 29)), # Orange
|
||||||
(False, True, False, False): np.array((51, 111, 0)), # Dark green
|
(False, True, False, False): np.array((51, 111, 0)), # Dark green
|
||||||
(False, True, False, True): np.array((126, 126, 126)), # Grey1
|
# XXX RGB values are used as keys in DOTS dict, need to be unique
|
||||||
|
(False, True, False, True): np.array((126, 126, 125)), # Grey1
|
||||||
(False, True, True, False): np.array((67, 200, 0)), # Green
|
(False, True, True, False): np.array((67, 200, 0)), # Green
|
||||||
(False, True, True, True): np.array((221, 206, 23)), # Yellow
|
(False, True, True, True): np.array((221, 206, 23)), # Yellow
|
||||||
(True, False, False, False): np.array((32, 54, 212)), # Dark blue
|
(True, False, False, False): np.array((32, 54, 212)), # Dark blue
|
||||||
@ -39,11 +26,39 @@ RGB = {
|
|||||||
(True, True, True, True): np.array((255, 255, 255)), # White
|
(True, True, True, True): np.array((255, 255, 255)), # White
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NAMES = {
|
||||||
|
(0, 0, 0): "Black",
|
||||||
|
(148, 12, 125): "Magenta",
|
||||||
|
(99, 77, 0): "Brown",
|
||||||
|
(249, 86, 29): "Orange",
|
||||||
|
(51, 111, 0): "Dark green",
|
||||||
|
(126, 126, 125): "Grey1", # XXX
|
||||||
|
(67, 200, 0): "Green",
|
||||||
|
(221, 206, 23): "Yellow",
|
||||||
|
(32, 54, 212): "Dark blue",
|
||||||
|
(188, 55, 255): "Violet",
|
||||||
|
(126, 126, 126): "Grey2",
|
||||||
|
(255, 129, 236): "Pink",
|
||||||
|
(7, 168, 225): "Med blue",
|
||||||
|
(158, 172, 255): "Light blue",
|
||||||
|
(93, 248, 133): "Aqua",
|
||||||
|
(255, 255, 255): "White"
|
||||||
|
}
|
||||||
|
|
||||||
def find_closest_color(pixel):
|
DOTS = {}
|
||||||
|
for k, v in RGB.items():
|
||||||
|
DOTS[tuple(v)] = k
|
||||||
|
|
||||||
|
|
||||||
|
def find_closest_color(pixel, last_pixel, x: int):
|
||||||
least_diff = 1e9
|
least_diff = 1e9
|
||||||
best_colour = None
|
best_colour = None
|
||||||
for v in RGB.values():
|
|
||||||
|
last_dots = DOTS[tuple(last_pixel)]
|
||||||
|
other_dots = list(last_dots)
|
||||||
|
other_dots[x % 4] = not other_dots[x % 4]
|
||||||
|
other_dots = tuple(other_dots)
|
||||||
|
for v in (RGB[last_dots], RGB[other_dots]):
|
||||||
diff = np.sum(np.power(v - np.array(pixel), 2))
|
diff = np.sum(np.power(v - np.array(pixel), 2))
|
||||||
if diff < least_diff:
|
if diff < least_diff:
|
||||||
least_diff = diff
|
least_diff = diff
|
||||||
@ -51,91 +66,52 @@ def find_closest_color(pixel):
|
|||||||
return best_colour
|
return best_colour
|
||||||
|
|
||||||
|
|
||||||
|
class Dither:
|
||||||
|
PATTERN = None
|
||||||
|
ORIGIN = None
|
||||||
|
|
||||||
|
def apply(self, image, x, y, quant_error):
|
||||||
|
pattern = self.PATTERN[:Y_RES - y, :X_RES - x] / np.sum(self.PATTERN)
|
||||||
|
for offset, error_fraction in np.ndenumerate(pattern):
|
||||||
|
coord = (x + offset[1] - self.ORIGIN[1], y + offset[0] -
|
||||||
|
self.ORIGIN[0])
|
||||||
|
new_pixel = image.getpixel(coord) + error_fraction * quant_error
|
||||||
|
image.putpixel(coord, tuple(new_pixel.astype(int)))
|
||||||
|
|
||||||
|
|
||||||
|
class FloydSteinbergDither(Dither):
|
||||||
|
# 0 * 7
|
||||||
|
# 3 5 1
|
||||||
|
PATTERN = np.array(((0, 0, 7), (3, 5, 1)))
|
||||||
|
ORIGIN = (0, 1)
|
||||||
|
|
||||||
|
|
||||||
|
class KennawayDither(Dither):
|
||||||
|
# 0 * 7 5 3 1
|
||||||
|
# 3 5 3 1 1 0
|
||||||
|
PATTERN = np.array(((0, 0, 7, 5, 3, 1), (3, 5, 3, 1, 1, 0)))
|
||||||
|
ORIGIN = (0, 1)
|
||||||
|
|
||||||
|
|
||||||
def dither(filename):
|
def dither(filename):
|
||||||
im = Image.open(filename)
|
im = Image.open(filename)
|
||||||
if im.mode != "RGB":
|
if im.mode != "RGB":
|
||||||
im = im.convert("RGB")
|
im = im.convert("RGB")
|
||||||
im.resize((X_RES, Y_RES), resample=Image.LANCZOS)
|
im.resize((X_RES, Y_RES), resample=Image.LANCZOS)
|
||||||
im.show()
|
im.show()
|
||||||
|
|
||||||
|
# ditherer = FloydSteinbergDither()
|
||||||
|
ditherer = KennawayDither()
|
||||||
for y in range(Y_RES):
|
for y in range(Y_RES):
|
||||||
print(y)
|
print(y)
|
||||||
|
newpixel = (0, 0, 0)
|
||||||
for x in range(X_RES):
|
for x in range(X_RES):
|
||||||
# print(x)
|
|
||||||
oldpixel = im.getpixel((x, y))
|
oldpixel = im.getpixel((x, y))
|
||||||
newpixel = find_closest_color(oldpixel, newpixel, x)
|
newpixel = find_closest_color(oldpixel, newpixel, x)
|
||||||
|
im.putpixel((x, y), tuple(newpixel))
|
||||||
quant_error = oldpixel - newpixel
|
quant_error = oldpixel - newpixel
|
||||||
# print(quant_error)
|
ditherer.apply(im, x, y, 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()
|
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():
|
def main():
|
||||||
@ -146,10 +122,5 @@ def main():
|
|||||||
dither(args.input)
|
dither(args.input)
|
||||||
|
|
||||||
|
|
||||||
#
|
|
||||||
# def put(image, xy, lab_value):
|
|
||||||
# # print(lab_value)
|
|
||||||
# image.putpixel(xy, tuple((lab_value * 256).astype(int)))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
Loading…
Reference in New Issue
Block a user