Skip to content

Instantly share code, notes, and snippets.

@Syncrossus
Last active October 4, 2019 11:14
Show Gist options
  • Save Syncrossus/0546222a3c00b6fc953cad732f4fba6e to your computer and use it in GitHub Desktop.
Save Syncrossus/0546222a3c00b6fc953cad732f4fba6e to your computer and use it in GitHub Desktop.
Finds the dominant color in an image. Requires files from https://gist.github.com/Syncrossus/38e2886205602443620c871957ddfdd6
import sys
from math import ceil
from hsv_to_rgb import hsv_to_rgb
from rgb_to_hsv import rgb_to_hsv
def compress_color(pixel, level=16):
""" Compresses color by a specified factor.
Compressed colors are rounded up.
Args:
pixel<int, int, int>: an RGB triple to compress
level<int>: the desired amount of compression
Returns:
r, g, b: compressed RGB values
"""
r, g, b = pixel
# Using the ceil function here rounds up the colors in order
# for the compressed colors to always be at least as vibrant
# as in the original. Otherwise, vibrancy is easily lost.
r = min(ceil(r / level) * level, 255)
g = min(ceil(g / level) * level, 255)
b = min(ceil(b / level) * level, 255)
return r, g, b
def histogram(img):
""" Creates a histogram of an image.
This is a dict of the form {pixel_value: number}
Args:
img<list<list<?>>>: an image represented as a list
of rows with each row a list of pixels
Return:
histogram<dict<?:int>>: the histogram of the image
"""
histogram = {}
for row in img:
for px in row:
if px in histogram.keys():
histogram[px] += 1
else:
histogram[px] = 1
return histogram
def get_dom_color(histogram):
""" Finds the dominant color in a histogram of HSV pixels
Args:
histogram<dict<([0-360],[0-1],[0-1]):int>>:
an HSV histogram as returned by _histogram()
Return:
dom_color<[0-360],[0-1],[0-1]>: the dominant color, an HSV triple
"""
counts = [v for k, v in histogram.items()]
total_px = sum(counts)
# eliminating colors that are less than 1% of the image
min_number_cutoff = 0.01 * total_px
histogram = {k: v for k, v in histogram.items() if v >= min_number_cutoff}
counts = [v for k, v in histogram.items()]
colors = list(histogram.keys())
scores = {}
for i in range(len(colors)):
_, s, v = colors[i]
score = s + v + (counts[i] / total_px)
scores[score] = colors[i]
dom_color = scores[max(scores.keys())]
return dom_color
def read_img(f):
""" Reads the image. This returns a tuple of the form
(height, width, raw_image_data, other_metadata_dict)
"""
img = png.Reader(file=f).read()
raw_img = img[2]
step = 4 if img[3]['alpha'] else 3
processed_img = []
for row in raw_img:
new_row = []
for i in range(0, len(row), step):
r, g, b = row[i], row[i + 1], row[i + 2]
new_row.append((r, g, b))
processed_img.append(new_row)
return processed_img
def convert_image(img, fun, args=()):
""" Applies a pixel-wise function to an entire image.
Args:
img <list<list<tuple>>>: an image represented as a list of rows
with each row a list of pixels with each pixel a tuple
fun <func>: the function to apply
args <tuple>: arguments to pass on to the pixel-wise function
Return:
new_img: a new image (same representation as argument) after
the pixel-wise function has been applied
"""
new_img = []
for row in img:
new_row = []
for px in row:
new_row.append(fun(px, *args))
new_img.append(new_row)
return new_img
if __name__ == '__main__':
# Getting file name
cmd_args = sys.argv[1:]
if len(cmd_args) == 1:
fname = cmd_args[0]
else:
fname = input('Filename : ')
# Processing the image
with open(fname, 'rb') as f:
img = read_img(f)
img = convert_image(img, compress_color, (32,))
img = convert_image(img, rgb_to_hsv)
# Extracting the dominant color and converting it to RGB
dom_color = get_dom_color(histogram(img))
dom_color = hsv_to_rgb(dom_color)
print(dom_color)
@Syncrossus
Copy link
Author

This code is released under the WTFPL.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment