Assign
Step 2: Assign¶
This part can be hard to understand if you aren’t as skilled with mathematical notations so we are going to take it very slowly! To start with, we are going to have to go through each pixel and assign a group, so pull the first pixel in our image that we will be working with. I will use a random number I chose for this example.
#Grab the pixel
pixel = X[85000]
print(pixel)
#Plot the pixel color (we need to nest it twice for this to work with just a 1D array)
plt.imshow([[pixel]])
plt.show()
For this pixel, we have to find the distance from each centroid that we currently have. Let's grab the first color that we want to compare with.
#Grab the centroid color
c = colors[0]
print(c)
#Plot the centroid color (we need to nest it twice for this to work with just a 1D array)
plt.imshow([[c]])
plt.show()
Euclidean Distance¶
The euclidean distance between two points p and q is defined as:
$$d(p, q) = \sqrt{\sum_{i=1}^{n}(q_i-p_i)^2}$$
where
$d = \text{Euclidean distance} $
$p = \text{Point 1} $
$q = \text{Point 2} $
$i = \text{Component i of a point} $
$n = \text{Number of components in the points} $
Let's work through piece by piece how to quickly compute the euclidean distance between these two points. First to find the term $q_i-p_i$, we can do the following.
#Finding euclidean distance piece by piece...
print(pixel-c)
Now to move on to $(q_i-p_i)^2$
#Finding euclidean distance piece by piece...
print((pixel-c) ** 2)
We need to sum the values which can easily be with the sum function, which will get us to the point of $\sum_{i=1}^{n}(q_i-p_i)^2$
#Finding euclidean distance piece by piece...
print(((pixel-c) ** 2).sum())
And finally, we need to take the square root of that to get to our final answer for the distance.
#Finding euclidean distance piece by piece...
print(((pixel-c) ** 2).sum() ** .5)
Now the distance needs to be computed for all colors to find the lowest distance of the bunch. Let's loop thorugh each and see what the distances are below.
#Plot the pixel color (we need to nest it twice for this to work with just a 1D array)
print("Pixel to assign:")
print(pixel)
plt.imshow([[pixel]])
plt.show()
print("--------")
print()
for c in colors:
#Plot the centroid
print("Centroid:")
print(c)
plt.imshow([[c]])
plt.show()
#Find the distance
distance = ((pixel-c) ** 2).sum() ** .5
print("Distance: {}".format(distance))
print("--------")
print()
As we can tell, that blue color is the closest in terms of color. Now to generalize this so that it can run fast, I want to build out a function which will assign the centroid. Once again we will work through first how to compute the distances without needing a for loop (numpy is much faster than using a for loop).
#Step by step finding the distance
print("Step 1 - Find difference")
distance = pixel - colors
print(distance)
print()
print("Step 2 - Square Values")
distance = distance ** 2
print(distance)
print()
print("Step 3 - Get the sum by summing across axis 1")
distance = distance.sum(axis=1)
print(distance)
print()
print("Step 4 - Take the square root")
distance = distance ** .5
print(distance)
print()
Combining all these steps we have the following.
#Find the distance to each centroid
distance = ((pixel - colors) ** 2).sum(axis=1) ** .5
print(distance)
The argmin function will return the index of the smallest distance in the array.
#Find the smallest distance
i = distance.argmin()
print(i)
Now we are going to make a minor change up. Instead of going pixel by pixel, we are going to go color by color. The following code is an example of how we can get the distance for every point from the first centroid.
#Find each points distance from the first centroid
c = colors[0]
print(((X - c) ** 2).sum(axis=1) ** .5)
Using list comprehension, create a list of 4 arrays where each array holds the distance for each point from the corresponding centroid.
#Get the distance
distance = [((X - c) ** 2).sum(axis=1) ** .5 for c in colors]
print(distance)
Using the vstack function from numpy will stack it so that each columns holds the distance from each centroid for each point.
#Stack the distances
distance = np.vstack(distance)
print(distance)
Finally, use argmin along the columns to grab the labels!
#Find the labels
labels = distance.argmin(axis=0)
print(labels)
Like we did before, you can index with the labels to find the image created from this before the next step.
#Show the image
Y = colors[labels]
Y = Y.reshape(img_shape)
plt.imshow(Y)
plt.show()
If you wanted to see the number of pixels with each label, you can call np.unique with the labels and return_count=True. It will give you back unique labels and the count for each.
#Find the unique label counts
print(np.unique(labels, return_counts=True))