Skip to content

Instantly share code, notes, and snippets.

@Syncrossus
Last active October 4, 2019 11:14
Show Gist options
  • Select an option

  • Save Syncrossus/0546222a3c00b6fc953cad732f4fba6e to your computer and use it in GitHub Desktop.

Select an option

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
Copy Markdown
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