Question

How to get centers of circles arranged in a grid in specific order in opencv

I have some approximate circles in an approximate grid shape, but there is distortion.

How can I get the centers in the order of left to right, top to bottom?

I tried findCirclesGrid, but I don't think it works with distortion.

I don't think blob detection has an order.

enter image description here

 2  60  2
1 Jan 1970

Solution

 2

findCirclesGrid() is intended for camera calibration. Such patterns are expected to have distortion, just like yours. It's definitely supposed to work on your data.

It just needs a little help. It uses a BlobDetector internally, which is initialized with some defaults that work well for the usual circles grids that show well-defined circles.

For your picture, you need a BlobDetector with custom parameters. To find all those lumpy blobs, you need to disable all the filtering it usually does.

params = cv.SimpleBlobDetector_Params()
params.filterByArea = False
params.filterByCircularity = False
params.filterByColor = False
params.filterByConvexity = False
params.filterByInertia = False

blobdet = cv.SimpleBlobDetector_create(params)

Result of keypoints = blobdet.detect(im) and cv.drawKeypoints():

blobdetector result

You also need to be careful with the flags to findCirclesGrid(). CALIB_CB_CLUSTERING failed in that it misassigned points to grid positions.

(rv, gridpts) = cv.findCirclesGrid(
    im, (10, 7),
    flags=cv.CALIB_CB_SYMMETRIC_GRID,
    blobDetector=blobdet)

And that'll give you gridpts having shape (70, 1, 2).

Some visualization:

im_with_grid = cv.cvtColor(cv.pyrUp(im >> 1), cv.COLOR_GRAY2BGR)

for k in range(1, len(gridpts)):
    [p1] = gridpts[k-1]
    [p2] = gridpts[k]
    cv.line(im_with_grid, (p1 * 2.0).astype(int), (p2 * 2.0).astype(int), (0, 0, 255), 2)

for i,[pt] in enumerate(gridpts):
    cv.circle(im_with_grid, (pt * 2.0).astype(int), 5, (0, 255, 0), -1)
    cv.putText(
        im_with_grid, f"{i}", (pt * 2.0).astype(int),
        cv.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2, cv.LINE_AA )

result 1

Or if you specify the grid size as (7, 10), it'll be flipped along its diagonal and look like this:

result 2

Caveat

findCirclesGrid will fail if the grid has any defects, i.e. missing points. It can't go around those defects. You need a perfect detection of all grid points.

2024-07-22
Christoph Rackwitz