From b3e9d12db4cfbbf8f1760d80fcd760d48a83e583 Mon Sep 17 00:00:00 2001 From: Lex Neva <github.com@lexneva.name> Date: Sat, 21 Apr 2018 15:13:44 -0400 Subject: [PATCH] add inkstitch.threads.ThreadPalette class --- inkstitch/threads/__init__.py | 1 + inkstitch/threads/color.py | 8 ++-- inkstitch/threads/palette.py | 72 +++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 3 deletions(-) create mode 100644 inkstitch/threads/palette.py diff --git a/inkstitch/threads/__init__.py b/inkstitch/threads/__init__.py index 3ba5ec15c..fec82cede 100644 --- a/inkstitch/threads/__init__.py +++ b/inkstitch/threads/__init__.py @@ -1 +1,2 @@ from color import ThreadColor +from palette import ThreadPalette diff --git a/inkstitch/threads/color.py b/inkstitch/threads/color.py index ad3dc051a..af474127c 100644 --- a/inkstitch/threads/color.py +++ b/inkstitch/threads/color.py @@ -2,10 +2,11 @@ import simplestyle import re import colorsys + class ThreadColor(object): hex_str_re = re.compile('#([0-9a-z]{3}|[0-9a-z]{6})', re.I) - def __init__(self, color, name=None, description=None): + def __init__(self, color, name=None, number=None, manufacturer=None): if color is None: self.rgb = (0, 0, 0) elif isinstance(color, (list, tuple)): @@ -16,7 +17,8 @@ class ThreadColor(object): raise ValueError("Invalid color: " + repr(color)) self.name = name - self.description = description + self.number = number + self.manufacturer = manufacturer def __eq__(self, other): if isinstance(other, ThreadColor): @@ -77,4 +79,4 @@ class ThreadColor(object): # convert back to values in the range of 0-255 color = tuple(value * 255 for value in color) - return ThreadColor(color, name=self.name, description=self.description) + return ThreadColor(color, name=self.name, number=self.number, manufacturer=self.manufacturer) diff --git a/inkstitch/threads/palette.py b/inkstitch/threads/palette.py new file mode 100644 index 000000000..e1f47c7f8 --- /dev/null +++ b/inkstitch/threads/palette.py @@ -0,0 +1,72 @@ +from collections import Set +from .color import ThreadColor +from colormath.color_objects import sRGBColor, LabColor +from colormath.color_conversions import convert_color +from colormath.color_diff import delta_e_cie1994 + + +def compare_thread_colors(color1, color2): + # K_L=2 indicates textiles + return delta_e_cie1994(color1, color2, K_L=2) + + +class ThreadPalette(Set): + """Holds a set of ThreadColors all from the same manufacturer.""" + + def __init__(self, palette_file): + self.threads = dict() + self.parse_palette_file(palette_file) + + def parse_palette_file(self, palette_file): + """Read a GIMP palette file and load thread colors. + + Example file: + + GIMP Palette + Name: Ink/Stitch: Metro + Columns: 4 + # RGB Value Color Name Number + 240 186 212 Sugar Pink 1624 + 237 171 194 Carnatio 1636 + + """ + + with open(palette_file) as palette: + line = palette.readline().strip() + if line.lower() != "gimp palette": + raise ValueError("Invalid gimp palette header") + + self.name = palette.readline().strip() + if self.name.lower().startswith('name: ink/stitch: '): + self.name = self.name[18:] + + columns_line = palette.readline() + headers_line = palette.readline() + + for line in palette: + fields = line.split("\t", 3) + thread_color = [int(field) for field in fields[:3]] + thread_name, thread_number = fields[3].strip().rsplit(" ", 1) + thread_name = thread_name.strip() + + thread = ThreadColor(thread_color, thread_name, thread_number, manufacturer=self.name) + self.threads[thread] = convert_color(sRGBColor(*thread_color, is_upscaled=True), LabColor) + + def __contains__(self, thread): + return thread in self.threads + + def __iter__(self): + return iter(self.threads) + + def __len__(self): + return len(self.threads) + + def nearest_color(self, color): + """Find the thread in this palette that looks the most like the specified color.""" + + if isinstance(color, ThreadColor): + color = color.rgb + + color = convert_color(sRGBColor(*color, is_upscaled=True), LabColor) + + return min(self, key=lambda thread: compare_thread_colors(self.threads[thread], color))