Created
February 22, 2016 04:29
-
-
Save kernalphage/1d53647ca16af246fbbf to your computer and use it in GitHub Desktop.
given an image, generates a palette of n colors
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
import collections | |
from functools import * | |
import argparse | |
from PIL import Image | |
from colour import Color | |
def interesting_colors(filename, num_colors, steps=32, sample_scale=(200, 200)): | |
""" | |
:rtype: List(Color) | |
:type num_colors: picks the top n colors | |
:type steps: Lower steps, more duplicate colors. Higher steps, more washed out colors. | |
distance from a color that a sample can be before it is considered a different color | |
:type sample_scale: (width,height): Scale down the image to speed up processing time | |
""" | |
im = Image.open(filename).resize(sample_scale) | |
px = im.load() | |
# Shove the pixels into buckets | |
buckets = collections.defaultdict(list) | |
for x in range(im.size[0]): | |
for y in range(im.size[1]): | |
cur = px[x, y] | |
bk = bucket(cur, steps) | |
buckets[bk].append(cur) | |
# get the top n most filled buckets (aka, most common colors) | |
bucket_length = lambda k: len(buckets[k]) | |
topn_keys = sorted(buckets, key=bucket_length, reverse=True)[:num_colors] | |
topn_colors = [color_average(buckets[k]) for k in topn_keys] | |
# | |
topn_colors = sorted(topn_colors, key=lambda c: c.hue) | |
return topn_colors | |
def hue_dist(c1, c2): | |
a = min(c1.hue, c2.hue) | |
b = max(c1.hue, c2.hue) | |
dist1 = b - a | |
dist2 = ((a+1) - b) | |
return min(dist1, dist2) | |
def named_colors( colors ): | |
#lets' make the background 'dark' and unsaturated | |
background = min(colors, key=lambda c: c.saturation + c.luminance * .5) | |
colors.remove(background) | |
#Contrast, baby | |
foreground = max(colors, key=lambda c: c.saturation) | |
colors.remove(foreground) | |
#totally color science | |
accent = max(colors, key=lambda c: hue_dist(foreground,c) + abs(foreground.luminance - c.luminance) * .5) | |
colors.remove(accent) | |
rest = sorted(colors, key=lambda c: c.hue) | |
return {"background": background, "foreground": foreground, "accent": accent, "rest": rest} | |
#### Below are helper functions | |
def rgb_clamp(x): | |
return max(0, min(x, 255)) | |
def hx(r, g, b): | |
return "#{0:02x}{1:02x}{2:02x}".format(rgb_clamp(r), rgb_clamp(g), rgb_clamp(b)) | |
def color_dist(a, b): | |
return abs(a[0] - b[0]) + abs(a[1] - b[1]) + abs(a[2] - b[2]) | |
def color_sum(c1, c2): | |
return c1[0] + c2[0], c1[1] + c2[1], c1[2] + c2[2] | |
def color_div(c1, s): | |
return c1[0] // s, c1[1] // s, c1[2] // s | |
# get the 'average' color: sum the (r,g,b) components and then divide by number of elements | |
# I'm no color scientist, but this is a good place for improvement | |
def color_average(l): | |
return Color(hx(*color_div(reduce(color_sum, l), len(l)))) | |
def bucket(color, steps): | |
bucket_sz = 256 // steps | |
r = color[0] // steps | |
g = color[1] // steps | |
b = color[2] // steps | |
return r + g * bucket_sz + b * bucket_sz * bucket_sz | |
def bounds(s): | |
""" | |
:rtype: (width,height) | |
""" | |
try: | |
x, y = map(int, s.split(',')) | |
return x, y | |
except: | |
raise argparse.ArgumentTypeError("Coordinates must be x,y") | |
if __name__ == "__main__": | |
parser = argparse.ArgumentParser() | |
parser.add_argument('file', help="the image to parse") | |
parser.add_argument('--samples', help="The number of resulting colors", default=5, type=int) | |
parser.add_argument('--fidelity', help="the size of the buckets to use (advanced, untested with non-power of two)", default=16) | |
parser.add_argument('--size', help="resize image before sampling", default="100,100", type=bounds) | |
args = parser.parse_args() | |
colors = interesting_colors(args.file, args.samples, args.fidelity, args.size) | |
for c in colors: | |
print(c.hex) | |
print(named_colors(colors)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example Output:
Take an image and create a color palette. Could be used for accent colors, stylized thumnails, etc.