Created
January 23, 2022 03:07
-
-
Save aminnj/0f1b18420108ea248fcfa9f856e27949 to your computer and use it in GitHub Desktop.
Get color palette of image on the clipboard
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# requires | |
# numpy, PIL (and IPython, if you keep in the HTML repr stuff) | |
import itertools | |
import numpy as np | |
def _image_from_clipboard(algo="png"): | |
if algo == "jpeg": | |
from PIL import ImageGrab | |
im = ImageGrab.grabclipboard().convert("RGB") | |
elif algo == "png": | |
# adapted from https://pillow.readthedocs.io/en/stable/_modules/PIL/ImageGrab.html#grabclipboard | |
import sys | |
import os | |
import tempfile | |
import subprocess | |
from PIL import Image | |
fh, filepath = tempfile.mkstemp(".png") | |
os.close(fh) | |
commands = [ | |
'try', | |
f' write (the clipboard as «class PNGf») to (open for access "{filepath}" with write permission)', | |
'end try', | |
] | |
script = ["osascript"] | |
for command in commands: | |
script += ["-e", command] | |
subprocess.call(script) | |
im = None | |
if os.stat(filepath).st_size != 0: | |
im = Image.open(filepath) | |
im.load() | |
os.unlink(filepath) | |
return im | |
def colors_from_clipboard(algo="png"): | |
""" | |
This returns a nice HTML repr of the common colors and their hexadecimal | |
representation from the picture in the system clipboard. Useful for | |
getting a hexadecimal color palette given a plot. | |
""" | |
from PIL import ImageGrab | |
from IPython.display import HTML | |
img = _image_from_clipboard(algo=algo) | |
arr = np.array(img) | |
arr = np.array(img)[:,:,(0,1,2)] | |
r = arr[:,:,0].flatten().astype(np.uint32) | |
g = arr[:,:,1].flatten().astype(np.uint32) | |
b = arr[:,:,2].flatten().astype(np.uint32) | |
sel = ((r > 230) & (g > 230) & (b > 230)) | ((r < 20) & (g < 20) & (b < 20)) | |
r = r[~sel] | |
g = g[~sel] | |
b = b[~sel] | |
all_rgb = (r << 16) | (g << 8) | b | |
unique_rgb, counts = np.unique(all_rgb, return_counts=True) | |
top_rgb = unique_rgb[np.argsort(counts)[-12:]][::-1] # most to least common | |
def dist_rgb(rgb1, rgb2): | |
r1 = (rgb1 >> 16) & 0xff | |
g1 = (rgb1 >> 8) & 0xff | |
b1 = (rgb1 >> 0) & 0xff | |
r2 = (rgb2 >> 16) & 0xff | |
g2 = (rgb2 >> 8) & 0xff | |
b2 = (rgb2 >> 0) & 0xff | |
return ((r1-r2)**2 + (b1-b2)**2 + (g1-g2)**2)**0.5 | |
if len(top_rgb) > 1: | |
deduped_rgb = [top_rgb[0]] | |
for i,rgb in enumerate(top_rgb[1:],1): | |
closest = min([dist_rgb(rgb,v) for v in top_rgb[:i]]) | |
if closest > 6: | |
deduped_rgb.append(rgb) | |
top_rgb = deduped_rgb | |
def rgb_to_hex(v): | |
r = (v >> 16) & 0xff | |
g = (v >> 8) & 0xff | |
b = (v >> 0) & 0xff | |
return f"{r:02x}{g:02x}{b:02x}".upper() | |
from IPython.display import HTML | |
hex_values = list(map(rgb_to_hex,top_rgb)) | |
body = "<table>" | |
for hv in hex_values: | |
body += f'<tr style="background:white;"><td><svg width="40" height="10"><rect width="40" height="10" style="fill:#{hv};stroke-width:0;"/></svg></td><td><pre>#{hv}</pre></td></tr>\n' | |
body += "</table>" | |
return HTML(body) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment