I'm trying to use numpy to implement k-means in order to perform basic image segmentation based on pixel color. However, when I run my program and have it print the cost function and the locations of the centroids after each iteration, it seems like something is wrong. The cost function is oscillating and centroids don't converge to a local optimum.
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import image
rng = np.random.default_rng()
img = image.imread('fruits_small.jpg') / 256.0
h, w = img.shape[:2]
copy = np.array(img)
plt.subplot(3, 3, 1)
plt.title('Original')
plt.imshow(img)
for plot, k in enumerate(range(5, 13)):
centroids = rng.choice(copy.reshape((-1, 3)), size=k, replace=False)
clusters = np.empty((h, w))
print(centroids)
while True:
for y, x in np.ndindex(img.shape[:2]):
v = copy[y, x]
clusters[y, x] = np.argmin(np.linalg.norm(centroids - v, axis=1))
cost = 0
for i in range(k):
cost += np.linalg.norm(copy[clusters == i] - centroids[i], axis=1).sum()
print(f'cost = {cost}')
d = 0
for i in range(k):
new_centroid = copy[clusters == i].mean(axis=0)
d += np.linalg.norm(centroids[i] - new_centroid)
centroids[i] = new_centroid
if d == 0:
break
print(centroids)
for i in range(k):
img[clusters == i] = centroids[i]
plt.subplot(3, 3, plot + 1)
plt.title(f'k = {k}')
plt.imshow(img)
plt.show()
Have I made a mistake in my implementation somewhere?