Link to this Jupyter notebook and the needed files micmacs_resized.jpg, micmacs_original.jpg

In [4]:
from scipy import misc
import imageio
import numpy as np
from numpy import random
from IPython.display import Image

# read sample image (we cheat and use resized (smaller) image first)
img = imageio.imread('/content/micmacs_resized.jpg')
# prints pixel by pixel RGB info
img
Out[4]:
Array([[[66, 62, 15],
        [69, 65, 20],
        [70, 66, 21],
        ...,
        [28, 25,  6],
        [28, 25,  6],
        [30, 25,  6]],

       [[72, 67, 25],
        [71, 67, 22],
        [71, 67, 20],
        ...,
        [29, 26,  7],
        [29, 26,  7],
        [28, 25,  6]],

       [[67, 62, 20],
        [66, 62, 17],
        [68, 64, 16],
        ...,
        [30, 27,  8],
        [29, 26,  7],
        [28, 25,  6]],

       ...,

       [[19, 13,  1],
        [19, 14,  8],
        [23, 14,  7],
        ...,
        [20, 11,  2],
        [14, 10,  1],
        [11, 11,  3]],

       [[16, 12,  0],
        [18, 11,  3],
        [24, 11,  2],
        ...,
        [18, 11,  3],
        [13, 10,  3],
        [ 9,  9,  1]],

       [[19, 15,  3],
        [18, 11,  1],
        [24, 10,  0],
        ...,
        [18, 11,  5],
        [12, 12,  4],
        [ 9,  9,  1]]], dtype=uint8)
In [ ]:
Image(filename='micmacs_resized.jpg') 
Out[ ]:
In [ ]:
# see what the image is
# misc.imshow(img)
In [5]:
# check the image dimension (it is 128x300 pixel)
img.shape
Out[5]:
(124, 300, 3)
In [6]:
# check the RGB intensities
img[1,2,] # red, green, blue intensities for pixel 1x2 (index starts from 0)
Out[6]:
Array([71, 67, 20], dtype=uint8)
In [7]:
k = 10 # number of clusters (colors)

# randomly sample colors from the image
means = np.zeros((k,3))
for i in range(0,k):
    randx=random.randint(0,img.shape[0]-1)
    randy=random.randint(0,img.shape[1]-1)
    means[i,] = img[randx,randy,]
means
Out[7]:
array([[ 23.,  39.,  10.],
       [120.,  89.,   6.],
       [ 58.,  44.,   5.],
       [ 60.,  38.,   1.],
       [ 14.,  26.,   6.],
       [103.,  74.,   0.],
       [ 89.,  71.,  21.],
       [138.,  88.,   3.],
       [ 84.,  63.,  16.],
       [ 55.,  56.,   0.]])
In [9]:
max_iterations = 20;
for itr in range(0,max_iterations):
    # means to be calculated in this iteration
    new_means = np.zeros((k,3))
    # keeps number of members for each cluster
    num_elements = np.zeros((k, 1))
    # loops over all pixels
    for i in range(0, img.shape[0]):
        for j in range(0, img.shape[1]):
            # extracts pixel ixj RGB color
            r = img[i,j,0]
            g = img[i,j,1]
            b = img[i,j,2]
            # computes euclidean distance between pixel ixj and k clusters
            distance = np.sqrt(np.sum(([r,g,b]-means)**2,axis=1))
            # finds the closest cluster, returns its index
            min_index = np.argmin(distance)
            
            new_means[min_index, 0] = new_means[min_index, 0] + r;
            new_means[min_index, 1] = new_means[min_index, 1] + g;
            new_means[min_index, 2] = new_means[min_index, 2] + b;
            
            # increments the number of members in the assigned cluster min_index
            num_elements[min_index] = num_elements[min_index] + 1;
            
    # computes the new means for each cluster
    for i in range(0,k):
        if (num_elements[i] > 0):
            new_means[i,] = new_means[i,] / num_elements[i]
              
    # terminates if threshold is reached, add a threshold here to save time!
    # how about this: 
    # threshold = np.sum(np.sqrt(np.sum((new_means - means)**2, axis=1)),axis=0)
    # if threshold < 1e-5:
        # break
        
    # update means with the new values
    means = new_means
    print("iteration = ",itr)

# generates the new image by replacing pixels in each cluster with its mean
img = imageio.imread('/content/micmacs_original.jpg') # load the actual image here
new_img = np.zeros(img.shape)
# loops over all pixels
for i in range(0,img.shape[0]):
    for j in range(0, img.shape[1]):
        r = img[i,j,0]
        g = img[i,j,1]
        b = img[i,j,2]
        # finds the closest cluster
        distance = np.sqrt(np.sum(([r,g,b]-means)**2,axis=1))
        min_index = np.argmin(distance)
        # replaces the pixel with the mean
        new_img[i,j,] = means[min_index,]
iteration =  0
iteration =  1
iteration =  2
iteration =  3
iteration =  4
iteration =  5
iteration =  6
iteration =  7
iteration =  8
iteration =  9
iteration =  10
iteration =  11
iteration =  12
iteration =  13
iteration =  14
iteration =  15
iteration =  16
iteration =  17
iteration =  18
iteration =  19
In [10]:
Image(filename='micmacs_original.jpg') 
Out[10]:
In [12]:
# writes the new image
imageio.imsave('micmacs_resampled.jpg', new_img)
Image(filename='micmacs_resampled.jpg') 
WARNING:root:Lossy conversion from float64 to uint8. Range [4.676014760147601, 175.8144827586207]. Convert image to uint8 prior to saving to suppress this warning.
Out[12]: