I created a web application that included screenshots of about 190 country’s national statistics agencies website home page. I created the site to host the results of comparisons of each country’s website home page features.
One of the features I wanted to include was the top 5 colors used on each home page. For example here is an image of the Central Statistical Office of Zambia website home page.
I wanted a list of the top 5 colors used in the web page, in my case I wanted these as a list of rgb color values that I could save to a database and use to create a color palette image.
For example the Central Statistical Office of Zambia website home page top 5 rgb colors were:
- 138, 187, 223
- 174, 212, 235
- 101, 166, 216
- 93, 92, 88
- 242, 245, 247
These 5 colors were not equally distributed on the home page and when plotted as stacked bar plot and saved as an image, looked like this:
How to identify top 5 colors on webpage
Identifying dominant colors in an image is a common task for a variety of use cases so there was a number of options available.
The general technique essentially involves reducing an image to a list of pixels and then identifying each pixel’s color and the relative proportion of that color to all other colors and then taking the top 5 pixel counts to get top 5 colors.
However a web page can have many similar colors for example many shades of blue. So identification of colors involves a statistical categorization and enumeration to group similar colors into one category, for example, group all shades of blue into one ‘blue’ category.
There are a couple of different statistical methods to do this categorization that are discussed below.
Modified Median Cut Quantization
This method involves counting image pixels by color and charting them on a histogram from which peaks are counted to get dominant colors.
This method is used in a Python module called color-thief-py. This module uses Pillow to process the image and modified median cut quantization
I used this module in the code below where I loop through a folder of screenshots to open image and then pass it to color-thief-py to process. Then the top 5 colors’ rgb strings are written into my database as a list so they can be used later.
import os, os.path from PIL import Image import psycopg2 from colorthief import ColorThief conn_string = \ "host='localhost' \ dbname='databasename' \ user='username' \ password='userpassword'" conn = psycopg2.connect(conn_string) cur = conn.cursor() ## dev paths screenshots_path = 'C:/screenshots/' screenshots_dir = os.listdir(screenshots_path) for screenshot in screenshots_dir: if screenshot != 'Thumbs.db': img = Image.open(screenshots_path + screenshot) width, height = img.size quantized = img.quantize(colors=5, kmeans=3) palette = quantized.getpalette()[:15] convert_rgb = quantized.convert('RGB') colors = convert_rgb.getcolors(width*height) color_str = str(sorted(colors, reverse=True)) color_str = str([x[1] for x in colors]) print screenshot + ' ' + str(img.size[1]) + ' ' + color_str cur.execute("UPDATE screenshots \ set color_palette = %s, \ height = %s \ WHERE filename like %s", \ (str(color_str),img.size[1], '%' + screenshot + '%',)) conn.commit() cur.close() conn.close()
K-means clustering
Another statistical method to identify dominant colors in an image is using K-means clustering to group image pixels by rgb values into centroids. Centroid’s with highest counts are identified as dominant colors.
This method is used in the process I found on this awesome blog post on pyimagesearch.com.
This method also uses Python module sklearn k-means to identify the dominant colors. It produced very similar results to the other method.
This code from the pyimagesearch website used Python module matplotlib to create plot of the palette and saved these as images. I modified the code to instead simply save the rgb color values as strings to insert into database and save the matplotlib palette rendered plot as an image. I added plt.close() after each loop to close the rendered plot after the plot image was saved because if they aren’t closed, they accumulate in memory and crashed the program.
# USAGE # python color_kmeans.py --image images/jp.png --clusters 3 # import the necessary packages from sklearn.cluster import KMeans import matplotlib.pyplot as plt import argparse import utils import cv2 import os, os.path import csv # construct the argument parser and parse the arguments #ap = argparse.ArgumentParser() #ap.add_argument("-i", "--image", required = True, help = "Path to the image") #ap.add_argument("-c", "--clusters", required = True, type = int, help = "# of clusters") #args = vars(ap.parse_args()) screenshots_path = 'screenshots/' screenshot_palette = list() ## Create csv file to write results to file = csv.writer(open('palettes.csv', 'wb')) file.writerow(['screenshot','palette']) screenshots_dir = os.listdir(screenshots_path) for screenshot in screenshots_dir: if screenshot != 'Thumbs.db': print screenshot # load the image and convert it from BGR to RGB so that # we can dispaly it with matplotlib image = cv2.imread(screenshots_path + screenshot) image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) # show our image #plt.figure() #plt.axis("off") #plt.imshow(image) #plt.close() # reshape the image to be a list of pixels image = image.reshape((image.shape[0] * image.shape[1], 3)) # cluster the pixel intensities clt = KMeans(n_clusters = 3) clt.fit(image) # build a histogram of clusters and then create a figure # representing the number of pixels labeled to each color hist = utils.centroid_histogram(clt) bar = utils.plot_colors(hist, clt.cluster_centers_) #color_palette = utils.plot_colors(hist, clt.cluster_centers_) #print color_palette #row = (screenshot, [tuple(x) for x in color_palette]) #screenshot_palette.append(row) #print row #print row[0] #print row[1] # show our color bar plt.figure() plt.axis("off") plt.imshow(bar) plt.show() plt.savefig('palettes/' + screenshot) plt.close() #file.writerow([ # row[0].encode('utf-8', 'ignore'), # row[1], # ])
Oh wow. I was looking for a way to extract the dominant color from my pictures and stumbled upon your article. As it turns out, I am Zambia!! Thank you for the code. It works perfectly.