1

looking at trying to find the center point of "clusters" / "circles" / "ellipses" ect in an image.

Example image:

picture with 3 clusters

It is clear by eye that there are 3ish clusters. I am looking to find the center point of the clusters, and the defining - rectangle, or circle (with radius) or ellipse, however to describe it.

Ideally, it is expanded to the following cases:

  1. Unknown number of clusters (assume min 1, max 10) for simplicity if needed.

This picture is part of a larger picture that made use of canny-edge detection, thresholding and then contours to find me this area of interest. Unfortunately, I need it to be...more refined (or better parameters but I couldn't find anything that worked)

Things I have tried:

I have tried using kmeans segmentation, but they are mainly for color segments. And this image could be black/white for all the information the colors give us. HughCircles in opencv are not really the circles I want in that they give me way too many circles that matches "edges" Template matching via opencv also didnt work since it was too constrained and these cant be exact matches.

Any suggestions/avenues to examine would be welcome!
I have also attempted some basic scatterplot k-means clustering (as if this is data) but have not had good results so far.

Language of choice: python, but adaptable.

4
  • 1
    I would probably try a few dilations followed by erosions on a binary copy of the image to get those clusters' holes filled in & sperated from each other. Then I'd run a blob detector Commented Dec 31, 2021 at 13:26
  • Note that you would need to invert before dilation/erosion since OpenCV expects a black background and white object/foreground. Commented Dec 31, 2021 at 13:37
  • You will need to define some domain knowledge. You could easily say that this whole thing is only a single cluster or a lot of very small clusters. Just a matter of perspective. I would start with a thresholding and a distance transform and then try find the average distance of elements in a cluster (here domain knowledge could help to simplify or skip that step). Then find points which a further away to the next masked pixel and find "big" (again domain knowledge) such regions and count them as being likely separators between clusters. But I didnt try that, so probably work well. Commented Dec 31, 2021 at 14:14
  • you said you used Canny. that's a red flag. present the data before Canny. Commented Dec 31, 2021 at 14:58

1 Answer 1

1

I had an attempt at this and it may give you some ideas on how to proceed - even if only by seeing where it fails to work. I wanted to post it before the question gets a third close vote.

#!/usr/bin/env python3

import cv2
import numpy as np

# Load image and make greyscale version too
im = cv2.imread('dl9Vx.png')
grey = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)

# Threshold (in case we do morphology) and invert
_, thresh = cv2.threshold(grey, 254, 255, cv2.THRESH_BINARY_INV)
cv2.imwrite('DEBUG-thresh.png', thresh)

# Prepare to do some K-means
# https://docs.opencv.org/4.x/d1/d5c/tutorial_py_kmeans_opencv.html
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Find x,y coordinates of all non-white pixels in original image
Y, X = np.where(thresh==255)
Z = np.column_stack((X,Y)).astype(np.float32)

nClusters = 3
ret,label,center=cv2.kmeans(Z,nClusters,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)

# Mark and display cluster centres 
for x,y in center:
    print(f'Cluster centre: [{int(x)},{int(y)}]')
    cv2.drawMarker(im, (int(x), int(y)), [0,0,255])

cv2.imwrite('result.png', im)

Output

Cluster centre: [103,65]
Cluster centre: [50,93]
Cluster centre: [60,29]

enter image description here


Notes:

Note 1: I asked it to search for 3 clusters as that was the number you suggested. There are ways of automagically determining the number of clusters - search for "Elbow Method" for example.

Note 2: If you wanted to do a morphological closing of the gaps, to make the clusters more solid, you could add in this code after cv2.threshold():

kernel = np.ones((3,3),np.uint8)
thresh = cv2.dilate(thresh, kernel, iterations=1)

and it will make the thresholded image look like this:

enter image description here

Note 3:

You could remove the thresholding call and change the np.where() line to:

Y, X = np.where(im!=255)
Sign up to request clarification or add additional context in comments.

Comments

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.