Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Optimize circle inside circle detection algorithm lower than O(n²)

I am trying to do a function that takes a list of circles, and returns only a list of circles that are fully overlapped (one inside another). The problem is that the algorithm is at least O(n²), due to the nested for's in getConcentricCircles function, and taking ages for large datasets. Is there any way to optimize it?

EDIT: I don't know if this would help, but I use the algorithm to detect false positives in iris and pupil detection. If a circle is fully inside another circle, it is likely that that is the pupil and the outside is the iris. They should be concentric, what would simplifiy a lot, but it happens that the pupil in the human eye is not exactly in the center of the iris, that is why I do this.

EDIT 2: I have replaced isCircleInCircle with Peter Lawrey's solution, mine was not correct for some cases

Function to check if a circle is inside a circle:

private static boolean isCircleInCircle(Circle a, Circle b) {
    // the circle is inside if the distance between the centre is less than the difference in the radius
    double dx = a.getX() - b.getX();
    double dy = a.getY() - b.getY();
    double radiusDifference = a.getRadius() - b.getRadius();
    double centreDistanceSquared = dx*dx + dy*dy; // edited
    return radiusDifference * radiusDifference > centreDistanceSquared;
}

Then I check every element of the list with each other, and save only the overlapping circles (and the overlapped circle):

public HashSet<Circle> getConcentricCircles(List<Circle> circleList) {
    HashSet<Circle> toReturn = new HashSet<Circle>();

    for (Circle circle : circleList) {
        for (Circle toCheck : circleList) {
            // if the circles are not the same and one is inside another,
            if (!toCheck.equals(circle) && isCircleInCircle(circle, toCheck)) {
                // add both to the hashset
                toReturn.add(circle);
                toReturn.add(toCheck);
            }
        }
    }
    return toReturn;
}
like image 771
Eugenio Cuevas Avatar asked Oct 06 '22 06:10

Eugenio Cuevas


1 Answers

My first impression to see if a circle is inside another would be to know

  1. the centre point of the two circles.
  2. the two radii of the circles.
  3. if C1 to C2 + R2 > R1 then its outside, otherwise its inside.

This should simplify your logic a lot.

Edit: to improve the complexity,

  1. order by radius (large to small)
  2. first for loop go from large to small
  3. second for loop go from large to small
  4. once you find a inner circle within the outer you can remove this circle from the outer loop
  5. reason is as the first outer circle is encapsulating the this inner circle, you don't care if anything else falls in this circle, only if it is outside the larger one subsequently.

This will get your list of circles which is surrounded by a larger circle.

like image 171
Alan J Liu Avatar answered Oct 13 '22 10:10

Alan J Liu