├── .gitignore ├── requirements.txt ├── ui ├── merging_colours.ui ├── progress_bar.ui ├── number_display.py ├── progress_bar.py ├── pixel_perfect.ui ├── ui_funcs.py ├── pixel_perfect.py ├── main.ui └── main_gui.py ├── README.md ├── scrapper.py ├── main.py └── res └── colours.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Local files (pycache, logs, etc.) 2 | examples/ 3 | __pycache__/ 4 | **/__pycache__/ 5 | 6 | *.log 7 | 8 | # Virtual Env 9 | .Python 10 | [Bb]in 11 | [Ii]nclude 12 | [Ll]ib 13 | [Ll]ib64 14 | [Ll]ocal 15 | [Ss]cripts 16 | share 17 | pyvenv.cfg 18 | .venv 19 | pip-selfcheck.json -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | cycler==0.10.0 2 | decorator==4.4.1 3 | imageio==2.6.1 4 | kiwisolver==1.1.0 5 | matplotlib==3.1.1 6 | networkx==2.4 7 | numpy==1.17.4 8 | opencv-python==4.1.1.26 9 | Pillow==8.1.1 10 | pyparsing==2.4.5 11 | PyQt5==5.13.2 12 | PyQt5-sip==12.7.0 13 | python-dateutil==2.8.1 14 | PyWavelets==1.1.1 15 | scikit-image==0.16.2 16 | scipy==1.3.2 17 | six==1.13.0 18 | -------------------------------------------------------------------------------- /ui/merging_colours.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 245 10 | 32 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 10 20 | 10 21 | 161 22 | 16 23 | 24 | 25 | 26 | Number of colours merged: 27 | 28 | 29 | 30 | 31 | 32 | 170 33 | 10 34 | 55 35 | 16 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ui/progress_bar.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 70 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 137 20 | 10 21 | 251 22 | 23 23 | 24 | 25 | 26 | 0 27 | 28 | 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 10 36 | 10 37 | 111 38 | 16 39 | 40 | 41 | 42 | Reducing colours... 43 | 44 | 45 | 46 | 47 | 48 | 10 49 | 40 50 | 93 51 | 28 52 | 53 | 54 | 55 | Cancel 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # reduce-colours 2 | 3 | This algorithm allows you to reduce the number of colours inside an original image to a desired number of colours (*n*). 4 | 5 | ## Fast tutorial: 6 | 7 | 1. `pip install -r requirements.txt` 8 | 2. `py ui/main_gui.py` 9 | 10 | ## Slower tutorial: 11 | * First, download [Python 3.7](https://www.python.org/downloads/) 12 | * Do this OR the next step: 13 | * Download [GIT for Windows](https://gitforwindows.org/) (if you're using Windows) 14 | * Open a command prompt where you want to put the program's files (`SHIFT + right click` > "open command prompt here") 15 | * Write `git clone https://github.com/Coul33t/reduce-colours.git`: this will copy the content of this repository to your machine 16 | * Do this OR the previous step: 17 | * Download the ZIP file (green icon in the top right corner of this repository) 18 | * Extract it where you want to put the program's files 19 | * Open a command prompt in the folder (`SHIFT + Right click` on the folder, then > "open command prompt here") 20 | * Write `pip install -r requirements.txt` in the command prompt (this will install all the required libraries for the program to run corectly) 21 | * Write `py ui/main_gui.py` (this will launch the program) 22 | 23 | ### Libraries used: 24 | * Skimage (Scikit image) 25 | * OpenCV (unusued right now) 26 | * PIL 27 | * Numpy 28 | * Libtcod (unused right now) 29 | 30 | ### TODO: 31 | * Args parser (input image and *n*) 32 | * Allow to specify regions to exclude for the colour reduction 33 | * Initial and final colours display in terminal with libtcod 34 | -------------------------------------------------------------------------------- /ui/number_display.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'merging_colours.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.11.3 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtWidgets 10 | 11 | class Ui_Dialog(object): 12 | def setupUi(self, Dialog): 13 | Dialog.setObjectName("Dialog") 14 | Dialog.resize(245, 32) 15 | self.label_number = QtWidgets.QLabel(Dialog) 16 | self.label_number.setGeometry(QtCore.QRect(10, 10, 161, 16)) 17 | self.label_number.setObjectName("label_number") 18 | self.label_number_value = QtWidgets.QLabel(Dialog) 19 | self.label_number_value.setGeometry(QtCore.QRect(170, 10, 55, 16)) 20 | self.label_number_value.setText("") 21 | self.label_number_value.setObjectName("label_number_value") 22 | 23 | self.retranslateUi(Dialog) 24 | QtCore.QMetaObject.connectSlotsByName(Dialog) 25 | 26 | def retranslateUi(self, Dialog): 27 | _translate = QtCore.QCoreApplication.translate 28 | Dialog.setWindowTitle(_translate("Dialog", "Dialog")) 29 | self.label_number.setText(_translate("Dialog", "Number of colours merged:")) 30 | 31 | class NumberDisplay(QtWidgets.QDialog, Ui_Dialog): 32 | def __init__(self, name=None, parent=None): 33 | super(NumberDisplay, self).__init__(parent) 34 | self.setupUi(self) 35 | self.show() 36 | 37 | if name is not None: 38 | self.set_title(name) 39 | 40 | def set_value(self, val): 41 | self.label_number_value.setText(str(val)) 42 | 43 | def set_title(self, desc): 44 | self.setWindowTitle(desc) 45 | -------------------------------------------------------------------------------- /ui/progress_bar.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'progress_bar.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.11.3 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtWidgets 10 | 11 | class Ui_Dialog(object): 12 | def setupUi(self, Dialog): 13 | Dialog.setObjectName("Dialog") 14 | Dialog.resize(400, 70) 15 | self.progressBar = QtWidgets.QProgressBar(Dialog) 16 | self.progressBar.setGeometry(QtCore.QRect(137, 10, 251, 23)) 17 | self.progressBar.setProperty("value", 24) 18 | self.progressBar.setObjectName("progressBar") 19 | self.label = QtWidgets.QLabel(Dialog) 20 | self.label.setGeometry(QtCore.QRect(10, 10, 111, 16)) 21 | self.label.setObjectName("label") 22 | self.pushButton = QtWidgets.QPushButton(Dialog) 23 | self.pushButton.setGeometry(QtCore.QRect(10, 40, 93, 28)) 24 | self.pushButton.setObjectName("pushButton") 25 | 26 | self.retranslateUi(Dialog) 27 | QtCore.QMetaObject.connectSlotsByName(Dialog) 28 | 29 | def retranslateUi(self, Dialog): 30 | _translate = QtCore.QCoreApplication.translate 31 | Dialog.setWindowTitle(_translate("Dialog", "Dialog")) 32 | self.label.setText(_translate("Dialog", "Reducing colours...")) 33 | self.pushButton.setText(_translate("Dialog", "Cancel")) 34 | 35 | class ProgressBar(QtWidgets.QDialog, Ui_Dialog): 36 | def __init__(self, name=None, parent=None): 37 | super(ProgressBar, self).__init__(parent) 38 | self.setupUi(self) 39 | self.show() 40 | 41 | if name is not None: 42 | self.set_title(name) 43 | 44 | def set_value(self, val): 45 | self.progressBar.setProperty("value", val) 46 | 47 | def set_title(self, desc): 48 | self.setWindowTitle(desc) 49 | -------------------------------------------------------------------------------- /scrapper.py: -------------------------------------------------------------------------------- 1 | from bs4 import BeautifulSoup as BS 2 | from selenium.webdriver import Firefox 3 | from selenium.webdriver.firefox.options import Options 4 | import json 5 | 6 | # This file isn't meant to be run every time, I just did it once and that's it. 7 | # The result is located in the "colours.json" file. 8 | 9 | class RGB2DMC: 10 | def __init__(self, rgb_value, hex_value, dmc_name, floss_number): 11 | self.rgb_value = rgb_value 12 | self.hex_value = hex_value 13 | self.dmc_name = dmc_name 14 | self.floss_number = floss_number 15 | 16 | 17 | 18 | firefox_options = Options() 19 | firefox_options.add_argument('--headless') 20 | 21 | driver = Firefox(options=firefox_options) 22 | 23 | driver.get('https://threadcolors.com/') 24 | html = driver.page_source 25 | soup = BS(html, 'html.parser') 26 | 27 | table_lines = soup.find_all('tr') 28 | 29 | colours = [] 30 | colour_dict = {'Floss': None, 'DMC Name': None, 'RGB': None, 'Hex': None} 31 | keys_order = [] 32 | rgb_keys = ['R', 'G', 'B'] 33 | 34 | for i, row in enumerate(table_lines): 35 | if i == 0: 36 | for column in row.find_all('th'): 37 | to_string = ''.join(column.contents).replace(u'\xa0', u'') 38 | keys_order.append(to_string) 39 | if to_string != '' and to_string not in rgb_keys: 40 | colour_dict[to_string] = [] 41 | 42 | else: 43 | new_colour = colour_dict.copy() 44 | values = [x.get_text() for x in row.find_all('td')] 45 | rgb_values = [0, 0, 0] 46 | for j, val in enumerate(values): 47 | if j != 0: 48 | if keys_order[j] in rgb_keys: 49 | if keys_order[j] == 'R': 50 | rgb_values[0] = int(val) 51 | elif keys_order[j] == 'G': 52 | rgb_values[1] = int(val) 53 | elif keys_order[j] == 'B': 54 | rgb_values[2] = int(val) 55 | else: 56 | new_colour[keys_order[j]] = val 57 | new_colour['RGB'] = rgb_values 58 | colours.append(new_colour) 59 | 60 | 61 | with open('colours.json', 'w') as json_output: 62 | json.dump(colours, json_output) 63 | -------------------------------------------------------------------------------- /ui/pixel_perfect.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | Dialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 574 10 | 417 11 | 12 | 13 | 14 | Dialog 15 | 16 | 17 | 18 | 19 | 10 20 | 10 21 | 361 22 | 361 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 380 33 | 220 34 | 81 35 | 28 36 | 37 | 38 | 39 | Go 40 | 41 | 42 | 43 | 44 | 45 | 380 46 | 280 47 | 81 48 | 28 49 | 50 | 51 | 52 | Validate 53 | 54 | 55 | 56 | 57 | 58 | 480 59 | 280 60 | 81 61 | 28 62 | 63 | 64 | 65 | Cancel 66 | 67 | 68 | 69 | 70 | 71 | 482 72 | 220 73 | 81 74 | 28 75 | 76 | 77 | 78 | Reset 79 | 80 | 81 | 82 | 83 | 84 | 380 85 | 130 86 | 181 87 | 80 88 | 89 | 90 | 91 | Pixelisation type 92 | 93 | 94 | 95 | 96 | 10 97 | 20 98 | 151 99 | 20 100 | 101 | 102 | 103 | Most common colour 104 | 105 | 106 | true 107 | 108 | 109 | 110 | 111 | 112 | 10 113 | 50 114 | 141 115 | 20 116 | 117 | 118 | 119 | Colours' average 120 | 121 | 122 | 123 | 124 | 125 | 126 | 380 127 | 10 128 | 181 129 | 111 130 | 131 | 132 | 133 | Pixels 134 | 135 | 136 | 137 | 138 | 10 139 | 20 140 | 55 141 | 16 142 | 143 | 144 | 145 | Pixel size: 146 | 147 | 148 | 149 | 150 | 151 | 110 152 | 20 153 | 61 154 | 22 155 | 156 | 157 | 158 | 1 159 | 160 | 161 | 999999 162 | 163 | 164 | 165 | 166 | 167 | 10 168 | 50 169 | 101 170 | 16 171 | 172 | 173 | 174 | Offset pixel grid: 175 | 176 | 177 | 178 | 179 | 180 | 110 181 | 50 182 | 61 183 | 22 184 | 185 | 186 | 187 | 188 | 189 | 190 | 10 191 | 80 192 | 91 193 | 20 194 | 195 | 196 | 197 | Display grid 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | from PIL import Image, ImageCms 3 | from skimage import color 4 | import numpy as np 5 | from math import floor, sqrt 6 | # Display the initial and final in a console 7 | import tcod as libtcod 8 | import argparse 9 | 10 | 11 | def compute_similarity_matrix(colours): 12 | colour_without_occurences = colours 13 | # If the occurence are in the colours array 14 | if len(colours[0]) == 2: 15 | colour_without_occurences = [x[1] for x in colours] 16 | 17 | similarity_matrix = np.zeros((len(colour_without_occurences), len(colour_without_occurences))) 18 | 19 | for i, current_colour in enumerate(colour_without_occurences): 20 | for j, colour_to_compare in enumerate(colour_without_occurences): 21 | similarity_matrix[i, j] = sum(abs(np.asarray(current_colour) - np.asarray(colour_to_compare))) 22 | 23 | similarity_matrix = 1 - (similarity_matrix / 255) 24 | 25 | return similarity_matrix 26 | 27 | def merge_most_similar(similarity_matrix, colours_lab, replaced_colours, replacement:'bright'): 28 | # TODO: 29 | # get most similar 30 | # merge (take the most lumineuse one) 31 | # add to dict the replaced colour 32 | # ex: green replaced by blue, blue replaced by violet -> green replaced by violet too 33 | 34 | # TODO: 35 | # Maybe actually replace everything in the original image and redo the whole process? 36 | 37 | # Get the most similar colours 38 | similarity_matrix[similarity_matrix == 1] = -1 39 | max_idx = np.unravel_index(similarity_matrix.argmax(), similarity_matrix.shape) 40 | 41 | c1_idx = max_idx[0] 42 | c2_idx = max_idx[1] 43 | c1 = colours_lab[c1_idx] 44 | c2 = colours_lab[c2_idx] 45 | 46 | # Compare their luminances : we keep the brightest one 47 | # TODO: try with lowest and merge 48 | if replacement == 'bright': 49 | # c1 is brigther than c2 50 | brigther_colour = c1 51 | darker_colour = c2 52 | brigther_idx = c1_idx 53 | darker_idx = c2_idx 54 | # If not, switch them 55 | if c1[1][0] < c2[1][0]: 56 | brigther_colour = c2 57 | darker_colour = c1 58 | brigther_idx = c2_idx 59 | darker_idx = c1_idx 60 | 61 | brigther_colour = ','.join([str(x) for x in brigther_colour[1]]) 62 | darker_colour = ','.join([str(x) for x in darker_colour[1]]) 63 | 64 | # Add the colours to the replacement array 65 | replaced_colours[darker_colour] = brigther_colour 66 | 67 | 68 | colours_lab[brigther_idx][0] += colours_lab[darker_idx][0] 69 | del colours_lab[darker_idx] 70 | 71 | 72 | return colours_lab, replaced_colours 73 | 74 | def replace_colours(img_lab_data, colours_lab, replaced_colours): 75 | final_img = img_lab_data.copy() 76 | breakpoint() 77 | for i in range(final_img.shape[0]): 78 | for j in range(final_img.shape[1]): 79 | final_img[i, j] = replaced_colours[colour_to_str(final_img[i, j])] 80 | breakpoint() 81 | 82 | return final_img 83 | 84 | def colour_to_str(colour): 85 | return ','.join([str(x) for x in colour]) 86 | 87 | def merge_colours(img_rgb, final_colour_number, all_colours_rgb): 88 | final_colours = [] 89 | 90 | for i in range(final_colour_number): 91 | idx = [x[0] for x in all_colours_rgb].index(max([x[0] for x in all_colours_rgb])) 92 | final_colours.append(all_colours_rgb[idx][1]) 93 | all_colours_rgb[idx][0] = -1 94 | 95 | final_img = img_rgb.copy() 96 | 97 | #TODO: check if the RGB2LAB conversion MUST occurs at the very last 98 | # or if the problems I had were because of code problem uh 99 | 100 | for i in range(final_img.shape[0]): 101 | for j in range(final_img.shape[1]): 102 | c1 = color.rgb2lab([[final_img[i, j]]])[0][0] 103 | c2 = [color.rgb2lab([[x]])[0][0] for x in final_colours] 104 | distances = [dst(c1, x) for x in c2] 105 | final_img[i, j] = final_colours[distances.index(min(distances))] 106 | 107 | return final_img 108 | 109 | def dst(c1, c2): 110 | return sqrt(pow(c2[0] - c1[0], 2) + pow(c2[1] - c1[1], 2) + pow(c2[2] - c1[2], 2)) 111 | 112 | # return : file to convert, number of color 113 | def parser(): 114 | parser = argparse.ArgumentParser(description="Reduce the color number of a given file.") 115 | parser.add_argument("filename", metavar="filename", type=str,nargs=1, help="the path of the image") 116 | parser.add_argument("final_color_number", metavar="N", type=int, nargs='?', default=4, help="the final number of colors") 117 | args = parser.parse_args() 118 | return args.filename[0], args.final_color_number 119 | 120 | def main(): 121 | filename, final_colour_number = parser() 122 | 123 | # img = cv2.imread('tortank.png') 124 | img = Image.open(filename).convert('RGB') 125 | 126 | # sRGB instead of RGB ([0, 1] instead of [0, 255]) 127 | img_rgb_data = np.asarray(img) / 255 128 | 129 | # Get all the colours in the image 130 | all_colours_rgb = img.getcolors() 131 | 132 | # Convert tuples into lists 133 | all_colours_rgb = [[x[0], np.asarray(x[1]) / 255] for x in all_colours_rgb] 134 | 135 | # NEW METHOD: 136 | # take the 4 most represented colours 137 | # compute closeness to each colour, assign the closest one 138 | final_img = merge_colours(img_rgb_data, final_colour_number, all_colours_rgb) 139 | 140 | # MUST specify that the datatype should be uint8 (float in the array), 141 | # or else it displays a weird image 142 | final_img = np.asarray(final_img * 255, 'uint8') 143 | 144 | final_img = Image.fromarray(final_img, mode='RGB') 145 | final_img.show() 146 | final_img.save('dracaufeu_reduced.png') 147 | 148 | # OLD METHOD 149 | 150 | # # k: original value 151 | # # v: replacement 152 | # replaced_colours = {} 153 | # for elem in colours_lab: 154 | # replaced_colours[colour_to_str(elem[1])] = None 155 | 156 | # while len(colours_lab) > final_colour_number: 157 | # # Compute the similarity matrix between colours 158 | # similarity_matrix = compute_similarity_matrix(colours_lab) 159 | # # Merge two colours 160 | # colours_lab, replaced_colours = merge_most_similar(similarity_matrix, colours_lab, replaced_colours, 'bright') 161 | 162 | # final_img = replace_colours(img_lab_data, colours_lab, replaced_colours) 163 | 164 | if __name__ == '__main__': 165 | main() -------------------------------------------------------------------------------- /ui/ui_funcs.py: -------------------------------------------------------------------------------- 1 | from skimage import color 2 | import numpy as np 3 | from math import floor, sqrt 4 | from progress_bar import ProgressBar 5 | from number_display import NumberDisplay 6 | from PyQt5.QtWidgets import QApplication 7 | from PyQt5 import QtGui 8 | import cv2 9 | 10 | #TODO: replace skimage rgb2lab by OpenCV RGB2Lab (way faster) 11 | def resize_image(img, max_size): 12 | size = list(np.asarray(img).shape[:2]) 13 | 14 | resized = False 15 | 16 | #TODO: sure about this? 17 | if size[0] > max_size[0] or size[1] > max_size[1]: 18 | ratio = min(max_size[1] / size[1], max_size[0] / size[0]) 19 | new_size = [0, 0] 20 | new_size[1] = int(size[0] * ratio) 21 | new_size[0] = int(size[1] * ratio) 22 | resized = True 23 | 24 | elif size[0] < max_size[0] or size[1] < max_size[1]: 25 | ratio = min(max_size[1] / size[1], max_size[0] / size[0]) 26 | new_size = [0, 0] 27 | new_size[1] = int(size[0] * ratio) 28 | new_size[0] = int(size[1] * ratio) 29 | resized = True 30 | 31 | if resized: 32 | return img.resize(new_size) 33 | 34 | return img 35 | 36 | def get_number_of_colours(img): 37 | return len(img.getcolors(img.size[0] * img.size[1])) 38 | 39 | def get_colours(img, final_colour_number): 40 | # Get all the colours in the image 41 | all_colours_rgb = img.getcolors(img.size[0] * img.size[1]) 42 | 43 | if final_colour_number > len(all_colours_rgb): 44 | final_colour_number = len(all_colours_rgb) 45 | 46 | # Convert tuples into lists 47 | all_colours_rgb = [[x[0], np.asarray(x[1])] for x in all_colours_rgb] 48 | 49 | final_colours = [] 50 | 51 | for _ in range(final_colour_number): 52 | idx = [x[0] for x in all_colours_rgb].index(max([x[0] for x in all_colours_rgb])) 53 | final_colours.append(all_colours_rgb[idx][1]) 54 | all_colours_rgb[idx][0] = -1 55 | 56 | return final_colours 57 | 58 | def reduce_colours(img_rgb, final_colours): 59 | 60 | if not isinstance(img_rgb, np.ndarray): 61 | img_rgb = np.asarray(img_rgb) / 255 62 | 63 | final_img = img_rgb.copy() 64 | 65 | # TODO: check if the RGB2LAB conversion MUST occurs at the very last 66 | # or if the problems I had were because of code problem uh 67 | 68 | pb = ProgressBar('Reducing colours') 69 | 70 | for i in range(final_img.shape[0]): 71 | for j in range(final_img.shape[1]): 72 | c1 = color.rgb2lab([[final_img[i, j]]])[0][0] 73 | c2 = [color.rgb2lab([[x]])[0][0] for x in final_colours] 74 | distances = [dst(c1, x) for x in c2] 75 | final_img[i, j] = final_colours[distances.index(min(distances))] 76 | 77 | pb.set_value(floor((((i * final_img.shape[1]) + j) / (final_img.shape[0] * final_img.shape[1])) * 100)) 78 | QApplication.processEvents() 79 | 80 | pb.close() 81 | 82 | return final_img 83 | 84 | def dst(c1, c2, colour_space='lab'): 85 | if colour_space == 'lab': 86 | return sqrt(pow(c2[0] - c1[0], 2) + pow(c2[1] - c1[1], 2) + pow(c2[2] - c1[2], 2)) 87 | elif colour_space == 'rgb': 88 | #https://www.compuphase.com/cmetric.htm 89 | return sqrt(2 * pow(c2[0] - c1[0], 2) + 4 * pow(c2[1] - c1[1], 2) + 3 * pow(c2[2] - c1[2], 2)) 90 | 91 | def get_similarity_matrix(listView_colours): 92 | list_size = listView_colours.rowCount() 93 | similarity_matrix = np.zeros((list_size, list_size)) - 1 94 | 95 | for i in range(list_size): 96 | for j in range(list_size): 97 | if i != j: 98 | c1 = np.asarray(listView_colours.item(i).background().color().getRgb()[:-1]) / 255 99 | c2 = np.asarray(listView_colours.item(j).background().color().getRgb()[:-1]) / 255 100 | similarity_matrix[i, j] = dst(color.rgb2lab([[c1]])[0][0], color.rgb2lab([[c2]])[0][0]) 101 | 102 | return similarity_matrix 103 | 104 | def merge_colours(listView_colours, threshold): 105 | similarity_matrix = get_similarity_matrix(listView_colours) 106 | 107 | all_to_merge = np.where(np.logical_and(similarity_matrix > 0, similarity_matrix < threshold)) 108 | 109 | number_display_window = NumberDisplay('Merging colours') 110 | counter = 0 111 | 112 | while len(all_to_merge[0]) > 0: 113 | # Get the first one to merge 114 | to_merge = (all_to_merge[0][0], all_to_merge[1][0]) 115 | 116 | # Get colour as RGB values in range [0;1] 117 | c1 = np.asarray(listView_colours.item(to_merge[0]).background().color().getRgb()[:-1]) / 255 118 | c2 = np.asarray(listView_colours.item(to_merge[1]).background().color().getRgb()[:-1]) / 255 119 | 120 | # Get colour as LAB 121 | c1 = color.rgb2lab([[c1]])[0][0] 122 | c2 = color.rgb2lab([[c2]])[0][0] 123 | 124 | # Is this a godd way to merge colours? 125 | new_colour_lab = (c1 + c2) / 2.0 126 | 127 | nc = color.lab2rgb([[new_colour_lab]])[0][0] * 255 128 | nc = QtGui.QColor(int(nc[0]), int(nc[1]), int(nc[2])) 129 | 130 | listView_colours.item(to_merge[0]).setBackground(QtGui.QColor(nc)) 131 | listView_colours.removeRow(to_merge[1]) 132 | 133 | counter += 1 134 | number_display_window.set_value(counter) 135 | QApplication.processEvents() 136 | 137 | similarity_matrix = get_similarity_matrix(listView_colours) 138 | all_to_merge = np.where(np.logical_and(similarity_matrix > 0, similarity_matrix < threshold)) 139 | 140 | number_display_window.close() 141 | 142 | def convert_rgb_to_numpy_array(colours_list): 143 | for colour in colours_list: 144 | colour['RGB'] = np.asarray(colour['RGB']) / 255 145 | 146 | def add_Lab(colours_list): 147 | for colour in colours_list: 148 | rgb_colour = np.array(colour['RGB'], dtype=np.float32) 149 | colour_to_lab = cv2.cvtColor(np.asarray([[rgb_colour]]), cv2.COLOR_RGB2Lab)[0][0] 150 | colour['Lab'] = colour_to_lab 151 | 152 | def get_closest_colour(rgb_colour, colour_corres_list, colour_space='lab'): 153 | closest = [99999, None, None] 154 | 155 | if colour_space == 'lab': 156 | 157 | lab_colour = cv2.cvtColor(np.asarray([[rgb_colour]], dtype='float32') / 255, cv2.COLOR_RGB2Lab)[0][0] 158 | 159 | for colour_in_list in colour_corres_list: 160 | distance = dst(lab_colour, colour_in_list['Lab']) 161 | if distance < closest[0]: 162 | closest[0] = distance 163 | closest[1] = colour_in_list 164 | 165 | elif colour_space == 'rgb': 166 | 167 | for colour_in_list in colour_corres_list: 168 | distance = dst(rgb_colour, np.asarray((colour_in_list['RGB'] * 255), dtype='uint8')) 169 | if distance < closest[0]: 170 | closest[0] = distance 171 | closest[1] = colour_in_list 172 | 173 | closest[2] = luminance(np.asarray(closest[1]['RGB'])) 174 | return closest 175 | 176 | def to_dmc_colours(colours_list, colour_corres_list, colour_space='lab'): 177 | new_colours = [] 178 | 179 | for colour in colours_list: 180 | dmc_colour = get_closest_colour(colour, colour_corres_list, colour_space) 181 | if dmc_colour not in new_colours: 182 | new_colours.append(dmc_colour[1]['RGB'] * 255) 183 | 184 | return new_colours 185 | 186 | def luminance(colour): 187 | """ 188 | Compute the luminance of a colour, and return the adequate colour 189 | to write on the initial one (black or white). 190 | """ 191 | if (0.299 * colour[0] + 0.587 * colour[1] + 0.114 * colour[2]) > 0.5: 192 | return np.array([0, 0, 0]) 193 | 194 | return np.array([1, 1, 1]) 195 | -------------------------------------------------------------------------------- /ui/pixel_perfect.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'ui\pixel_perfect.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.11.3 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | from math import floor 12 | from skimage import color 13 | import numpy as np 14 | from PIL import Image, ImageQt 15 | from ui_funcs import resize_image 16 | from cv2 import cvtColor, COLOR_RGB2Lab, COLOR_Lab2RGB 17 | 18 | class Ui_Dialog(object): 19 | def setupUi(self, Dialog): 20 | Dialog.setObjectName("Dialog") 21 | Dialog.resize(574, 417) 22 | self.label_img = QtWidgets.QLabel(Dialog) 23 | self.label_img.setGeometry(QtCore.QRect(10, 10, 361, 361)) 24 | self.label_img.setText("") 25 | self.label_img.setObjectName("label_img") 26 | self.pushButton_go = QtWidgets.QPushButton(Dialog) 27 | self.pushButton_go.setGeometry(QtCore.QRect(380, 220, 81, 28)) 28 | self.pushButton_go.setObjectName("pushButton_go") 29 | self.pushButton_validate = QtWidgets.QPushButton(Dialog) 30 | self.pushButton_validate.setGeometry(QtCore.QRect(380, 280, 81, 28)) 31 | self.pushButton_validate.setObjectName("pushButton_validate") 32 | self.pushButton_cancel = QtWidgets.QPushButton(Dialog) 33 | self.pushButton_cancel.setGeometry(QtCore.QRect(480, 280, 81, 28)) 34 | self.pushButton_cancel.setObjectName("pushButton_cancel") 35 | self.pushButton_reset = QtWidgets.QPushButton(Dialog) 36 | self.pushButton_reset.setGeometry(QtCore.QRect(482, 220, 81, 28)) 37 | self.pushButton_reset.setObjectName("pushButton_reset") 38 | self.groupBox_pixelisation_type = QtWidgets.QGroupBox(Dialog) 39 | self.groupBox_pixelisation_type.setGeometry(QtCore.QRect(380, 130, 181, 80)) 40 | self.groupBox_pixelisation_type.setObjectName("groupBox_pixelisation_type") 41 | self.radioButton_common_colour = QtWidgets.QRadioButton(self.groupBox_pixelisation_type) 42 | self.radioButton_common_colour.setGeometry(QtCore.QRect(10, 20, 151, 20)) 43 | self.radioButton_common_colour.setObjectName("radioButton_common_colour") 44 | self.radioButton_colours_average = QtWidgets.QRadioButton(self.groupBox_pixelisation_type) 45 | self.radioButton_colours_average.setGeometry(QtCore.QRect(10, 50, 141, 20)) 46 | self.radioButton_colours_average.setObjectName("radioButton_colours_average") 47 | self.groupBox_pixels = QtWidgets.QGroupBox(Dialog) 48 | self.groupBox_pixels.setGeometry(QtCore.QRect(380, 10, 181, 111)) 49 | self.groupBox_pixels.setObjectName("groupBox_pixels") 50 | self.label_pixel_size = QtWidgets.QLabel(self.groupBox_pixels) 51 | self.label_pixel_size.setGeometry(QtCore.QRect(10, 20, 55, 16)) 52 | self.label_pixel_size.setObjectName("label_pixel_size") 53 | self.spinBox_pixel_size = QtWidgets.QSpinBox(self.groupBox_pixels) 54 | self.spinBox_pixel_size.setGeometry(QtCore.QRect(110, 20, 61, 22)) 55 | self.spinBox_pixel_size.setMinimum(1) 56 | self.spinBox_pixel_size.setMaximum(999999) 57 | self.spinBox_pixel_size.setObjectName("spinBox_pixel_size") 58 | self.label_offset_pixel_grid = QtWidgets.QLabel(self.groupBox_pixels) 59 | self.label_offset_pixel_grid.setGeometry(QtCore.QRect(10, 50, 101, 16)) 60 | self.label_offset_pixel_grid.setObjectName("label_offset_pixel_grid") 61 | self.spinBox_offset_pixel_grid = QtWidgets.QSpinBox(self.groupBox_pixels) 62 | self.spinBox_offset_pixel_grid.setGeometry(QtCore.QRect(110, 50, 61, 22)) 63 | self.spinBox_offset_pixel_grid.setObjectName("spinBox_offset_pixel_grid") 64 | self.checkBox_display_grid = QtWidgets.QCheckBox(self.groupBox_pixels) 65 | self.checkBox_display_grid.setGeometry(QtCore.QRect(10, 80, 91, 20)) 66 | self.checkBox_display_grid.setObjectName("checkBox_display_grid") 67 | 68 | self.retranslateUi(Dialog) 69 | QtCore.QMetaObject.connectSlotsByName(Dialog) 70 | 71 | def retranslateUi(self, Dialog): 72 | _translate = QtCore.QCoreApplication.translate 73 | Dialog.setWindowTitle(_translate("Dialog", "Dialog")) 74 | self.pushButton_go.setText(_translate("Dialog", "Go")) 75 | self.pushButton_validate.setText(_translate("Dialog", "Validate")) 76 | self.pushButton_cancel.setText(_translate("Dialog", "Cancel")) 77 | self.pushButton_reset.setText(_translate("Dialog", "Reset")) 78 | self.groupBox_pixelisation_type.setTitle(_translate("Dialog", "Pixelisation type")) 79 | self.radioButton_common_colour.setText(_translate("Dialog", "Most common colour")) 80 | self.radioButton_colours_average.setText(_translate("Dialog", "Colours\' average")) 81 | self.groupBox_pixels.setTitle(_translate("Dialog", "Pixels")) 82 | self.label_pixel_size.setText(_translate("Dialog", "Pixel size:")) 83 | self.label_offset_pixel_grid.setText(_translate("Dialog", "Offset pixel grid:")) 84 | self.checkBox_display_grid.setText(_translate("Dialog", "Display grid")) 85 | 86 | class PixelPerfect(QtWidgets.QDialog, Ui_Dialog): 87 | def __init__(self, original_image=None, name=None, parent=None): 88 | super(PixelPerfect, self).__init__(parent) 89 | self.setupUi(self) 90 | self.show() 91 | self.original_image = original_image 92 | 93 | self.label_size = (self.label_img.size().height(), self.label_img.size().width()) 94 | 95 | self.resized_original_image = original_image.copy() 96 | self.resized_original_image = resize_image(self.resized_original_image, self.label_size) 97 | self.qimage_resized_original = None 98 | self.pixel_perfected_image = None 99 | self.resized_pixel_perfected_image = None 100 | self.qimage_resized_pixel_perfected = None 101 | 102 | self.displayed_image = None 103 | 104 | self.grid_overlay = None 105 | 106 | self.link_components() 107 | 108 | self.display_original_image() 109 | 110 | 111 | def link_components(self): 112 | self.pushButton_cancel.clicked.connect(self.close_window) 113 | self.pushButton_validate.clicked.connect(self.return_image) 114 | self.pushButton_reset.clicked.connect(self.display_original_image) 115 | self.pushButton_go.clicked.connect(self.pixelate_image) 116 | 117 | def close_window(self): 118 | self.close() 119 | 120 | def reset_pixel_perfected_image(self): 121 | self.pixel_perfected_image = self.original_image.copy() 122 | 123 | def display_original_image(self): 124 | self.qimage_resized_original = ImageQt.ImageQt(self.resized_original_image) 125 | self.displayed_image = QtGui.QPixmap.fromImage(self.qimage_resized_original) 126 | self.label_img.setPixmap(self.displayed_image) 127 | 128 | def create_pixel_grid(self): 129 | grid_size = self.spinBox_pixel_size.value() 130 | 131 | self.grid_overlay = None 132 | self.grid_overlay = np.zeros((self.label_size[0], self.label_size[1], 4), dtype='uint8') 133 | 134 | for i in range(floor(self.label_size[0] / grid_size)): 135 | for j in range(floor(self.label_size[1] / grid_size)): 136 | 137 | colour = np.array([200, 200, 200, 127]).astype('uint8') 138 | if i % 2 == 0 and j % 2 == 0: 139 | colour = np.array([175, 175, 175, 127]).astype('uint8') 140 | 141 | for x in range(i * grid_size, (i * grid_size) + grid_size + 1): 142 | for y in range(j * grid_size, (j * grid_size) + grid_size + 1): 143 | self.grid_overlay[x, y] = colour 144 | 145 | print(self.grid_overlay) 146 | self.grid_overlay = Image.fromarray(self.grid_overlay, mode='RGBA') 147 | self.grid_overlay = ImageQt.ImageQt(self.grid_overlay) 148 | self.grid_overlay = QtGui.QPixmap.fromImage(self.grid_overlay) 149 | 150 | 151 | 152 | def pixelate_image(self): 153 | grid_offset = int(self.spinBox_offset_pixel_grid.value()) 154 | 155 | pixel_size = int(self.spinBox_pixel_size.value()) 156 | x_size = floor((self.original_image.size[0] - grid_offset) / pixel_size) 157 | y_size = floor((self.original_image.size[1] - grid_offset) / pixel_size) 158 | 159 | 160 | self.reset_pixel_perfected_image() 161 | img_as_array = np.array(self.pixel_perfected_image) 162 | 163 | for y in range(y_size): 164 | for x in range(x_size): 165 | sub_image = self.pixel_perfected_image.crop((x * pixel_size + grid_offset, 166 | y * pixel_size + grid_offset, 167 | (x * pixel_size) + pixel_size + grid_offset, 168 | (y * pixel_size) + pixel_size + grid_offset)) 169 | 170 | colours = sub_image.getcolors(sub_image.size[0] * sub_image.size[1]) 171 | 172 | # Pink debugging default colour 173 | final_colour = (255, 0, 255) 174 | if self.radioButton_common_colour.isChecked(): 175 | final_colour = max(colours, key=lambda x: x[0])[1] 176 | 177 | elif self.radioButton_colours_average.isChecked(): 178 | for i, colour in enumerate(colours): 179 | colours[i] = cvtColor(np.asarray([[colour[1]]], dtype='float32') / 255, COLOR_RGB2Lab) 180 | 181 | final_colour = sum(colours) / len(colours) 182 | final_colour = cvtColor(final_colour, COLOR_Lab2RGB) * 255 183 | 184 | img_as_array[y * pixel_size + grid_offset: (y * pixel_size) + pixel_size + grid_offset, 185 | x * pixel_size + grid_offset: (x * pixel_size) + pixel_size + grid_offset] = final_colour 186 | 187 | 188 | self.pixel_perfected_image = Image.fromarray(img_as_array, mode='RGB') 189 | self.resized_pixel_perfected_image = resize_image(self.pixel_perfected_image, self.label_size) 190 | self.qimage_resized_pixel_perfected = ImageQt.ImageQt(self.resized_pixel_perfected_image) 191 | self.displayed_image = QtGui.QPixmap.fromImage(self.qimage_resized_pixel_perfected) 192 | self.label_img.setPixmap(self.displayed_image) 193 | 194 | def return_image(self): 195 | self.close_window() 196 | 197 | -------------------------------------------------------------------------------- /ui/main.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 881 10 | 609 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 10 21 | 10 22 | 93 23 | 28 24 | 25 | 26 | 27 | Import 28 | 29 | 30 | 31 | 32 | 33 | 120 34 | 15 35 | 241 36 | 21 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 10 47 | 90 48 | 361 49 | 361 50 | 51 | 52 | 53 | 54 | 55 | 56 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 57 | 58 | 59 | 60 | 61 | 62 | 550 63 | 30 64 | 42 65 | 22 66 | 67 | 68 | 69 | 70 | 71 | 72 | 390 73 | 30 74 | 141 75 | 20 76 | 77 | 78 | 79 | Final number of colours: 80 | 81 | 82 | 83 | 84 | 85 | 600 86 | 30 87 | 51 88 | 28 89 | 90 | 91 | 92 | Ok 93 | 94 | 95 | 96 | 97 | 98 | 370 99 | 20 100 | 20 101 | 511 102 | 103 | 104 | 105 | Qt::Vertical 106 | 107 | 108 | 109 | 110 | 111 | 530 112 | 90 113 | 341 114 | 461 115 | 116 | 117 | 118 | 119 | 120 | 121 | 390 122 | 490 123 | 93 124 | 28 125 | 126 | 127 | 128 | Go! 129 | 130 | 131 | 132 | 133 | 134 | 10 135 | 460 136 | 93 137 | 28 138 | 139 | 140 | 141 | Reset 142 | 143 | 144 | 145 | 146 | 147 | 390 148 | 10 149 | 191 150 | 16 151 | 152 | 153 | 154 | Total Number of colours: 155 | 156 | 157 | 158 | 159 | 160 | 110 161 | 480 162 | 101 163 | 21 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 390 174 | 520 175 | 93 176 | 28 177 | 178 | 179 | 180 | Save 181 | 182 | 183 | 184 | 185 | 186 | 10 187 | 520 188 | 93 189 | 28 190 | 191 | 192 | 193 | Pixel Perfect 194 | 195 | 196 | 197 | 198 | 199 | 250 200 | 460 201 | 120 202 | 91 203 | 204 | 205 | 206 | Mouse colour 207 | 208 | 209 | 210 | 211 | 10 212 | 20 213 | 71 214 | 21 215 | 216 | 217 | 218 | 219 | 220 | 221 | 10 222 | 40 223 | 95 224 | 20 225 | 226 | 227 | 228 | Add 229 | 230 | 231 | true 232 | 233 | 234 | 235 | 236 | 237 | 10 238 | 60 239 | 95 240 | 20 241 | 242 | 243 | 244 | Change 245 | 246 | 247 | 248 | 249 | 250 | 251 | 530 252 | 50 253 | 331 254 | 31 255 | 256 | 257 | 258 | 259 | 10 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 390 270 | 90 271 | 131 272 | 121 273 | 274 | 275 | 276 | Colours 277 | 278 | 279 | 280 | 281 | 20 282 | 20 283 | 93 284 | 28 285 | 286 | 287 | 288 | Add colour 289 | 290 | 291 | 292 | 293 | 294 | 20 295 | 80 296 | 93 297 | 28 298 | 299 | 300 | 301 | Delete 302 | 303 | 304 | 305 | 306 | 307 | 20 308 | 50 309 | 93 310 | 28 311 | 312 | 313 | 314 | Change colour 315 | 316 | 317 | 318 | 319 | 320 | 321 | 390 322 | 220 323 | 131 324 | 91 325 | 326 | 327 | 328 | Auto-merging 329 | 330 | 331 | 332 | 333 | 10 334 | 50 335 | 93 336 | 28 337 | 338 | 339 | 340 | Merge 341 | 342 | 343 | 344 | 345 | 346 | 80 347 | 20 348 | 42 349 | 22 350 | 351 | 352 | 353 | 354 | 355 | 356 | 10 357 | 20 358 | 71 359 | 16 360 | 361 | 362 | 363 | Threshold: 364 | 365 | 366 | 367 | 368 | 369 | 370 | 390 371 | 320 372 | 120 373 | 101 374 | 375 | 376 | 377 | To DMC colours 378 | 379 | 380 | 381 | 382 | 10 383 | 20 384 | 95 385 | 20 386 | 387 | 388 | 389 | L*a*b* 390 | 391 | 392 | true 393 | 394 | 395 | 396 | 397 | 398 | 10 399 | 40 400 | 95 401 | 20 402 | 403 | 404 | 405 | RGB 406 | 407 | 408 | 409 | 410 | 411 | 10 412 | 60 413 | 93 414 | 28 415 | 416 | 417 | 418 | To DMC colour 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 0 427 | 0 428 | 881 429 | 26 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | -------------------------------------------------------------------------------- /ui/main_gui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'main.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.11.3 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | import json 12 | 13 | from math import floor 14 | from PIL import Image, ImageQt 15 | import numpy as np 16 | 17 | import ui_funcs 18 | from pixel_perfect import PixelPerfect 19 | 20 | class Ui_MainWindow(object): 21 | def __init__(self): 22 | self.path_to_img = None 23 | self.initial_img = None 24 | self.resized_initial_img = None 25 | self.new_img = None 26 | self.resized_new_img = None 27 | self.colours_to_display = [] 28 | self.final_colour_number = 0 29 | self.model_listView_choosen_colours = None 30 | self.model_listView_mouse_colour = None 31 | self.pixel_perfect = None 32 | self.colour_corres_list = None 33 | 34 | try: 35 | with open('res/colours.json', 'r') as json_colours_file: 36 | self.colour_corres_list = json.load(json_colours_file) 37 | except FileNotFoundError: 38 | try: 39 | with open('../res/colours.json', 'r') as json_colours_file: 40 | self.colour_corres_list = json.load(json_colours_file) 41 | except FileNotFoundError: 42 | print('ERROR: can\'t find colour.json file. Are you sure the file is located in the folder (root or ui/)? Exiting...') 43 | return 44 | 45 | ui_funcs.convert_rgb_to_numpy_array(self.colour_corres_list) 46 | ui_funcs.add_Lab(self.colour_corres_list) 47 | 48 | def setupUi(self, MainWindow): 49 | MainWindow.setObjectName("MainWindow") 50 | MainWindow.resize(881, 609) 51 | self.centralwidget = QtWidgets.QWidget(MainWindow) 52 | self.centralwidget.setObjectName("centralwidget") 53 | self.pushButton_import = QtWidgets.QPushButton(self.centralwidget) 54 | self.pushButton_import.setGeometry(QtCore.QRect(10, 10, 93, 28)) 55 | self.pushButton_import.setObjectName("pushButton_import") 56 | self.label_import_name = QtWidgets.QLabel(self.centralwidget) 57 | self.label_import_name.setGeometry(QtCore.QRect(120, 15, 241, 21)) 58 | self.label_import_name.setText("") 59 | self.label_import_name.setObjectName("label_import_name") 60 | self.label_original_image = QtWidgets.QLabel(self.centralwidget) 61 | self.label_original_image.setGeometry(QtCore.QRect(10, 90, 361, 361)) 62 | self.label_original_image.setText("") 63 | self.label_original_image.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) 64 | self.label_original_image.setObjectName("label_original_image") 65 | self.spinBox_n_colours = QtWidgets.QSpinBox(self.centralwidget) 66 | self.spinBox_n_colours.setGeometry(QtCore.QRect(550, 30, 42, 22)) 67 | self.spinBox_n_colours.setObjectName("spinBox_n_colours") 68 | self.label_n_colours = QtWidgets.QLabel(self.centralwidget) 69 | self.label_n_colours.setGeometry(QtCore.QRect(390, 30, 141, 20)) 70 | self.label_n_colours.setObjectName("label_n_colours") 71 | self.pushButton_n_colours = QtWidgets.QPushButton(self.centralwidget) 72 | self.pushButton_n_colours.setGeometry(QtCore.QRect(600, 30, 51, 28)) 73 | self.pushButton_n_colours.setObjectName("pushButton_n_colours") 74 | self.line = QtWidgets.QFrame(self.centralwidget) 75 | self.line.setGeometry(QtCore.QRect(370, 20, 20, 511)) 76 | self.line.setFrameShape(QtWidgets.QFrame.VLine) 77 | self.line.setFrameShadow(QtWidgets.QFrame.Sunken) 78 | self.line.setObjectName("line") 79 | self.listView_choosen_colours = QtWidgets.QListView(self.centralwidget) 80 | self.listView_choosen_colours.setGeometry(QtCore.QRect(530, 90, 341, 461)) 81 | self.listView_choosen_colours.setObjectName("listView_choosen_colours") 82 | self.pushButton_go = QtWidgets.QPushButton(self.centralwidget) 83 | self.pushButton_go.setGeometry(QtCore.QRect(390, 490, 93, 28)) 84 | self.pushButton_go.setObjectName("pushButton_go") 85 | self.pushButton_reset = QtWidgets.QPushButton(self.centralwidget) 86 | self.pushButton_reset.setGeometry(QtCore.QRect(10, 460, 93, 28)) 87 | self.pushButton_reset.setObjectName("pushButton_reset") 88 | self.label_total_number_of_colour = QtWidgets.QLabel(self.centralwidget) 89 | self.label_total_number_of_colour.setGeometry(QtCore.QRect(390, 10, 191, 16)) 90 | self.label_total_number_of_colour.setObjectName("label_total_number_of_colour") 91 | self.label_image_generation = QtWidgets.QLabel(self.centralwidget) 92 | self.label_image_generation.setGeometry(QtCore.QRect(110, 480, 101, 21)) 93 | self.label_image_generation.setText("") 94 | self.label_image_generation.setObjectName("label_image_generation") 95 | self.pushButton_save = QtWidgets.QPushButton(self.centralwidget) 96 | self.pushButton_save.setGeometry(QtCore.QRect(390, 520, 93, 28)) 97 | self.pushButton_save.setObjectName("pushButton_save") 98 | self.pushButton_pixel_perfect = QtWidgets.QPushButton(self.centralwidget) 99 | self.pushButton_pixel_perfect.setGeometry(QtCore.QRect(10, 520, 93, 28)) 100 | self.pushButton_pixel_perfect.setObjectName("pushButton_pixel_perfect") 101 | self.groupBox_mouse_colour = QtWidgets.QGroupBox(self.centralwidget) 102 | self.groupBox_mouse_colour.setGeometry(QtCore.QRect(250, 460, 120, 91)) 103 | self.groupBox_mouse_colour.setObjectName("groupBox_mouse_colour") 104 | self.listView_mouse_colour = QtWidgets.QListView(self.groupBox_mouse_colour) 105 | self.listView_mouse_colour.setGeometry(QtCore.QRect(10, 20, 71, 21)) 106 | self.listView_mouse_colour.setObjectName("listView_mouse_colour") 107 | self.radioButton_add = QtWidgets.QRadioButton(self.groupBox_mouse_colour) 108 | self.radioButton_add.setGeometry(QtCore.QRect(10, 40, 95, 20)) 109 | self.radioButton_add.setChecked(True) 110 | self.radioButton_add.setObjectName("radioButton_add") 111 | self.radioButton_change = QtWidgets.QRadioButton(self.groupBox_mouse_colour) 112 | self.radioButton_change.setGeometry(QtCore.QRect(10, 60, 95, 20)) 113 | self.radioButton_change.setObjectName("radioButton_change") 114 | self.label_already_in_list = QtWidgets.QLabel(self.centralwidget) 115 | self.label_already_in_list.setGeometry(QtCore.QRect(530, 50, 331, 31)) 116 | font = QtGui.QFont() 117 | font.setPointSize(10) 118 | self.label_already_in_list.setFont(font) 119 | self.label_already_in_list.setText("") 120 | self.label_already_in_list.setObjectName("label_already_in_list") 121 | self.groupBox_colours = QtWidgets.QGroupBox(self.centralwidget) 122 | self.groupBox_colours.setGeometry(QtCore.QRect(390, 90, 131, 121)) 123 | self.groupBox_colours.setObjectName("groupBox_colours") 124 | self.pushButton_add_colour = QtWidgets.QPushButton(self.groupBox_colours) 125 | self.pushButton_add_colour.setGeometry(QtCore.QRect(20, 20, 93, 28)) 126 | self.pushButton_add_colour.setObjectName("pushButton_add_colour") 127 | self.pushButton_delete_colour = QtWidgets.QPushButton(self.groupBox_colours) 128 | self.pushButton_delete_colour.setGeometry(QtCore.QRect(20, 80, 93, 28)) 129 | self.pushButton_delete_colour.setObjectName("pushButton_delete_colour") 130 | self.pushButton_change_colour = QtWidgets.QPushButton(self.groupBox_colours) 131 | self.pushButton_change_colour.setGeometry(QtCore.QRect(20, 50, 93, 28)) 132 | self.pushButton_change_colour.setObjectName("pushButton_change_colour") 133 | self.groupBox_auto_merging = QtWidgets.QGroupBox(self.centralwidget) 134 | self.groupBox_auto_merging.setGeometry(QtCore.QRect(390, 220, 131, 91)) 135 | self.groupBox_auto_merging.setObjectName("groupBox_auto_merging") 136 | self.pushButton_merge_colours = QtWidgets.QPushButton(self.groupBox_auto_merging) 137 | self.pushButton_merge_colours.setGeometry(QtCore.QRect(10, 50, 93, 28)) 138 | self.pushButton_merge_colours.setObjectName("pushButton_merge_colours") 139 | self.spinBox_merge_colours = QtWidgets.QSpinBox(self.groupBox_auto_merging) 140 | self.spinBox_merge_colours.setGeometry(QtCore.QRect(80, 20, 42, 22)) 141 | self.spinBox_merge_colours.setObjectName("spinBox_merge_colours") 142 | self.label_colours_merging = QtWidgets.QLabel(self.groupBox_auto_merging) 143 | self.label_colours_merging.setGeometry(QtCore.QRect(10, 20, 71, 16)) 144 | self.label_colours_merging.setObjectName("label_colours_merging") 145 | self.groupBox_to_dmc = QtWidgets.QGroupBox(self.centralwidget) 146 | self.groupBox_to_dmc.setGeometry(QtCore.QRect(390, 320, 120, 101)) 147 | self.groupBox_to_dmc.setObjectName("groupBox_to_dmc") 148 | self.radioButton_to_dmc_lab = QtWidgets.QRadioButton(self.groupBox_to_dmc) 149 | self.radioButton_to_dmc_lab.setGeometry(QtCore.QRect(10, 20, 95, 20)) 150 | self.radioButton_to_dmc_lab.setChecked(True) 151 | self.radioButton_to_dmc_lab.setObjectName("radioButton_to_dmc_lab") 152 | self.radioButton_to_dmc_rgb = QtWidgets.QRadioButton(self.groupBox_to_dmc) 153 | self.radioButton_to_dmc_rgb.setGeometry(QtCore.QRect(10, 40, 95, 20)) 154 | self.radioButton_to_dmc_rgb.setObjectName("radioButton_to_dmc_rgb") 155 | self.pushButton_to_dmc_colours = QtWidgets.QPushButton(self.groupBox_to_dmc) 156 | self.pushButton_to_dmc_colours.setGeometry(QtCore.QRect(10, 60, 93, 28)) 157 | self.pushButton_to_dmc_colours.setObjectName("pushButton_to_dmc_colours") 158 | MainWindow.setCentralWidget(self.centralwidget) 159 | self.menubar = QtWidgets.QMenuBar(MainWindow) 160 | self.menubar.setGeometry(QtCore.QRect(0, 0, 739, 26)) 161 | self.menubar.setObjectName("menubar") 162 | MainWindow.setMenuBar(self.menubar) 163 | self.statusbar = QtWidgets.QStatusBar(MainWindow) 164 | self.statusbar.setObjectName("statusbar") 165 | MainWindow.setStatusBar(self.statusbar) 166 | 167 | self.retranslateUi(MainWindow) 168 | QtCore.QMetaObject.connectSlotsByName(MainWindow) 169 | 170 | self.initialise() 171 | self.link_components() 172 | 173 | def retranslateUi(self, MainWindow): 174 | _translate = QtCore.QCoreApplication.translate 175 | MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow")) 176 | self.pushButton_import.setText(_translate("MainWindow", "Import")) 177 | self.label_n_colours.setText(_translate("MainWindow", "Final number of colours:")) 178 | self.pushButton_n_colours.setText(_translate("MainWindow", "Ok")) 179 | self.pushButton_go.setText(_translate("MainWindow", "Go!")) 180 | self.pushButton_reset.setText(_translate("MainWindow", "Reset")) 181 | self.label_total_number_of_colour.setText(_translate("MainWindow", "Total Number of colours:")) 182 | self.pushButton_save.setText(_translate("MainWindow", "Save")) 183 | self.pushButton_pixel_perfect.setText(_translate("MainWindow", "Pixel Perfect")) 184 | self.groupBox_mouse_colour.setTitle(_translate("MainWindow", "Mouse colour")) 185 | self.radioButton_add.setText(_translate("MainWindow", "Add")) 186 | self.radioButton_change.setText(_translate("MainWindow", "Change")) 187 | self.groupBox_colours.setTitle(_translate("MainWindow", "Colours")) 188 | self.pushButton_add_colour.setText(_translate("MainWindow", "Add colour")) 189 | self.pushButton_delete_colour.setText(_translate("MainWindow", "Delete")) 190 | self.pushButton_change_colour.setText(_translate("MainWindow", "Change colour")) 191 | self.groupBox_auto_merging.setTitle(_translate("MainWindow", "Auto-merging")) 192 | self.pushButton_merge_colours.setText(_translate("MainWindow", "Merge")) 193 | self.label_colours_merging.setText(_translate("MainWindow", "Threshold:")) 194 | self.groupBox_to_dmc.setTitle(_translate("MainWindow", "To DMC colours")) 195 | self.radioButton_to_dmc_lab.setText(_translate("MainWindow", "L*a*b*")) 196 | self.radioButton_to_dmc_rgb.setText(_translate("MainWindow", "RGB")) 197 | self.pushButton_to_dmc_colours.setText(_translate("MainWindow", "To DMC colour")) 198 | 199 | def initialise(self): 200 | self.model_listView_choosen_colours = QtGui.QStandardItemModel(self.listView_choosen_colours) 201 | self.listView_choosen_colours.setModel(self.model_listView_choosen_colours) 202 | self.model_listView_mouse_colour = QtGui.QStandardItemModel(self.listView_mouse_colour) 203 | self.listView_mouse_colour.setModel(self.model_listView_mouse_colour) 204 | 205 | self.hide_all() 206 | 207 | def hide_all(self): 208 | self.label_n_colours.hide() 209 | self.pushButton_n_colours.hide() 210 | self.label_already_in_list.hide() 211 | self.pushButton_go.hide() 212 | self.pushButton_reset.hide() 213 | self.label_total_number_of_colour.hide() 214 | self.pushButton_save.hide() 215 | self.groupBox_colours.hide() 216 | self.listView_choosen_colours.hide() 217 | self.listView_mouse_colour.hide() 218 | self.spinBox_n_colours.hide() 219 | self.groupBox_auto_merging.hide() 220 | self.pushButton_pixel_perfect.hide() 221 | self.groupBox_mouse_colour.hide() 222 | self.groupBox_to_dmc.hide() 223 | 224 | def show_all(self): 225 | self.label_n_colours.show() 226 | self.pushButton_n_colours.show() 227 | self.label_already_in_list.show() 228 | self.pushButton_go.show() 229 | self.pushButton_reset.show() 230 | self.label_total_number_of_colour.show() 231 | self.pushButton_save.show() 232 | self.groupBox_colours.show() 233 | self.listView_choosen_colours.show() 234 | self.listView_mouse_colour.show() 235 | self.spinBox_n_colours.show() 236 | self.groupBox_auto_merging.show() 237 | self.pushButton_pixel_perfect.show() 238 | self.groupBox_mouse_colour.show() 239 | self.groupBox_to_dmc.show() 240 | 241 | 242 | def link_components(self): 243 | self.pushButton_import.clicked.connect(self.select_file) 244 | self.pushButton_n_colours.clicked.connect(self.get_colours) 245 | self.pushButton_go.clicked.connect(self.generate_output) 246 | self.pushButton_reset.clicked.connect(self.reset_displayed_image) 247 | self.pushButton_save.clicked.connect(self.save_output_image) 248 | self.pushButton_add_colour.clicked.connect(self.add_colour_from_picker) 249 | self.pushButton_delete_colour.clicked.connect(self.delete_colour) 250 | self.pushButton_change_colour.clicked.connect(self.change_colour_from_picker) 251 | self.label_original_image.mousePressEvent = self.get_colour_under_mouse 252 | self.pushButton_merge_colours.clicked.connect(self.merge_colours) 253 | self.pushButton_pixel_perfect.clicked.connect(self.open_pixel_perfect) 254 | self.pushButton_to_dmc_colours.clicked.connect(self.to_dmc_colours) 255 | 256 | def select_file(self): 257 | string = QtWidgets.QFileDialog.getOpenFileName(filter="Image Files (*.png *.jpg *.bmp)") 258 | self.path_to_img = string[0] 259 | 260 | if not self.path_to_img: 261 | return 262 | 263 | self.label_import_name.setText(self.path_to_img.split('/')[-1]) 264 | 265 | self.initial_img = Image.open(self.path_to_img).convert('RGB') 266 | 267 | label_size = (self.label_original_image.size().height(), self.label_original_image.size().width()) 268 | 269 | self.resized_initial_img = ui_funcs.resize_image(self.initial_img, label_size) 270 | 271 | self.resized_initial_img = ImageQt.ImageQt(self.resized_initial_img) 272 | reference_img = QtGui.QPixmap.fromImage(self.resized_initial_img) 273 | self.label_original_image.setPixmap(reference_img) 274 | 275 | self.label_image_generation.setText('') 276 | self.label_total_number_of_colour.setText(f"Total number of colours: {ui_funcs.get_number_of_colours(self.initial_img)}") 277 | 278 | self.show_all() 279 | 280 | def open_pixel_perfect(self): 281 | if self.new_img is not None: 282 | self.pixel_perfect = PixelPerfect(self.new_img) 283 | else: 284 | self.pixel_perfect = PixelPerfect(self.initial_img) 285 | 286 | def check_if_colour_exists(self, colour): 287 | for index in range(self.model_listView_choosen_colours.rowCount()): 288 | if colour == self.model_listView_choosen_colours.item(index).background().color().getRgb()[:-1]: 289 | return True 290 | return False 291 | 292 | def add_colour_to_listview(self, colour_to_add): 293 | if isinstance(colour_to_add, QtGui.QColor): 294 | colour_to_add = colour_to_add.getRgb()[:-1] 295 | 296 | colour_to_add = tuple(colour_to_add) 297 | 298 | if self.check_if_colour_exists(colour_to_add): 299 | self.colour_already_in_list(colour_to_add) 300 | return 301 | 302 | closest = ui_funcs.get_closest_colour(colour_to_add, self.colour_corres_list) 303 | rgb_value = f'({int(colour_to_add[0])},{int(colour_to_add[1])},{int(colour_to_add[2])})' 304 | item = QtGui.QStandardItem(f'{closest[1]["Floss"]} {closest[1]["DMC Name"]} {rgb_value}') 305 | 306 | item.setForeground((QtGui.QColor(int(closest[2][0] * 255), 307 | int(closest[2][1] * 255), 308 | int(closest[2][2] * 255)))) 309 | 310 | item.setBackground(QtGui.QColor(int(colour_to_add[0]), 311 | int(colour_to_add[1]), 312 | int(colour_to_add[2]))) 313 | self.model_listView_choosen_colours.appendRow(item) 314 | 315 | def get_colours(self): 316 | self.model_listView_choosen_colours.clear() 317 | self.final_colour_number = self.spinBox_n_colours.value() 318 | self.colours_to_display = ui_funcs.get_colours(self.initial_img, self.final_colour_number) 319 | 320 | if not self.colours_to_display: 321 | return 322 | 323 | # TODO: order the list by colour proximity (purely aesthetic) 324 | for colour in self.colours_to_display: 325 | self.add_colour_to_listview(colour) 326 | 327 | def colour_already_in_list(self, colour): 328 | self.label_already_in_list.setText(f"{colour} already in list") 329 | 330 | def add_colour_from_picker(self): 331 | new_colour = QtWidgets.QColorDialog.getColor() 332 | 333 | print(new_colour) 334 | 335 | if not new_colour: 336 | return 337 | 338 | self.add_colour_to_listview(new_colour) 339 | 340 | def change_colour_from_picker(self): 341 | new_colour = QtWidgets.QColorDialog.getColor() 342 | 343 | self.change_colour(new_colour) 344 | 345 | def add_mouse_colour(self): 346 | if self.model_listView_mouse_colour.item(0) is None: 347 | return 348 | 349 | new_colour = self.model_listView_mouse_colour.item(0).background().color() 350 | 351 | self.add_colour_to_listview(new_colour) 352 | 353 | def change_colour(self, new_colour): 354 | if self.check_if_colour_exists(new_colour): 355 | self.colour_already_in_list(new_colour) 356 | return 357 | 358 | index = self.listView_choosen_colours.selectedIndexes() 359 | if len(index) < 1: 360 | return 361 | 362 | bg_colour = QtGui.QColor(int(new_colour[0]), 363 | int(new_colour[1]), 364 | int(new_colour[2])) 365 | self.model_listView_choosen_colours.itemFromIndex(index[0]).setBackground(bg_colour) 366 | 367 | 368 | def delete_colour(self): 369 | index = self.listView_choosen_colours.selectedIndexes() 370 | if len(index) > 0: 371 | self.model_listView_choosen_colours.removeRow(index[0].row()) 372 | 373 | def get_colour_under_mouse(self, event): 374 | if self.resized_initial_img is None: 375 | return 376 | 377 | # TODO: pixel offset sometimes (probably has something to do with resizing) 378 | x = event.localPos().x() 379 | y = event.localPos().y() 380 | 381 | colour = self.resized_initial_img.pixel(x, y) 382 | rgb = QtGui.QColor(colour).getRgb()[0:3] 383 | 384 | if self.radioButton_add.isChecked(): 385 | self.add_colour_to_listview(rgb) 386 | 387 | elif self.radioButton_change.isChecked(): 388 | self.change_colour(rgb) 389 | 390 | item = QtGui.QStandardItem() 391 | item.setBackground(QtGui.QColor(int(rgb[0]), 392 | int(rgb[1]), 393 | int(rgb[2]))) 394 | 395 | self.model_listView_mouse_colour.removeRow(0) 396 | self.model_listView_mouse_colour.appendRow(item) 397 | 398 | 399 | def merge_colours(self): 400 | ui_funcs.merge_colours(self.model_listView_choosen_colours, 401 | self.spinBox_merge_colours.value()) 402 | 403 | def to_dmc_colours(self): 404 | colours_list = [] 405 | for i in range(self.model_listView_choosen_colours.rowCount()): 406 | item = self.model_listView_choosen_colours.item(i) 407 | colours_list.append(np.asarray(item.background().color().getRgb()[0:3])) 408 | 409 | colour_space = None 410 | 411 | if self.radioButton_to_dmc_lab.isChecked(): 412 | colour_space = 'lab' 413 | 414 | elif self.radioButton_to_dmc_rgb.isChecked(): 415 | colour_space = 'rgb' 416 | 417 | new_colours = ui_funcs.to_dmc_colours(colours_list, self.colour_corres_list, colour_space) 418 | 419 | self.model_listView_choosen_colours.clear() 420 | 421 | for colour in new_colours: 422 | self.add_colour_to_listview(colour) 423 | 424 | 425 | 426 | def generate_output(self): 427 | self.label_image_generation.setText('Generating image...') 428 | 429 | self.colours_to_display = [] 430 | for i in range(self.model_listView_choosen_colours.rowCount()): 431 | item = self.model_listView_choosen_colours.item(i) 432 | self.colours_to_display.append(np.asarray(item.background().color().getRgb()[0:3], np.float64) / 255) 433 | 434 | self.new_img = ui_funcs.reduce_colours(self.initial_img, self.colours_to_display) 435 | 436 | label_size = (self.label_original_image.size().height(), self.label_original_image.size().width()) 437 | 438 | self.new_img = np.asarray(self.new_img * 255, 'uint8') 439 | self.new_img = Image.fromarray(self.new_img, mode='RGB') 440 | 441 | self.new_resized_img = ui_funcs.resize_image(self.new_img, label_size) 442 | 443 | self.new_resized_img = ImageQt.ImageQt(self.new_resized_img) 444 | reference_img = QtGui.QPixmap.fromImage(self.new_resized_img) 445 | self.label_original_image.setPixmap(reference_img) 446 | 447 | self.label_image_generation.setText('Image generated!') 448 | 449 | def reset_displayed_image(self): 450 | reference_img = QtGui.QPixmap.fromImage(self.resized_initial_img) 451 | self.label_original_image.setPixmap(reference_img) 452 | 453 | def save_output_image(self): 454 | final_name = f"{self.path_to_img.split('.')[0]}_reduced.{self.path_to_img.split('.')[-1]}" 455 | self.new_img.save(final_name) 456 | msg = QtWidgets.QMessageBox() 457 | msg.setIcon(QtWidgets.QMessageBox.Information) 458 | msg.setText(f"Image successfully saved as {final_name} !") 459 | msg.exec_() 460 | 461 | 462 | if __name__ == "__main__": 463 | import sys 464 | app = QtWidgets.QApplication(sys.argv) 465 | MainWindow = QtWidgets.QMainWindow() 466 | ui = Ui_MainWindow() 467 | ui.setupUi(MainWindow) 468 | MainWindow.show() 469 | sys.exit(app.exec_()) 470 | -------------------------------------------------------------------------------- /res/colours.json: -------------------------------------------------------------------------------- 1 | [{"Floss": "3713", "DMC Name": "Salmon Very Light", "RGB": [255, 226, 226], "Hex": "ffe2e2"}, {"Floss": "761", "DMC Name": "Salmon Light", "RGB": [255, 201, 201], "Hex": "ffc9c9"}, {"Floss": "760", "DMC Name": "Salmon", "RGB": [245, 173, 173], "Hex": "f5adad"}, {"Floss": "3712", "DMC Name": "Salmon Medium", "RGB": [241, 135, 135], "Hex": "f18787"}, {"Floss": "3328", "DMC Name": "Salmon Dark", "RGB": [227, 109, 109], "Hex": "e36d6d"}, {"Floss": "347", "DMC Name": "Salmon Very Dark", "RGB": [191, 45, 45], "Hex": "bf2d2d"}, {"Floss": "353", "DMC Name": "Peach", "RGB": [254, 215, 204], "Hex": "fed7cc"}, {"Floss": "352", "DMC Name": "Coral Light", "RGB": [253, 156, 151], "Hex": "fd9c97"}, {"Floss": "351", "DMC Name": "Coral", "RGB": [233, 106, 103], "Hex": "e96a67"}, {"Floss": "350", "DMC Name": "Coral Medium", "RGB": [224, 72, 72], "Hex": "e04848"}, {"Floss": "349", "DMC Name": "Coral Dark", "RGB": [210, 16, 53], "Hex": "d21035"}, {"Floss": "817", "DMC Name": "Coral Red Very Dark", "RGB": [187, 5, 31], "Hex": "bb051f"}, {"Floss": "3708", "DMC Name": "Melon Light", "RGB": [255, 203, 213], "Hex": "ffcbd5"}, {"Floss": "3706", "DMC Name": "Melon Medium", "RGB": [255, 173, 188], "Hex": "ffadbc"}, {"Floss": "3705", "DMC Name": "Melon Dark", "RGB": [255, 121, 146], "Hex": "ff7992"}, {"Floss": "3801", "DMC Name": "Melon Very Dark", "RGB": [231, 73, 103], "Hex": "e74967"}, {"Floss": "666", "DMC Name": "Bright Red", "RGB": [227, 29, 66], "Hex": "e31d42"}, {"Floss": "321", "DMC Name": "Red", "RGB": [199, 43, 59], "Hex": "c72b3b"}, {"Floss": "304", "DMC Name": "Red Medium", "RGB": [183, 31, 51], "Hex": "b71f33"}, {"Floss": "498", "DMC Name": "Red Dark", "RGB": [167, 19, 43], "Hex": "a7132b"}, {"Floss": "816", "DMC Name": "Garnet", "RGB": [151, 11, 35], "Hex": "970b23"}, {"Floss": "815", "DMC Name": "Garnet Medium", "RGB": [135, 7, 31], "Hex": "87071f"}, {"Floss": "814", "DMC Name": "Garnet Dark", "RGB": [123, 0, 27], "Hex": "7b001b"}, {"Floss": "894", "DMC Name": "Carnation Very Light", "RGB": [255, 178, 187], "Hex": "ffb2bb"}, {"Floss": "893", "DMC Name": "Carnation Light", "RGB": [252, 144, 162], "Hex": "fc90a2"}, {"Floss": "892", "DMC Name": "Carnation Medium", "RGB": [255, 121, 140], "Hex": "ff798c"}, {"Floss": "891", "DMC Name": "Carnation Dark", "RGB": [255, 87, 115], "Hex": "ff5773"}, {"Floss": "818", "DMC Name": "Baby Pink", "RGB": [255, 223, 217], "Hex": "ffdfd9"}, {"Floss": "957", "DMC Name": "Geranium Pale", "RGB": [253, 181, 181], "Hex": "fdb5b5"}, {"Floss": "956", "DMC Name": "Geranium", "RGB": [255, 145, 145], "Hex": "ff9191"}, {"Floss": "309", "DMC Name": "Rose Dark", "RGB": [214, 43, 91], "Hex": "d62b5b"}, {"Floss": "963", "DMC Name": "Dusty Rose Ult Vy Lt", "RGB": [255, 215, 215], "Hex": "ffd7d7"}, {"Floss": "3716", "DMC Name": "Dusty Rose Med Vy Lt", "RGB": [255, 189, 189], "Hex": "ffbdbd"}, {"Floss": "962", "DMC Name": "Dusty Rose Medium", "RGB": [230, 138, 138], "Hex": "e68a8a"}, {"Floss": "961", "DMC Name": "Dusty Rose Dark", "RGB": [207, 115, 115], "Hex": "cf7373"}, {"Floss": "3833", "DMC Name": "Raspberry Light", "RGB": [234, 134, 153], "Hex": "ea8699"}, {"Floss": "3832", "DMC Name": "Raspberry Medium", "RGB": [219, 85, 110], "Hex": "db556e"}, {"Floss": "3831", "DMC Name": "Raspberry Dark", "RGB": [179, 47, 72], "Hex": "b32f48"}, {"Floss": "777", "DMC Name": "Raspberry Very Dark", "RGB": [145, 53, 70], "Hex": "913546"}, {"Floss": "819", "DMC Name": "Baby Pink Light", "RGB": [255, 238, 235], "Hex": "ffeeeb"}, {"Floss": "3326", "DMC Name": "Rose Light", "RGB": [251, 173, 180], "Hex": "fbadb4"}, {"Floss": "776", "DMC Name": "Pink Medium", "RGB": [252, 176, 185], "Hex": "fcb0b9"}, {"Floss": "899", "DMC Name": "Rose Medium", "RGB": [242, 118, 136], "Hex": "f27688"}, {"Floss": "335", "DMC Name": "Rose", "RGB": [238, 84, 110], "Hex": "ee546e"}, {"Floss": "326", "DMC Name": "Rose Very Dark", "RGB": [179, 59, 75], "Hex": "b33b4b"}, {"Floss": "151", "DMC Name": "Dusty Rose Vry Lt", "RGB": [240, 206, 212], "Hex": "f0ced4"}, {"Floss": "3354", "DMC Name": "Dusty Rose Light", "RGB": [228, 166, 172], "Hex": "e4a6ac"}, {"Floss": "3733", "DMC Name": "Dusty Rose", "RGB": [232, 135, 155], "Hex": "e8879b"}, {"Floss": "3731", "DMC Name": "Dusty Rose Very Dark", "RGB": [218, 103, 131], "Hex": "da6783"}, {"Floss": "3350", "DMC Name": "Dusty Rose Ultra Dark", "RGB": [188, 67, 101], "Hex": "bc4365"}, {"Floss": "150", "DMC Name": "Dusty Rose Ult Vy Dk", "RGB": [171, 2, 73], "Hex": "ab0249"}, {"Floss": "3689", "DMC Name": "Mauve Light", "RGB": [251, 191, 194], "Hex": "fbbfc2"}, {"Floss": "3688", "DMC Name": "Mauve Medium", "RGB": [231, 169, 172], "Hex": "e7a9ac"}, {"Floss": "3687", "DMC Name": "Mauve", "RGB": [201, 107, 112], "Hex": "c96b70"}, {"Floss": "3803", "DMC Name": "Mauve Dark", "RGB": [171, 51, 87], "Hex": "ab3357"}, {"Floss": "3685", "DMC Name": "Mauve Very Dark", "RGB": [136, 21, 49], "Hex": "881531"}, {"Floss": "605", "DMC Name": "Cranberry Very Light", "RGB": [255, 192, 205], "Hex": "ffc0cd"}, {"Floss": "604", "DMC Name": "Cranberry Light", "RGB": [255, 176, 190], "Hex": "ffb0be"}, {"Floss": "603", "DMC Name": "Cranberry", "RGB": [255, 164, 190], "Hex": "ffa4be"}, {"Floss": "602", "DMC Name": "Cranberry Medium", "RGB": [226, 72, 116], "Hex": "e24874"}, {"Floss": "601", "DMC Name": "Cranberry Dark", "RGB": [209, 40, 106], "Hex": "d1286a"}, {"Floss": "600", "DMC Name": "Cranberry Very Dark", "RGB": [205, 47, 99], "Hex": "cd2f63"}, {"Floss": "3806", "DMC Name": "Cyclamen Pink Light", "RGB": [255, 140, 174], "Hex": "ff8cae"}, {"Floss": "3805", "DMC Name": "Cyclamen Pink", "RGB": [243, 71, 139], "Hex": "f3478b"}, {"Floss": "3804", "DMC Name": "Cyclamen Pink Dark", "RGB": [224, 40, 118], "Hex": "e02876"}, {"Floss": "3609", "DMC Name": "Plum Ultra Light", "RGB": [244, 174, 213], "Hex": "f4aed5"}, {"Floss": "3608", "DMC Name": "Plum Very Light", "RGB": [234, 156, 196], "Hex": "ea9cc4"}, {"Floss": "3607", "DMC Name": "Plum Light", "RGB": [197, 73, 137], "Hex": "c54989"}, {"Floss": "718", "DMC Name": "Plum", "RGB": [156, 36, 98], "Hex": "9c2462"}, {"Floss": "917", "DMC Name": "Plum Medium", "RGB": [155, 19, 89], "Hex": "9b1359"}, {"Floss": "915", "DMC Name": "Plum Dark", "RGB": [130, 0, 67], "Hex": "820043"}, {"Floss": "225", "DMC Name": "Shell Pink Ult Vy Lt", "RGB": [255, 223, 213], "Hex": "ffdfd5"}, {"Floss": "224", "DMC Name": "Shell Pink Very Light", "RGB": [235, 183, 175], "Hex": "ebb7af"}, {"Floss": "152", "DMC Name": "Shell Pink Med Light", "RGB": [226, 160, 153], "Hex": "e2a099"}, {"Floss": "223", "DMC Name": "Shell Pink Light", "RGB": [204, 132, 124], "Hex": "cc847c"}, {"Floss": "3722", "DMC Name": "Shell Pink Med", "RGB": [188, 108, 100], "Hex": "bc6c64"}, {"Floss": "3721", "DMC Name": "Shell Pink Dark", "RGB": [161, 75, 81], "Hex": "a14b51"}, {"Floss": "221", "DMC Name": "Shell Pink Vy Dk", "RGB": [136, 62, 67], "Hex": "883e43"}, {"Floss": "778", "DMC Name": "Antique Mauve Vy Lt", "RGB": [223, 179, 187], "Hex": "dfb3bb"}, {"Floss": "3727", "DMC Name": "Antique Mauve Light", "RGB": [219, 169, 178], "Hex": "dba9b2"}, {"Floss": "316", "DMC Name": "Antique Mauve Med", "RGB": [183, 115, 127], "Hex": "b7737f"}, {"Floss": "3726", "DMC Name": "Antique Mauve Dark", "RGB": [155, 91, 102], "Hex": "9b5b66"}, {"Floss": "315", "DMC Name": "Antique Mauve Md Dk", "RGB": [129, 73, 82], "Hex": "814952"}, {"Floss": "3802", "DMC Name": "Antique Mauve Vy Dk", "RGB": [113, 65, 73], "Hex": "714149"}, {"Floss": "902", "DMC Name": "Garnet Very Dark", "RGB": [130, 38, 55], "Hex": "822637"}, {"Floss": "3743", "DMC Name": "Antique Violet Vy Lt", "RGB": [215, 203, 211], "Hex": "d7cbd3"}, {"Floss": "3042", "DMC Name": "Antique Violet Light", "RGB": [183, 157, 167], "Hex": "b79da7"}, {"Floss": "3041", "DMC Name": "Antique Violet Medium", "RGB": [149, 111, 124], "Hex": "956f7c"}, {"Floss": "3740", "DMC Name": "Antique Violet Dark", "RGB": [120, 87, 98], "Hex": "785762"}, {"Floss": "3836", "DMC Name": "Grape Light", "RGB": [186, 145, 170], "Hex": "ba91aa"}, {"Floss": "3835", "DMC Name": "Grape Medium", "RGB": [148, 96, 131], "Hex": "946083"}, {"Floss": "3834", "DMC Name": "Grape Dark", "RGB": [114, 55, 93], "Hex": "72375d"}, {"Floss": "154", "DMC Name": "Grape Very Dark", "RGB": [87, 36, 51], "Hex": "572433"}, {"Floss": "211", "DMC Name": "Lavender Light", "RGB": [227, 203, 227], "Hex": "e3cbe3"}, {"Floss": "210", "DMC Name": "Lavender Medium", "RGB": [195, 159, 195], "Hex": "c39fc3"}, {"Floss": "209", "DMC Name": "Lavender Dark", "RGB": [163, 123, 167], "Hex": "a37ba7"}, {"Floss": "208", "DMC Name": "Lavender Very Dark", "RGB": [131, 91, 139], "Hex": "835b8b"}, {"Floss": "3837", "DMC Name": "Lavender Ultra Dark", "RGB": [108, 58, 110], "Hex": "6c3a6e"}, {"Floss": "327", "DMC Name": "Violet Dark", "RGB": [99, 54, 102], "Hex": "633666"}, {"Floss": "153", "DMC Name": "Violet Very Light", "RGB": [230, 204, 217], "Hex": "e6ccd9"}, {"Floss": "554", "DMC Name": "Violet Light", "RGB": [219, 179, 203], "Hex": "dbb3cb"}, {"Floss": "553", "DMC Name": "Violet", "RGB": [163, 99, 139], "Hex": "a3638b"}, {"Floss": "552", "DMC Name": "Violet Medium", "RGB": [128, 58, 107], "Hex": "803a6b"}, {"Floss": "550", "DMC Name": "Violet Very Dark", "RGB": [92, 24, 78], "Hex": "5c184e"}, {"Floss": "3747", "DMC Name": "Blue Violet Vy Lt", "RGB": [211, 215, 237], "Hex": "d3d7ed"}, {"Floss": "341", "DMC Name": "Blue Violet Light", "RGB": [183, 191, 221], "Hex": "b7bfdd"}, {"Floss": "156", "DMC Name": "Blue Violet Med Lt", "RGB": [163, 174, 209], "Hex": "a3aed1"}, {"Floss": "340", "DMC Name": "Blue Violet Medium", "RGB": [173, 167, 199], "Hex": "ada7c7"}, {"Floss": "155", "DMC Name": "Blue Violet Med Dark", "RGB": [152, 145, 182], "Hex": "9891b6"}, {"Floss": "3746", "DMC Name": "Blue Violet Dark", "RGB": [119, 107, 152], "Hex": "776b98"}, {"Floss": "333", "DMC Name": "Blue Violet Very Dark", "RGB": [92, 84, 120], "Hex": "5c5478"}, {"Floss": "157", "DMC Name": "Cornflower Blue Vy Lt", "RGB": [187, 195, 217], "Hex": "bbc3d9"}, {"Floss": "794", "DMC Name": "Cornflower Blue Light", "RGB": [143, 156, 193], "Hex": "8f9cc1"}, {"Floss": "793", "DMC Name": "Cornflower Blue Med", "RGB": [112, 125, 162], "Hex": "707da2"}, {"Floss": "3807", "DMC Name": "Cornflower Blue", "RGB": [96, 103, 140], "Hex": "60678c"}, {"Floss": "792", "DMC Name": "Cornflower Blue Dark", "RGB": [85, 91, 123], "Hex": "555b7b"}, {"Floss": "158", "DMC Name": "Cornflower Blu M V D", "RGB": [76, 82, 110], "Hex": "4c526e"}, {"Floss": "791", "DMC Name": "Cornflower Blue V D", "RGB": [70, 69, 99], "Hex": "464563"}, {"Floss": "3840", "DMC Name": "Lavender Blue Light", "RGB": [176, 192, 218], "Hex": "b0c0da"}, {"Floss": "3839", "DMC Name": "Lavender Blue Med", "RGB": [123, 142, 171], "Hex": "7b8eab"}, {"Floss": "3838", "DMC Name": "Lavender Blue Dark", "RGB": [92, 114, 148], "Hex": "5c7294"}, {"Floss": "800", "DMC Name": "Delft Blue Pale", "RGB": [192, 204, 222], "Hex": "c0ccde"}, {"Floss": "809", "DMC Name": "Delft Blue", "RGB": [148, 168, 198], "Hex": "94a8c6"}, {"Floss": "799", "DMC Name": "Delft Blue Medium", "RGB": [116, 142, 182], "Hex": "748eb6"}, {"Floss": "798", "DMC Name": "Delft Blue Dark", "RGB": [70, 106, 142], "Hex": "466a8e"}, {"Floss": "797", "DMC Name": "Royal Blue", "RGB": [19, 71, 125], "Hex": "13477d"}, {"Floss": "796", "DMC Name": "Royal Blue Dark", "RGB": [17, 65, 109], "Hex": "11416d"}, {"Floss": "820", "DMC Name": "Royal Blue Very Dark", "RGB": [14, 54, 92], "Hex": "0e365c"}, {"Floss": "162", "DMC Name": "Blue Ultra Very Light", "RGB": [219, 236, 245], "Hex": "dbecf5"}, {"Floss": "827", "DMC Name": "Blue Very Light", "RGB": [189, 221, 237], "Hex": "bddded"}, {"Floss": "813", "DMC Name": "Blue Light", "RGB": [161, 194, 215], "Hex": "a1c2d7"}, {"Floss": "826", "DMC Name": "Blue Medium", "RGB": [107, 158, 191], "Hex": "6b9ebf"}, {"Floss": "825", "DMC Name": "Blue Dark", "RGB": [71, 129, 165], "Hex": "4781a5"}, {"Floss": "824", "DMC Name": "Blue Very Dark", "RGB": [57, 105, 135], "Hex": "396987"}, {"Floss": "996", "DMC Name": "Electric Blue Medium", "RGB": [48, 194, 236], "Hex": "30c2ec"}, {"Floss": "3843", "DMC Name": "Electric Blue", "RGB": [20, 170, 208], "Hex": "14aad0"}, {"Floss": "995", "DMC Name": "Electric Blue Dark", "RGB": [38, 150, 182], "Hex": "2696b6"}, {"Floss": "3846", "DMC Name": "Turquoise Bright Light", "RGB": [6, 227, 230], "Hex": "06e3e6"}, {"Floss": "3845", "DMC Name": "Turquoise Bright Med", "RGB": [4, 196, 202], "Hex": "04c4ca"}, {"Floss": "3844", "DMC Name": "Turquoise Bright Dark", "RGB": [18, 174, 186], "Hex": "12aeba"}, {"Floss": "159", "DMC Name": "Blue Gray Light", "RGB": [199, 202, 215], "Hex": "c7cad7"}, {"Floss": "160", "DMC Name": "Blue Gray Medium", "RGB": [153, 159, 183], "Hex": "999fb7"}, {"Floss": "161", "DMC Name": "Blue Gray", "RGB": [120, 128, 164], "Hex": "7880a4"}, {"Floss": "3756", "DMC Name": "Baby Blue Ult Vy Lt", "RGB": [238, 252, 252], "Hex": "eefcfc"}, {"Floss": "775", "DMC Name": "Baby Blue Very Light", "RGB": [217, 235, 241], "Hex": "d9ebf1"}, {"Floss": "3841", "DMC Name": "Baby Blue Pale", "RGB": [205, 223, 237], "Hex": "cddfed"}, {"Floss": "3325", "DMC Name": "Baby Blue Light", "RGB": [184, 210, 230], "Hex": "b8d2e6"}, {"Floss": "3755", "DMC Name": "Baby Blue", "RGB": [147, 180, 206], "Hex": "93b4ce"}, {"Floss": "334", "DMC Name": "Baby Blue Medium", "RGB": [115, 159, 193], "Hex": "739fc1"}, {"Floss": "322", "DMC Name": "Baby Blue Dark", "RGB": [90, 143, 184], "Hex": "5a8fb8"}, {"Floss": "312", "DMC Name": "Baby Blue Very Dark", "RGB": [53, 102, 139], "Hex": "35668b"}, {"Floss": "803", "DMC Name": "Baby Blue Ult Vy Dk", "RGB": [44, 89, 124], "Hex": "2c597c"}, {"Floss": "336", "DMC Name": "Navy Blue", "RGB": [37, 59, 115], "Hex": "253b73"}, {"Floss": "823", "DMC Name": "Navy Blue Dark", "RGB": [33, 48, 99], "Hex": "213063"}, {"Floss": "939", "DMC Name": "Navy Blue Very Dark", "RGB": [27, 40, 83], "Hex": "1b2853"}, {"Floss": "3753", "DMC Name": "Antique Blue Ult Vy Lt", "RGB": [219, 226, 233], "Hex": "dbe2e9"}, {"Floss": "3752", "DMC Name": "Antique Blue Very Lt", "RGB": [199, 209, 219], "Hex": "c7d1db"}, {"Floss": "932", "DMC Name": "Antique Blue Light", "RGB": [162, 181, 198], "Hex": "a2b5c6"}, {"Floss": "931", "DMC Name": "Antique Blue Medium", "RGB": [106, 133, 158], "Hex": "6a859e"}, {"Floss": "930", "DMC Name": "Antique Blue Dark", "RGB": [69, 92, 113], "Hex": "455c71"}, {"Floss": "3750", "DMC Name": "Antique Blue Very Dk", "RGB": [56, 76, 94], "Hex": "384c5e"}, {"Floss": "828", "DMC Name": "Sky Blue Vy Lt", "RGB": [197, 232, 237], "Hex": "c5e8ed"}, {"Floss": "3761", "DMC Name": "Sky Blue Light", "RGB": [172, 216, 226], "Hex": "acd8e2"}, {"Floss": "519", "DMC Name": "Sky Blue", "RGB": [126, 177, 200], "Hex": "7eb1c8"}, {"Floss": "518", "DMC Name": "Wedgewood Light", "RGB": [79, 147, 167], "Hex": "4f93a7"}, {"Floss": "3760", "DMC Name": "Wedgewood Med", "RGB": [62, 133, 162], "Hex": "3e85a2"}, {"Floss": "517", "DMC Name": "Wedgewood Dark", "RGB": [59, 118, 143], "Hex": "3b768f"}, {"Floss": "3842", "DMC Name": "Wedgewood Vry Dk", "RGB": [50, 102, 124], "Hex": "32667c"}, {"Floss": "311", "DMC Name": "Wedgewood Ult VyDk", "RGB": [28, 80, 102], "Hex": "1c5066"}, {"Floss": "747", "DMC Name": "Peacock Blue Vy Lt", "RGB": [229, 252, 253], "Hex": "e5fcfd"}, {"Floss": "3766", "DMC Name": "Peacock Blue Light", "RGB": [153, 207, 217], "Hex": "99cfd9"}, {"Floss": "807", "DMC Name": "Peacock Blue", "RGB": [100, 171, 186], "Hex": "64abba"}, {"Floss": "806", "DMC Name": "Peacock Blue Dark", "RGB": [61, 149, 165], "Hex": "3d95a5"}, {"Floss": "3765", "DMC Name": "Peacock Blue Vy Dk", "RGB": [52, 127, 140], "Hex": "347f8c"}, {"Floss": "3811", "DMC Name": "Turquoise Very Light", "RGB": [188, 227, 230], "Hex": "bce3e6"}, {"Floss": "598", "DMC Name": "Turquoise Light", "RGB": [144, 195, 204], "Hex": "90c3cc"}, {"Floss": "597", "DMC Name": "Turquoise", "RGB": [91, 163, 179], "Hex": "5ba3b3"}, {"Floss": "3810", "DMC Name": "Turquoise Dark", "RGB": [72, 142, 154], "Hex": "488e9a"}, {"Floss": "3809", "DMC Name": "Turquoise Vy Dark", "RGB": [63, 124, 133], "Hex": "3f7c85"}, {"Floss": "3808", "DMC Name": "Turquoise Ult Vy Dk", "RGB": [54, 105, 112], "Hex": "366970"}, {"Floss": "928", "DMC Name": "Gray Green Vy Lt", "RGB": [221, 227, 227], "Hex": "dde3e3"}, {"Floss": "927", "DMC Name": "Gray Green Light", "RGB": [189, 203, 203], "Hex": "bdcbcb"}, {"Floss": "926", "DMC Name": "Gray Green Med", "RGB": [152, 174, 174], "Hex": "98aeae"}, {"Floss": "3768", "DMC Name": "Gray Green Dark", "RGB": [101, 127, 127], "Hex": "657f7f"}, {"Floss": "924", "DMC Name": "Gray Green Vy Dark", "RGB": [86, 106, 106], "Hex": "566a6a"}, {"Floss": "3849", "DMC Name": "Teal Green Light", "RGB": [82, 179, 164], "Hex": "52b3a4"}, {"Floss": "3848", "DMC Name": "Teal Green Med", "RGB": [85, 147, 146], "Hex": "559392"}, {"Floss": "3847", "DMC Name": "Teal Green Dark", "RGB": [52, 125, 117], "Hex": "347d75"}, {"Floss": "964", "DMC Name": "Sea Green Light", "RGB": [169, 226, 216], "Hex": "a9e2d8"}, {"Floss": "959", "DMC Name": "Sea Green Med", "RGB": [89, 199, 180], "Hex": "59c7b4"}, {"Floss": "958", "DMC Name": "Sea Green Dark", "RGB": [62, 182, 161], "Hex": "3eb6a1"}, {"Floss": "3812", "DMC Name": "Sea Green Vy Dk", "RGB": [47, 140, 132], "Hex": "2f8c84"}, {"Floss": "3851", "DMC Name": "Green Bright Lt", "RGB": [73, 179, 161], "Hex": "49b3a1"}, {"Floss": "943", "DMC Name": "Green Bright Md", "RGB": [61, 147, 132], "Hex": "3d9384"}, {"Floss": "3850", "DMC Name": "Green Bright Dk", "RGB": [55, 132, 119], "Hex": "378477"}, {"Floss": "993", "DMC Name": "Aquamarine Vy Lt", "RGB": [144, 192, 180], "Hex": "90c0b4"}, {"Floss": "992", "DMC Name": "Aquamarine Lt", "RGB": [111, 174, 159], "Hex": "6fae9f"}, {"Floss": "3814", "DMC Name": "Aquamarine", "RGB": [80, 139, 125], "Hex": "508b7d"}, {"Floss": "991", "DMC Name": "Aquamarine Dk", "RGB": [71, 123, 110], "Hex": "477b6e"}, {"Floss": "966", "DMC Name": "Jade Ultra Vy Lt", "RGB": [185, 215, 192], "Hex": "b9d7c0"}, {"Floss": "564", "DMC Name": "Jade Very Light", "RGB": [167, 205, 175], "Hex": "a7cdaf"}, {"Floss": "563", "DMC Name": "Jade Light", "RGB": [143, 192, 152], "Hex": "8fc098"}, {"Floss": "562", "DMC Name": "Jade Medium", "RGB": [83, 151, 106], "Hex": "53976a"}, {"Floss": "505", "DMC Name": "Jade Green", "RGB": [51, 131, 98], "Hex": "338362"}, {"Floss": "3817", "DMC Name": "Celadon Green Lt", "RGB": [153, 195, 170], "Hex": "99c3aa"}, {"Floss": "3816", "DMC Name": "Celadon Green", "RGB": [101, 165, 125], "Hex": "65a57d"}, {"Floss": "163", "DMC Name": "Celadon Green Md", "RGB": [77, 131, 97], "Hex": "4d8361"}, {"Floss": "3815", "DMC Name": "Celadon Green Dk", "RGB": [71, 119, 89], "Hex": "477759"}, {"Floss": "561", "DMC Name": "Celadon Green VD", "RGB": [44, 106, 69], "Hex": "2c6a45"}, {"Floss": "504", "DMC Name": "Blue Green Vy Lt", "RGB": [196, 222, 204], "Hex": "c4decc"}, {"Floss": "3813", "DMC Name": "Blue Green Lt", "RGB": [178, 212, 189], "Hex": "b2d4bd"}, {"Floss": "503", "DMC Name": "Blue Green Med", "RGB": [123, 172, 148], "Hex": "7bac94"}, {"Floss": "502", "DMC Name": "Blue Green", "RGB": [91, 144, 113], "Hex": "5b9071"}, {"Floss": "501", "DMC Name": "Blue Green Dark", "RGB": [57, 111, 82], "Hex": "396f52"}, {"Floss": "500", "DMC Name": "Blue Green Vy Dk", "RGB": [4, 77, 51], "Hex": "044d33"}, {"Floss": "955", "DMC Name": "Nile Green Light", "RGB": [162, 214, 173], "Hex": "a2d6ad"}, {"Floss": "954", "DMC Name": "Nile Green", "RGB": [136, 186, 145], "Hex": "88ba91"}, {"Floss": "913", "DMC Name": "Nile Green Med", "RGB": [109, 171, 119], "Hex": "6dab77"}, {"Floss": "912", "DMC Name": "Emerald Green Lt", "RGB": [27, 157, 107], "Hex": "1b9d6b"}, {"Floss": "911", "DMC Name": "Emerald Green Med", "RGB": [24, 144, 101], "Hex": "189065"}, {"Floss": "910", "DMC Name": "Emerald Green Dark", "RGB": [24, 126, 86], "Hex": "187e56"}, {"Floss": "909", "DMC Name": "Emerald Green Vy Dk", "RGB": [21, 111, 73], "Hex": "156f49"}, {"Floss": "3818", "DMC Name": "Emerald Grn Ult V Dk", "RGB": [17, 90, 59], "Hex": "115a3b"}, {"Floss": "369", "DMC Name": "Pistachio Green Vy Lt", "RGB": [215, 237, 204], "Hex": "d7edcc"}, {"Floss": "368", "DMC Name": "Pistachio Green Lt", "RGB": [166, 194, 152], "Hex": "a6c298"}, {"Floss": "320", "DMC Name": "Pistachio Green Med", "RGB": [105, 136, 90], "Hex": "69885a"}, {"Floss": "367", "DMC Name": "Pistachio Green Dk", "RGB": [97, 122, 82], "Hex": "617a52"}, {"Floss": "319", "DMC Name": "Pistachio Grn Vy Dk", "RGB": [32, 95, 46], "Hex": "205f2e"}, {"Floss": "890", "DMC Name": "Pistachio Grn Ult V D", "RGB": [23, 73, 35], "Hex": "174923"}, {"Floss": "164", "DMC Name": "Forest Green Lt", "RGB": [200, 216, 184], "Hex": "c8d8b8"}, {"Floss": "989", "DMC Name": "Forest Green ", "RGB": [141, 166, 117], "Hex": "8da675"}, {"Floss": "988", "DMC Name": "Forest Green Med", "RGB": [115, 139, 91], "Hex": "738b5b"}, {"Floss": "987", "DMC Name": "Forest Green Dk", "RGB": [88, 113, 65], "Hex": "587141"}, {"Floss": "986", "DMC Name": "Forest Green Vy Dk", "RGB": [64, 82, 48], "Hex": "405230"}, {"Floss": "772", "DMC Name": "Yellow Green Vy Lt", "RGB": [228, 236, 212], "Hex": "e4ecd4"}, {"Floss": "3348", "DMC Name": "Yellow Green Lt", "RGB": [204, 217, 177], "Hex": "ccd9b1"}, {"Floss": "3347", "DMC Name": "Yellow Green Med", "RGB": [113, 147, 92], "Hex": "71935c"}, {"Floss": "3346", "DMC Name": "Hunter Green", "RGB": [64, 106, 58], "Hex": "406a3a"}, {"Floss": "3345", "DMC Name": "Hunter Green Dk", "RGB": [27, 89, 21], "Hex": "1b5915"}, {"Floss": "895", "DMC Name": "Hunter Green Vy Dk", "RGB": [27, 83, 0], "Hex": "1b5300"}, {"Floss": "704", "DMC Name": "Chartreuse Bright", "RGB": [158, 207, 52], "Hex": "9ecf34"}, {"Floss": "703", "DMC Name": "Chartreuse", "RGB": [123, 181, 71], "Hex": "7bb547"}, {"Floss": "702", "DMC Name": "Kelly Green", "RGB": [71, 167, 47], "Hex": "47a72f"}, {"Floss": "701", "DMC Name": "Green Light", "RGB": [63, 143, 41], "Hex": "3f8f29"}, {"Floss": "700", "DMC Name": "Green Bright", "RGB": [7, 115, 27], "Hex": "07731b"}, {"Floss": "699", "DMC Name": "Green", "RGB": [5, 101, 23], "Hex": "056517"}, {"Floss": "907", "DMC Name": "Parrot Green Lt", "RGB": [199, 230, 102], "Hex": "c7e666"}, {"Floss": "906", "DMC Name": "Parrot Green Md", "RGB": [127, 179, 53], "Hex": "7fb335"}, {"Floss": "905", "DMC Name": "Parrot Green Dk", "RGB": [98, 138, 40], "Hex": "628a28"}, {"Floss": "904", "DMC Name": "Parrot Green V Dk", "RGB": [85, 120, 34], "Hex": "557822"}, {"Floss": "472", "DMC Name": "Avocado Grn U Lt", "RGB": [216, 228, 152], "Hex": "d8e498"}, {"Floss": "471", "DMC Name": "Avocado Grn V Lt", "RGB": [174, 191, 121], "Hex": "aebf79"}, {"Floss": "470", "DMC Name": "Avocado Grn Lt", "RGB": [148, 171, 79], "Hex": "94ab4f"}, {"Floss": "469", "DMC Name": "Avocado Green", "RGB": [114, 132, 60], "Hex": "72843c"}, {"Floss": "937", "DMC Name": "Avocado Green Md", "RGB": [98, 113, 51], "Hex": "627133"}, {"Floss": "936", "DMC Name": "Avocado Grn V Dk", "RGB": [76, 88, 38], "Hex": "4c5826"}, {"Floss": "935", "DMC Name": "Avocado Green Dk", "RGB": [66, 77, 33], "Hex": "424d21"}, {"Floss": "934", "DMC Name": "Avocado Grn Black", "RGB": [49, 57, 25], "Hex": "313919"}, {"Floss": "523", "DMC Name": "Fern Green Lt", "RGB": [171, 177, 151], "Hex": "abb197"}, {"Floss": "3053", "DMC Name": "Green Gray", "RGB": [156, 164, 130], "Hex": "9ca482"}, {"Floss": "3052", "DMC Name": "Green Gray Md", "RGB": [136, 146, 104], "Hex": "889268"}, {"Floss": "3051", "DMC Name": "Green Gray Dk", "RGB": [95, 102, 72], "Hex": "5f6648"}, {"Floss": "524", "DMC Name": "Fern Green Vy Lt", "RGB": [196, 205, 172], "Hex": "c4cdac"}, {"Floss": "522", "DMC Name": "Fern Green", "RGB": [150, 158, 126], "Hex": "969e7e"}, {"Floss": "520", "DMC Name": "Fern Green Dark", "RGB": [102, 109, 79], "Hex": "666d4f"}, {"Floss": "3364", "DMC Name": "Pine Green", "RGB": [131, 151, 95], "Hex": "83975f"}, {"Floss": "3363", "DMC Name": "Pine Green Md", "RGB": [114, 130, 86], "Hex": "728256"}, {"Floss": "3362", "DMC Name": "Pine Green Dk", "RGB": [94, 107, 71], "Hex": "5e6b47"}, {"Floss": "165", "DMC Name": "Moss Green Vy Lt", "RGB": [239, 244, 164], "Hex": "eff4a4"}, {"Floss": "3819", "DMC Name": "Moss Green Lt", "RGB": [224, 232, 104], "Hex": "e0e868"}, {"Floss": "166", "DMC Name": "Moss Green Md Lt", "RGB": [192, 200, 64], "Hex": "c0c840"}, {"Floss": "581", "DMC Name": "Moss Green", "RGB": [167, 174, 56], "Hex": "a7ae38"}, {"Floss": "580", "DMC Name": "Moss Green Dk", "RGB": [136, 141, 51], "Hex": "888d33"}, {"Floss": "734", "DMC Name": "Olive Green Lt", "RGB": [199, 192, 119], "Hex": "c7c077"}, {"Floss": "733", "DMC Name": "Olive Green Md", "RGB": [188, 179, 76], "Hex": "bcb34c"}, {"Floss": "732", "DMC Name": "Olive Green", "RGB": [148, 140, 54], "Hex": "948c36"}, {"Floss": "731", "DMC Name": "Olive Green Dk", "RGB": [147, 139, 55], "Hex": "938b37"}, {"Floss": "730", "DMC Name": "Olive Green V Dk", "RGB": [130, 123, 48], "Hex": "827b30"}, {"Floss": "3013", "DMC Name": "Khaki Green Lt", "RGB": [185, 185, 130], "Hex": "b9b982"}, {"Floss": "3012", "DMC Name": "Khaki Green Md", "RGB": [166, 167, 93], "Hex": "a6a75d"}, {"Floss": "3011", "DMC Name": "Khaki Green Dk", "RGB": [137, 138, 88], "Hex": "898a58"}, {"Floss": "372", "DMC Name": "Mustard Lt", "RGB": [204, 183, 132], "Hex": "ccb784"}, {"Floss": "371", "DMC Name": "Mustard", "RGB": [191, 166, 113], "Hex": "bfa671"}, {"Floss": "370", "DMC Name": "Mustard Medium", "RGB": [184, 157, 100], "Hex": "b89d64"}, {"Floss": "834", "DMC Name": "Golden Olive Vy Lt", "RGB": [219, 190, 127], "Hex": "dbbe7f"}, {"Floss": "833", "DMC Name": "Golden Olive Lt", "RGB": [200, 171, 108], "Hex": "c8ab6c"}, {"Floss": "832", "DMC Name": "Golden Olive", "RGB": [189, 155, 81], "Hex": "bd9b51"}, {"Floss": "831", "DMC Name": "Golden Olive Md", "RGB": [170, 143, 86], "Hex": "aa8f56"}, {"Floss": "830", "DMC Name": "Golden Olive Dk", "RGB": [141, 120, 75], "Hex": "8d784b"}, {"Floss": "829", "DMC Name": "Golden Olive Vy Dk", "RGB": [126, 107, 66], "Hex": "7e6b42"}, {"Floss": "613", "DMC Name": "Drab Brown V Lt", "RGB": [220, 196, 170], "Hex": "dcc4aa"}, {"Floss": "612", "DMC Name": "Drab Brown Lt", "RGB": [188, 154, 120], "Hex": "bc9a78"}, {"Floss": "611", "DMC Name": "Drab Brown", "RGB": [150, 118, 86], "Hex": "967656"}, {"Floss": "610", "DMC Name": "Drab Brown Dk", "RGB": [121, 96, 71], "Hex": "796047"}, {"Floss": "3047", "DMC Name": "Yellow Beige Lt", "RGB": [231, 214, 193], "Hex": "e7d6c1"}, {"Floss": "3046", "DMC Name": "Yellow Beige Md", "RGB": [216, 188, 154], "Hex": "d8bc9a"}, {"Floss": "3045", "DMC Name": "Yellow Beige Dk", "RGB": [188, 150, 106], "Hex": "bc966a"}, {"Floss": "167", "DMC Name": "Yellow Beige V Dk", "RGB": [167, 124, 73], "Hex": "a77c49"}, {"Floss": "746", "DMC Name": "Off White", "RGB": [252, 252, 238], "Hex": "fcfcee"}, {"Floss": "677", "DMC Name": "Old Gold Vy Lt", "RGB": [245, 236, 203], "Hex": "f5eccb"}, {"Floss": "422", "DMC Name": "Hazelnut Brown Lt", "RGB": [198, 159, 123], "Hex": "c69f7b"}, {"Floss": "3828", "DMC Name": "Hazelnut Brown", "RGB": [183, 139, 97], "Hex": "b78b61"}, {"Floss": "420", "DMC Name": "Hazelnut Brown Dk", "RGB": [160, 112, 66], "Hex": "a07042"}, {"Floss": "869", "DMC Name": "Hazelnut Brown V Dk", "RGB": [131, 94, 57], "Hex": "835e39"}, {"Floss": "728", "DMC Name": "Topaz", "RGB": [228, 180, 104], "Hex": "e4b468"}, {"Floss": "783", "DMC Name": "Topaz Medium", "RGB": [206, 145, 36], "Hex": "ce9124"}, {"Floss": "782", "DMC Name": "Topaz Dark", "RGB": [174, 119, 32], "Hex": "ae7720"}, {"Floss": "781", "DMC Name": "Topaz Very Dark", "RGB": [162, 109, 32], "Hex": "a26d20"}, {"Floss": "780", "DMC Name": "Topaz Ultra Vy Dk", "RGB": [148, 99, 26], "Hex": "94631a"}, {"Floss": "676", "DMC Name": "Old Gold Lt", "RGB": [229, 206, 151], "Hex": "e5ce97"}, {"Floss": "729", "DMC Name": "Old Gold Medium", "RGB": [208, 165, 62], "Hex": "d0a53e"}, {"Floss": "680", "DMC Name": "Old Gold Dark", "RGB": [188, 141, 14], "Hex": "bc8d0e"}, {"Floss": "3829", "DMC Name": "Old Gold Vy Dark", "RGB": [169, 130, 4], "Hex": "a98204"}, {"Floss": "3822", "DMC Name": "Straw Light", "RGB": [246, 220, 152], "Hex": "f6dc98"}, {"Floss": "3821", "DMC Name": "Straw", "RGB": [243, 206, 117], "Hex": "f3ce75"}, {"Floss": "3820", "DMC Name": "Straw Dark", "RGB": [223, 182, 95], "Hex": "dfb65f"}, {"Floss": "3852", "DMC Name": "Straw Very Dark", "RGB": [205, 157, 55], "Hex": "cd9d37"}, {"Floss": "445", "DMC Name": "Lemon Light", "RGB": [255, 251, 139], "Hex": "fffb8b"}, {"Floss": "307", "DMC Name": "Lemon", "RGB": [253, 237, 84], "Hex": "fded54"}, {"Floss": "973", "DMC Name": "Canary Bright", "RGB": [255, 227, 0], "Hex": "ffe300"}, {"Floss": "444", "DMC Name": "Lemon Dark", "RGB": [255, 214, 0], "Hex": "ffd600"}, {"Floss": "3078", "DMC Name": "Golden Yellow Vy Lt", "RGB": [253, 249, 205], "Hex": "fdf9cd"}, {"Floss": "727", "DMC Name": "Topaz Vy Lt", "RGB": [255, 241, 175], "Hex": "fff1af"}, {"Floss": "726", "DMC Name": "Topaz Light", "RGB": [253, 215, 85], "Hex": "fdd755"}, {"Floss": "725", "DMC Name": "Topaz Med Lt", "RGB": [255, 200, 64], "Hex": "ffc840"}, {"Floss": "972", "DMC Name": "Canary Deep", "RGB": [255, 181, 21], "Hex": "ffb515"}, {"Floss": "745", "DMC Name": "Yellow Pale Light", "RGB": [255, 233, 173], "Hex": "ffe9ad"}, {"Floss": "744", "DMC Name": "Yellow Pale", "RGB": [255, 231, 147], "Hex": "ffe793"}, {"Floss": "743", "DMC Name": "Yellow Med", "RGB": [254, 211, 118], "Hex": "fed376"}, {"Floss": "742", "DMC Name": "Tangerine Light", "RGB": [255, 191, 87], "Hex": "ffbf57"}, {"Floss": "741", "DMC Name": "Tangerine Med", "RGB": [255, 163, 43], "Hex": "ffa32b"}, {"Floss": "740", "DMC Name": "Tangerine", "RGB": [255, 139, 0], "Hex": "ff8b00"}, {"Floss": "970", "DMC Name": "Pumpkin Light", "RGB": [247, 139, 19], "Hex": "f78b13"}, {"Floss": "971", "DMC Name": "Pumpkin", "RGB": [246, 127, 0], "Hex": "f67f00"}, {"Floss": "947", "DMC Name": "Burnt Orange", "RGB": [255, 123, 77], "Hex": "ff7b4d"}, {"Floss": "946", "DMC Name": "Burnt Orange Med", "RGB": [235, 99, 7], "Hex": "eb6307"}, {"Floss": "900", "DMC Name": "Burnt Orange Dark", "RGB": [209, 88, 7], "Hex": "d15807"}, {"Floss": "967", "DMC Name": "Apricot Very Light", "RGB": [255, 222, 213], "Hex": "ffded5"}, {"Floss": "3824", "DMC Name": "Apricot Light", "RGB": [254, 205, 194], "Hex": "fecdc2"}, {"Floss": "3341", "DMC Name": "Apricot", "RGB": [252, 171, 152], "Hex": "fcab98"}, {"Floss": "3340", "DMC Name": "Apricot Med", "RGB": [255, 131, 111], "Hex": "ff836f"}, {"Floss": "608", "DMC Name": "Burnt Orange Bright", "RGB": [253, 93, 53], "Hex": "fd5d35"}, {"Floss": "606", "DMC Name": "Orange?Red Bright", "RGB": [250, 50, 3], "Hex": "fa3203"}, {"Floss": "951", "DMC Name": "Tawny Light", "RGB": [255, 226, 207], "Hex": "ffe2cf"}, {"Floss": "3856", "DMC Name": "Mahogany Ult Vy Lt", "RGB": [255, 211, 181], "Hex": "ffd3b5"}, {"Floss": "722", "DMC Name": "Orange Spice Light", "RGB": [247, 151, 111], "Hex": "f7976f"}, {"Floss": "721", "DMC Name": "Orange Spice Med", "RGB": [242, 120, 66], "Hex": "f27842"}, {"Floss": "720", "DMC Name": "Orange Spice Dark", "RGB": [229, 92, 31], "Hex": "e55c1f"}, {"Floss": "3825", "DMC Name": "Pumpkin Pale", "RGB": [253, 189, 150], "Hex": "fdbd96"}, {"Floss": "922", "DMC Name": "Copper Light", "RGB": [226, 115, 35], "Hex": "e27323"}, {"Floss": "921", "DMC Name": "Copper", "RGB": [198, 98, 24], "Hex": "c66218"}, {"Floss": "920", "DMC Name": "Copper Med", "RGB": [172, 84, 20], "Hex": "ac5414"}, {"Floss": "919", "DMC Name": "Red Copper", "RGB": [166, 69, 16], "Hex": "a64510"}, {"Floss": "918", "DMC Name": "Red Copper Dark", "RGB": [130, 52, 10], "Hex": "82340a"}, {"Floss": "3770", "DMC Name": "Tawny Vy Light", "RGB": [255, 238, 227], "Hex": "ffeee3"}, {"Floss": "945", "DMC Name": "Tawny", "RGB": [251, 213, 187], "Hex": "fbd5bb"}, {"Floss": "402", "DMC Name": "Mahogany Vy Lt", "RGB": [247, 167, 119], "Hex": "f7a777"}, {"Floss": "3776", "DMC Name": "Mahogany Light", "RGB": [207, 121, 57], "Hex": "cf7939"}, {"Floss": "301", "DMC Name": "Mahogany Med", "RGB": [179, 95, 43], "Hex": "b35f2b"}, {"Floss": "400", "DMC Name": "Mahogany Dark", "RGB": [143, 67, 15], "Hex": "8f430f"}, {"Floss": "300", "DMC Name": "Mahogany Vy Dk", "RGB": [111, 47, 0], "Hex": "6f2f00"}, {"Floss": "3823", "DMC Name": "Yellow Ultra Pale", "RGB": [255, 253, 227], "Hex": "fffde3"}, {"Floss": "3855", "DMC Name": "Autumn Gold Lt", "RGB": [250, 211, 150], "Hex": "fad396"}, {"Floss": "3854", "DMC Name": "Autumn Gold Med", "RGB": [242, 175, 104], "Hex": "f2af68"}, {"Floss": "3853", "DMC Name": "Autumn Gold Dk", "RGB": [242, 151, 70], "Hex": "f29746"}, {"Floss": "3827", "DMC Name": "Golden Brown Pale", "RGB": [247, 187, 119], "Hex": "f7bb77"}, {"Floss": "977", "DMC Name": "Golden Brown Light", "RGB": [220, 156, 86], "Hex": "dc9c56"}, {"Floss": "976", "DMC Name": "Golden Brown Med", "RGB": [194, 129, 66], "Hex": "c28142"}, {"Floss": "3826", "DMC Name": "Golden Brown", "RGB": [173, 114, 57], "Hex": "ad7239"}, {"Floss": "975", "DMC Name": "Golden Brown Dk", "RGB": [145, 79, 18], "Hex": "914f12"}, {"Floss": "948", "DMC Name": "Peach Very Light", "RGB": [254, 231, 218], "Hex": "fee7da"}, {"Floss": "754", "DMC Name": "Peach Light", "RGB": [247, 203, 191], "Hex": "f7cbbf"}, {"Floss": "3771", "DMC Name": "Terra Cotta Ult Vy Lt", "RGB": [244, 187, 169], "Hex": "f4bba9"}, {"Floss": "758", "DMC Name": "Terra Cotta Vy Lt", "RGB": [238, 170, 155], "Hex": "eeaa9b"}, {"Floss": "3778", "DMC Name": "Terra Cotta Light", "RGB": [217, 137, 120], "Hex": "d98978"}, {"Floss": "356", "DMC Name": "Terra Cotta Med", "RGB": [197, 106, 91], "Hex": "c56a5b"}, {"Floss": "3830", "DMC Name": "Terra Cotta", "RGB": [185, 85, 68], "Hex": "b95544"}, {"Floss": "355", "DMC Name": "Terra Cotta Dark", "RGB": [152, 68, 54], "Hex": "984436"}, {"Floss": "3777", "DMC Name": "Terra Cotta Vy Dk", "RGB": [134, 48, 34], "Hex": "863022"}, {"Floss": "3779", "DMC Name": "Rosewood Ult Vy Lt", "RGB": [248, 202, 200], "Hex": "f8cac8"}, {"Floss": "3859", "DMC Name": "Rosewood Light", "RGB": [186, 139, 124], "Hex": "ba8b7c"}, {"Floss": "3858", "DMC Name": "Rosewood Med", "RGB": [150, 74, 63], "Hex": "964a3f"}, {"Floss": "3857", "DMC Name": "Rosewood Dark", "RGB": [104, 37, 26], "Hex": "68251a"}, {"Floss": "3774", "DMC Name": "Desert Sand Vy Lt", "RGB": [243, 225, 215], "Hex": "f3e1d7"}, {"Floss": "950", "DMC Name": "Desert Sand Light", "RGB": [238, 211, 196], "Hex": "eed3c4"}, {"Floss": "3064", "DMC Name": "Desert Sand", "RGB": [196, 142, 112], "Hex": "c48e70"}, {"Floss": "407", "DMC Name": "Desert Sand Med", "RGB": [187, 129, 97], "Hex": "bb8161"}, {"Floss": "3773", "DMC Name": "Desert Sand Dark", "RGB": [182, 117, 82], "Hex": "b67552"}, {"Floss": "3772", "DMC Name": "Desert Sand Vy Dk", "RGB": [160, 108, 80], "Hex": "a06c50"}, {"Floss": "632", "DMC Name": "Desert Sand Ult Vy Dk", "RGB": [135, 85, 57], "Hex": "875539"}, {"Floss": "453", "DMC Name": "Shell Gray Light", "RGB": [215, 206, 203], "Hex": "d7cecb"}, {"Floss": "452", "DMC Name": "Shell Gray Med", "RGB": [192, 179, 174], "Hex": "c0b3ae"}, {"Floss": "451", "DMC Name": "Shell Gray Dark", "RGB": [145, 123, 115], "Hex": "917b73"}, {"Floss": "3861", "DMC Name": "Cocoa Light", "RGB": [166, 136, 129], "Hex": "a68881"}, {"Floss": "3860", "DMC Name": "Cocoa", "RGB": [125, 93, 87], "Hex": "7d5d57"}, {"Floss": "779", "DMC Name": "Cocoa Dark", "RGB": [98, 75, 69], "Hex": "624b45"}, {"Floss": "712", "DMC Name": "Cream", "RGB": [255, 251, 239], "Hex": "fffbef"}, {"Floss": "739", "DMC Name": "Tan Ult Vy Lt", "RGB": [248, 228, 200], "Hex": "f8e4c8"}, {"Floss": "738", "DMC Name": "Tan Very Light", "RGB": [236, 204, 158], "Hex": "eccc9e"}, {"Floss": "437", "DMC Name": "Tan Light", "RGB": [228, 187, 142], "Hex": "e4bb8e"}, {"Floss": "436", "DMC Name": "Tan", "RGB": [203, 144, 81], "Hex": "cb9051"}, {"Floss": "435", "DMC Name": "Brown Very Light", "RGB": [184, 119, 72], "Hex": "b87748"}, {"Floss": "434", "DMC Name": "Brown Light", "RGB": [152, 94, 51], "Hex": "985e33"}, {"Floss": "433", "DMC Name": "Brown Med", "RGB": [122, 69, 31], "Hex": "7a451f"}, {"Floss": "801", "DMC Name": "Coffee Brown Dk", "RGB": [101, 57, 25], "Hex": "653919"}, {"Floss": "898", "DMC Name": "Coffee Brown Vy Dk", "RGB": [73, 42, 19], "Hex": "492a13"}, {"Floss": "938", "DMC Name": "Coffee Brown Ult Dk", "RGB": [54, 31, 14], "Hex": "361f0e"}, {"Floss": "3371", "DMC Name": "Black Brown", "RGB": [30, 17, 8], "Hex": "1e1108"}, {"Floss": "543", "DMC Name": "Beige Brown Ult Vy Lt", "RGB": [242, 227, 206], "Hex": "f2e3ce"}, {"Floss": "3864", "DMC Name": "Mocha Beige Light", "RGB": [203, 182, 156], "Hex": "cbb69c"}, {"Floss": "3863", "DMC Name": "Mocha Beige Med", "RGB": [164, 131, 92], "Hex": "a4835c"}, {"Floss": "3862", "DMC Name": "Mocha Beige Dark", "RGB": [138, 110, 78], "Hex": "8a6e4e"}, {"Floss": "3031", "DMC Name": "Mocha Brown Vy Dk", "RGB": [75, 60, 42], "Hex": "4b3c2a"}, {"Floss": "B5200", "DMC Name": "Snow White", "RGB": [255, 255, 255], "Hex": "ffffff"}, {"Floss": "White", "DMC Name": "White", "RGB": [252, 251, 248], "Hex": "fcfbf8"}, {"Floss": "3865", "DMC Name": "Winter White", "RGB": [249, 247, 241], "Hex": "f9f7f1"}, {"Floss": "Ecru", "DMC Name": "Ecru", "RGB": [240, 234, 218], "Hex": "f0eada"}, {"Floss": "822", "DMC Name": "Beige Gray Light", "RGB": [231, 226, 211], "Hex": "e7e2d3"}, {"Floss": "644", "DMC Name": "Beige Gray Med", "RGB": [221, 216, 203], "Hex": "ddd8cb"}, {"Floss": "642", "DMC Name": "Beige Gray Dark", "RGB": [164, 152, 120], "Hex": "a49878"}, {"Floss": "640", "DMC Name": "Beige Gray Vy Dk", "RGB": [133, 123, 97], "Hex": "857b61"}, {"Floss": "3787", "DMC Name": "Brown Gray Dark", "RGB": [98, 93, 80], "Hex": "625d50"}, {"Floss": "3021", "DMC Name": "Brown Gray Vy Dk", "RGB": [79, 75, 65], "Hex": "4f4b41"}, {"Floss": "3024", "DMC Name": "Brown Gray Vy Lt", "RGB": [235, 234, 231], "Hex": "ebeae7"}, {"Floss": "3023", "DMC Name": "Brown Gray Light", "RGB": [177, 170, 151], "Hex": "b1aa97"}, {"Floss": "3022", "DMC Name": "Brown Gray Med", "RGB": [142, 144, 120], "Hex": "8e9078"}, {"Floss": "535", "DMC Name": "Ash Gray Vy Lt", "RGB": [99, 100, 88], "Hex": "636458"}, {"Floss": "3033", "DMC Name": "Mocha Brown Vy Lt", "RGB": [227, 216, 204], "Hex": "e3d8cc"}, {"Floss": "3782", "DMC Name": "Mocha Brown Lt", "RGB": [210, 188, 166], "Hex": "d2bca6"}, {"Floss": "3032", "DMC Name": "Mocha Brown Med", "RGB": [179, 159, 139], "Hex": "b39f8b"}, {"Floss": "3790", "DMC Name": "Beige Gray Ult Dk", "RGB": [127, 106, 85], "Hex": "7f6a55"}, {"Floss": "3781", "DMC Name": "Mocha Brown Dk", "RGB": [107, 87, 67], "Hex": "6b5743"}, {"Floss": "3866", "DMC Name": "Mocha Brn Ult Vy Lt", "RGB": [250, 246, 240], "Hex": "faf6f0"}, {"Floss": "842", "DMC Name": "Beige Brown Vy Lt", "RGB": [209, 186, 161], "Hex": "d1baa1"}, {"Floss": "841", "DMC Name": "Beige Brown Lt", "RGB": [182, 155, 126], "Hex": "b69b7e"}, {"Floss": "840", "DMC Name": "Beige Brown Med", "RGB": [154, 124, 92], "Hex": "9a7c5c"}, {"Floss": "839", "DMC Name": "Beige Brown Dk", "RGB": [103, 85, 65], "Hex": "675541"}, {"Floss": "838", "DMC Name": "Beige Brown Vy Dk", "RGB": [89, 73, 55], "Hex": "594937"}, {"Floss": "3072", "DMC Name": "Beaver Gray Vy Lt", "RGB": [230, 232, 232], "Hex": "e6e8e8"}, {"Floss": "648", "DMC Name": "Beaver Gray Lt", "RGB": [188, 180, 172], "Hex": "bcb4ac"}, {"Floss": "647", "DMC Name": "Beaver Gray Med", "RGB": [176, 166, 156], "Hex": "b0a69c"}, {"Floss": "646", "DMC Name": "Beaver Gray Dk", "RGB": [135, 125, 115], "Hex": "877d73"}, {"Floss": "645", "DMC Name": "Beaver Gray Vy Dk", "RGB": [110, 101, 92], "Hex": "6e655c"}, {"Floss": "844", "DMC Name": "Beaver Gray Ult Dk", "RGB": [72, 72, 72], "Hex": "484848"}, {"Floss": "762", "DMC Name": "Pearl Gray Vy Lt", "RGB": [236, 236, 236], "Hex": "ececec"}, {"Floss": "415", "DMC Name": "Pearl Gray", "RGB": [211, 211, 214], "Hex": "d3d3d6"}, {"Floss": "318", "DMC Name": "Steel Gray Lt", "RGB": [171, 171, 171], "Hex": "ababab"}, {"Floss": "414", "DMC Name": "Steel Gray Dk", "RGB": [140, 140, 140], "Hex": "8c8c8c"}, {"Floss": "168", "DMC Name": "Pewter Very Light", "RGB": [209, 209, 209], "Hex": "d1d1d1"}, {"Floss": "169", "DMC Name": "Pewter Light", "RGB": [132, 132, 132], "Hex": "848484"}, {"Floss": "317", "DMC Name": "Pewter Gray", "RGB": [108, 108, 108], "Hex": "6c6c6c"}, {"Floss": "413", "DMC Name": "Pewter Gray Dark", "RGB": [86, 86, 86], "Hex": "565656"}, {"Floss": "3799", "DMC Name": "Pewter Gray Vy Dk", "RGB": [66, 66, 66], "Hex": "424242"}, {"Floss": "310", "DMC Name": "Black", "RGB": [0, 0, 0], "Hex": "000000"}] --------------------------------------------------------------------------------