3

I am trying to take binary masks say with two different people and everything else is black. Now I want to group each person into their own clusters using K-means so that I can eventually draw bounding boxes around them. Here is the code I have so far:

def kmeans(img):
  k_values = range(1, 5)
  pixels = np.float32(img.reshape(-1,1))
  criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
  flags = cv2.KMEANS_PP_CENTERS
  min_ssd = 0
  for k in k_values:
    ssd,labels,centers = cv2.kmeans(pixels,k,None,criteria,10,flags)
    if k == 1 or ssd < min_ssd: #looking for elbow in graph
      min_ssd = ssd
      min_labels = labels 
      min_centers = centers

  return min_labels,min_centers

As it stands, this code is returning the centers as pixel values and not coordinates so I am losing the spatial information in the frame. I am not quite sure how to change this code in order to obtain the cluster center as a point in the image and be able to visualize the clusters.

Example photo

Edit: I have changed the approach, first I start with OpenCV's simpleblobdetector() to detect the centroids of each blob in the image. From here I am initializing the centroids with those values. I plan to update the code when I finish making the changes

Centroid detection

Final Code

def kmeans_helper(img, centroids):
  data = np.nonzero(img)
  data = tuple(zip(data[0],data[1]))
  data = np.asarray(data).reshape(-1,2)

  num_clusters = len(centroids)
  kmeans = KMeans(n_clusters = num_clusters, init = centroids, n_init = 1)
  kmeans.fit(data)
  centroids_array = np.uint8(kmeans.cluster_centers_)

  num_labels = np.unique(kmeans.labels_)
  clusters = [data[kmeans.labels_ == label] for label in num_labels]

  return clusters

I figured I would update my final code for anyone who might need this in the future.

5
  • 1
    Welcome to SO! Please post the image as well so that we can work on your problem. Commented Nov 10, 2020 at 9:59
  • If you want centroids as coordinates, you should give kmeans coordinates. That is, do not pass the binary mask directly. For every pixel on the mask, you first have to generate an array of its coordinates. Commented Nov 10, 2020 at 19:56
  • @eldesgraciado So I would create an array of triples containing (x,y,pixel_val)? Commented Nov 10, 2020 at 20:00
  • I don’t know if you need to also include the pixel's intensity value - unless you are planing to do something with that info too. Just an array of (x,y) points should do the trick, kmeans will return each cluster's centroid also as an (x,y) array, and that should give you the centroid of each blob. Side note: if you are looking just for each blob centroid, you could also carry out component analysis and calculate the bounding box of each blob, from there it should be fairly easy to compute the centroids. You can also use central moments to compute blob centroids. Commented Nov 10, 2020 at 20:11
  • Connected components seems like a much better solution to do what you want to do. Or get bounding boxes for the contours from each binary blob. Commented Nov 12, 2020 at 2:26

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.