Reducing the number of colors in an image is also called Color quantization. It’s commonly used for generating GIF images which currently supports only 256 colors. The general idea is, group similar colors in an image into regions, replace them with the color which closely resembles or represents the region. This color is also called the representative color.
Lets understand the simplest color reduction algorithm called the Uniform Quantization.
A single color pixel represented by an RGB tuple can take values between 0-255 each. The steps involved in uniform quantization are:
- Find 3 color regions by dividing the color spaces R,G,B into equal parts.
- For each pixel, assign its R, G and B components to the corresponding color region.
- Find the color that represents the region by taking the average of all colors in it.
- Go through the pixels in the image and replace the color with the representative color of the region.
For example, the color space red is divided into regions R(0-31), R(32-63), R(64-95), R(96-127), R(128-159), R(160-191), R(192-223), R(224-255). If a color C is (3,34,189), it will go into region R(0-31), G(32-63) and B(160-191). Now if the average of all the Red colors falling in region R(0-31) is 16, Green G(32-63) is 48 and Blue B(160-191) is 178, then the color C = (3,34,189) will become C = (16, 48, 178) after quantization.
import matplotlib.pyplot as plt import numpy as np from skimage.io import imread, imsave car = imread('resources/auto.jpg') def get_region_index(color_value): # colors divided into 8 regions for each color space eight_regions = [[0,31], [32,63], [64,95], [96,127], [128,159], [160,191], [192,223], [224,255]] for index, region_value in enumerate(eight_regions): if color_value >= region_value and color_value <= region_value: return index r_region_mappings = [,,,,,,,] g_region_mappings = [,,,,,,,] b_region_mappings = [,,,,,,,] # loop through all pixels and the put the colors into the respective color regions for rows in car: for pixel in rows: red = pixel green = pixel blue = pixel #find the index where the color is supposed to go and add it r_region_mappings[get_region_index(red)].append(red) g_region_mappings[get_region_index(green)].append(green) b_region_mappings[get_region_index(blue)].append(blue) # find the color that represents each region r_representative_color_per_region = [0,0,0,0,0,0,0,0] g_representative_color_per_region = [0,0,0,0,0,0,0,0] b_representative_color_per_region = [0,0,0,0,0,0,0,0] # find the average of all colors in the regions to find the representative color for index in range(8): r_representative_color_per_region[index] = np.mean(r_region_mappings[index]).astype(int) g_representative_color_per_region[index] = np.mean(g_region_mappings[index]).astype(int) b_representative_color_per_region[index] = np.mean(b_region_mappings[index]).astype(int) # now replace all colors in the image with their uniform quantized representative colors new_car = np.copy(car) for rindex, rows in enumerate(car): for cindex, pixel in enumerate(rows): red = pixel green = pixel blue = pixel new_car[rindex, cindex] = r_representative_color_per_region[get_region_index(red)] new_car[rindex, cindex] = g_representative_color_per_region[get_region_index(green)] new_car[rindex, cindex] = b_representative_color_per_region[get_region_index(blue)] imsave('uniform_quantized.jpg', new_car)
You can understand the entire process from my notebook attached below.
Below image shows my sample image reduced to 512 color palette.
You can also divide the regions into 4*4*4 and get 64 color palette or 8*8*4 and get a 256 color palette.