Saturday, July 22, 2017

Using PIL to leave only specific colors in an image.

Here's a step by step guide on using PIL to find the 3 most common colors in an image and create a grayscale image that highlights only these colors.

Start by importing PIL and returning a list of the RBG codes
from PIL import Image 
def get_pixels(image):
im =
pixels = []
pix = im.load()
width,height = im.size
img_width, img_height = 0,0
for _ in range(width * height):
if img_width == width:
img_height += 1
img_width = 0
pixels.append(pix[img_width, img_height])
img_width += 1
return pixels
then you'll need to find the most common colours

def pixel_index(pixels):
pix_index = {}
for pixel in pixels:
if pixel in pix_index.keys():
pix_index[pixel] += 1
pix_index[pixel] = 1
return pix_index 

and from there get the top 3 from the result

def sort_pixels(pixels, n=3, offset_range=50): #return top n pixels outside the offset_range deviation
pixel_copy = copy.deepcopy(pixels)
result = []
result.append(max(pixel_copy, key=lambda key: pixel_copy[key]))
while len(result) < n:
find_max = max(pixel_copy, key=lambda key: pixel_copy[key])
for val in result:
off_1 = abs(find_max[0] - val[0])
off_2 = abs(find_max[1] - val[1])
off_3 = abs(find_max[2] - val[2])
if off_1 > offset_range or off_2 > offset_range or off_3 > offset_range and find_max not in result:
del pixel_copy[find_max]
return result
 Okay now with this we can start creating the new image, I've put each functionality into a separate function so it's easier to explain and replace logic later on. Note that the task of finding the top colours using "pixel_index" is slow because it iterates over every pixel while creating a dictionary. It should be far more quicker if you create a new function with picked rbg codes you want to keep.

def pixels_check(top_pixels, pixel):
for pix in top_pixels:
result = valid_check(pix, pixel)
if result == True:
return True
return False
def valid_check(first_color, second_color, offset=10):
off_1 = abs(first_color[0] - second_color[0])
off_2 = abs(first_color[1] - second_color[1])
off_3 = abs(first_color[2] - second_color[2])
if off_1 < offset and off_2 < offset and off_3 < offset:
return True
return False

pixels_check will use the result of list of top colors and return True if the color is almost close to one in the top colors.

Finally the main function will get a string of the color name and create the new file. Remember to create a folder "colored" where your testing.

def colourfest(image):
image_name = image
pix = pixels = get_pixels(image)
pix = pixel_index(pix)
top_pixels = sort_pixels(pix)
image =
pix_load = image.load()
width,height = image.size
new_image =, image.size)
img_width, img_height = 0,0
for _ in range(width * height):
if img_width == width: #checks if width exceeded and jumps to next row
img_height += 1
img_width = 0
pixel = pix_load[img_width, img_height]
top_pix = pixels_check(top_pixels, pixel)
if top_pix == True:
new_image.putpixel([img_width, img_height],pixel)
gray = (pixel[0] + pixel[1] + pixel[2])/3
new_image.putpixel([img_width, img_height], (gray, gray, gray))
img_width += 1'colored/'+image_name)
Now you can run this code and get your colourfest image

In [1]: from colorfest import *
In [2]: colourfest('58c7814d7857c.jpeg')
Here are some results from running this

You can find the code on github

No comments:

Post a Comment

Color Picker: Using Python and PIL

From the last post I've finished my color picker and you can see the live version here . What is basically does is from the colors you&#...