Skip to content

Instantly share code, notes, and snippets.

@wonderb0lt
Created April 8, 2014 18:36
Show Gist options
  • Save wonderb0lt/10168543 to your computer and use it in GitHub Desktop.
Save wonderb0lt/10168543 to your computer and use it in GitHub Desktop.
Average colour of a Wand Image
import colorsys
from wand.image import Image
from wand.color import Color
def get_average(image, lower_threshold=50, upper_threshold=230, sample_size=6, liveliness_boost=0.3):
"""
Calculates the average of color in the given image and gives it a bit of a "colour boost" by incrementing their hue
and saturation. This function's default are also slightly biased to prefer "lighter" colors (looks better on the
mostly white backgrounds where the images will be displayed).
If no pixels could be considered due to the ``lower_threshold`` and ``upper_threshold`` a standard grey
(``#e1e1e1``) is returned
:param image: The image to calculate the average vor
:param lower_threshold: The lower value threshold for a color channel to be considered significant (0-255)
:param upper_threshold: The upper value threshold for a color channel to be considered significant (0-255)
:param sample_size: The width and height the image will be sampled down to (in pixels)
:param liveliness_boost: The boost that the Saturation and Value of the result receives (in percent)
:type image: Image
:type lower_threshold: int
:type upper_threshold: int
:type sample_size: int
:type liveliness_boost: float
:returns: A tuple with the :py:class:`wand.color.Color` and the hexstring of the average color
:rtype: tuple
"""
pixels = 0
red, green, blue = 0, 0, 0
# Here come a few inner methods which check whether or not a colour should be considered for averaging
def above_lower_threshold(colour):
return colour.red * 255 > lower_threshold \
or colour.green * 255 > lower_threshold \
or colour.blue * 255 > lower_threshold
def below_upper_threshold(colour):
return colour.red * 255 < upper_threshold \
and colour.green * 255 < upper_threshold \
and colour.blue * 255 < upper_threshold
def significant(colour):
return above_lower_threshold(colour) and below_upper_threshold(colour) and colour.alpha * 255 > 10
# This method "boosts" the image by increasing the hue and saturation by ``liveliness_boost``%
def boost(r, g, b):
h, s, v = colorsys.rgb_to_hsv(r, g, b)
boosted_s, boosted_v = min(s * (1 + liveliness_boost), 255.0), min(v * (1 + liveliness_boost), 255.0)
return colorsys.hsv_to_rgb(h, boosted_s, boosted_v)
# We need to sample down the image because calculating the histogram for bigger images takes up to a few minutes
image.sample(width=sample_size, height=sample_size)
for colour, count in image.histogram.items():
# Only "significant" colors are counted, they shan't be too bright and not to dark, and not be transparent
if significant(colour):
red += colour.red * count * 255
green += colour.green * count * 255
blue += colour.blue * count * 255
pixels += count
try:
r, g, b = boost(red / pixels, green / pixels, blue / pixels)
except ZeroDivisionError:
r, g, b = 0xe1, 0xe1, 0xe1
hex_string = '#{0:02x}{1:02x}{2:02x}'.format(int(r), int(g), int(b))
return Color(hex_string), hex_string
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment