Search code examples
opencv

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


Solution

  • 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.