def reduce_palette(palette, image): # No-op if palette is smol if len(palette) <= 256: return palette palette_triplets = [x["rgb"] for x in palette] best_colours = set() # Get image palette in RGB triplets im = image.convert("P", palette=Image.ADAPTIVE, colors=256) image_palette = im.getpalette() my_colours = [] for i in range(0, len(image_palette), 3): my_colours.append(image_palette[i:i + 3]) # Get nearest colour https://stackoverflow.com/a/22478139 tree = sp.KDTree(palette_triplets) for colour in my_colours: _, result = tree.query(colour) best_colours.add(rgb2hex(palette_triplets[result])) # Stick best_colours at the front of our palette first_colours = [] for item in palette: if item["hex"] in best_colours: first_colours.append(item) first_colours += palette return first_colours
def generate_term_chart(chartimage, pal, render, palette_name, data): def c(text, bg=None, fg=None): def color(rgb, code): if not rgb: return "" R, G, B = rgb return f"\033[{code};2;{R};{G};{B}m" def foreground(rgb=None): return color(rgb, "38") def background(rgb=None): return color(rgb, "48") def reset(): return "\033[0;00m" result = foreground(fg) + background(bg) + text + reset() return result def star(rgb, render=False): p = helpers.rgb2hex(rgb) if render: return c(" ", bg=rgb) else: return c(legend[p], fg=rgb) + " " legend, styles, histogram = get_legend(chartimage) headers = [ "*", palette.get_identity_name(palette_name), f"{palette_name} code" ] table = [] result = "\n" for idx, h in enumerate(reversed(histogram)): count, rgb = h color = helpers.rgb2hex(rgb) thread = palette.thread_name(rgb, pal) code = thread["code"] symbol = star(rgb, render=render) table.append([symbol, str(count), code]) result += tabulate(table, headers=headers) result += "\n\n" for y in range(0, chartimage.height): row = [] for x in range(0, chartimage.width): rgb = chartimage.getpixel((x, y)) row.append(star(rgb, render=render)) result += "".join(row) + "\n" result += "\n" + data return result
def get_legend(chartimage): legend = {} styles = {} histogram = sorted(chartimage.getcolors()) STARS = helpers.STARS for idx, x in enumerate(histogram): rgb = x[1] h = helpers.rgb2hex(rgb) star = STARS[idx % len(STARS)] sclass = helpers.col_class(h) # Choose the best text colour if (rgb[0] * 0.299 + rgb[1] * 0.587 + rgb[2] * 0.114) > 186: color = "black" else: color = "lightgray" styles[sclass] = {"bg": h, "rgb": rgb, "c": color, "star": star} legend[helpers.rgb2hex(x[1])] = STARS[idx % len(STARS)] return legend, styles, histogram
def star(rgb, render=False): p = helpers.rgb2hex(rgb) if render: return c(" ", bg=rgb) else: return c(legend[p], fg=rgb) + " "
def generate_html_chart( image_name, chartimage, palette_name, pal, render=False, guidelines=False, data="", ): html = [ f'<html><meta charset="UTF-8" /><title>ih - {nicename(image_name)}</title>' ] html.append( '<link rel="icon" href="data:image/svg+xml,%3csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22%3e%3ctext y=%22.9em%22 font-size=%2290%22%3e%f0%9f%a7%b6%3c/text%3e%3c/svg%3e" />' ) with open(helpers.base_path("styling").joinpath("styling.css")) as s: html.append("<style>") if guidelines: html.append(":root { --border: lightgrey; }") else: html.append(":root { --border: black; }") html.append("".join(s.readlines()) + "</style>") if render: html.append( dedent(""" <style> .s { background-image: url('%s'); background-size: cover; border: none; } .r { border: none } .chart { border: 1px solid black } </style> """ % palette.get_thread_image(palette_name))) legend, styles, histogram = get_legend(chartimage) after = {} html.append("<style>") for _, x in enumerate(styles): y = styles[x] html.append(".%s { background-color: %s; color: %s }" % (x, y["bg"], y["c"])) if not render: html.append('.%s::after { content: "%s\ufe0e" }' % (x, y["star"])) if not render: html.append('.%s::after { content: "%s" }' % (helpers.col_class(helpers.WHITECOL), helpers.WHITESTAR)) html.append("</style>") html.append('<div class="container">') html.append('<div class="left-content">') html.append('<div class="legend_div"><table class="legend">') html.append(( f"<tr><td>X</td><td class='label'>{palette.get_identity_name(palette_name)}</td>" f"<td class='label'>{palette_name} code</td></tr>")) # Generate legend for idx, h in enumerate(reversed(histogram)): count, rgb = h color = helpers.rgb2hex(rgb) thread = palette.thread_name(rgb, pal) code = thread["code"] html.append("<tr>" + helpers.color_cell( color=color, star=legend[color], thread=False, legend=True) + "<td>{}</td><td>{}</td></tr>".format(count, code)) html.append("</table></div>") html.append(f'<div class="debug">{data}</div>') # If using guidelines, enable printhacks if guidelines: with open(helpers.base_path("styling").joinpath("styling.html")) as s: html.append("".join(s.readlines())) html.append("</div>") # end left-content html.append("<div class='page-break'></div>") # force page break html.append('<div class="right-content"><div class="chart">') # If using guidelines, expand the image to a whole number of guidelines first. if guidelines: chartimage = chartimage.convert("RGBA") xpad = GUIDE - (chartimage.width % GUIDE) ypad = GUIDE - (chartimage.height % GUIDE) padding = (floor(ypad / 2), ceil(xpad / 2), ceil(ypad / 2), floor(xpad / 2)) chartimage = ImageOps.expand(chartimage, padding, fill=GUIDECOL) # Generate Chart (TODO(glasnt): make this less terrible) CENTER = True for y in range(0, chartimage.height): row = [] for x in range(0, chartimage.width): guide_x, guide_y = False, False if guidelines: if x % GUIDE == GUIDE - 1: guide_x = True if y % GUIDE == GUIDE - 1: guide_y = True rgb = chartimage.getpixel((x, y)) if rgb == GUIDECOL: row.append(helpers.guide_cell([guide_x, guide_y])) continue p = helpers.rgb2hex(rgb) center_flag = False if not render: if CENTER: if chartimage.height / 2 - 1 <= y and chartimage.width / 2 - 1 <= x: center_flag = True CENTER = False row.append( helpers.color_cell( color=p, star=legend[p], center=center_flag, guide=[guide_x, guide_y], )) html.append("<div class='r'>" + "".join(row) + "</div>") html.append("</div></div></div></html>") return "\n".join(html)