7

If I have e.g. 20 points, how can i check to see if those points make up for a circle? It doesnt have to be a perfect circle.

For example if I store the coordinates of my mouse every 200ms (as the user moves the mouse), I want to see if the user makes a circle gesture. And I cant expect the user to make a perfect circle.

AakashM
  • 62,551
  • 17
  • 151
  • 186
Afra
  • 2,502
  • 3
  • 24
  • 27
  • Could you please be more specific in what you are trying to accomplish? – Alexandros Jan 30 '12 at 12:28
  • 2
    I will continue on Don Roby's statement - "What is an imperfect circle". Suppose that you sample a perfect circle in in 4 points. Suppose now that this points are : starting in 45 deg. and rotating around the circle by 90 deg. This will give you 4 points which are located exactly where they should be for a perfect circle. But if you draw these 4 points you will end up drawing a rectangle instead. Is this an imperfect or perfect circle? – brano Jan 30 '12 at 13:12
  • @Brano, I take an imperfect circle as a set of points that are co-circular to within a stated tolerance. e.g. if you create a best fit circle through the points, and 85% percent of the points lie within a distance of 20% of the radius of the fit circle, you have an 'imperfect circle' of types. Note you need four or more points to start with, and you need to tune your acceptance criteria for different applications and perhaps users. – SmacL Jan 30 '12 at 13:44
  • 1
    Don't know why this question was closed. Possibly changing the term "make up a circle" to "are co-circular" would remove the ambiguity. – SmacL Mar 26 '12 at 15:03

3 Answers3

9

I'd do the following;

  • Compute a best fit circle through the points
  • Calculate a residual for each point (the join distance from the centre to the point minus the best fit circle radius)
  • Accept the result if a large enough percentage of the residuals were below a certain value defined as a small percentage of the best fit radius. These parameters would be user definable acceptance criteria.
SmacL
  • 22,555
  • 12
  • 95
  • 149
  • This is probably the most correct method, but it seems like it'd be an undertaking to implement it in code. It would likely still run fast enough for real-time gesture recognition depending on the number of points. – Louis Ricci Jan 30 '12 at 17:26
  • 1
    It would probably be best to use L_1 fitting, as this is less sensitive to single outliers. Another possibility is L_∞ (minimum annulus) fit, where the test could be based on the ratio of annulus width to mean radius. There's a nice survey of [circle fitting algorithms here](http://valis.cs.uiuc.edu/~sariel/papers/05/l1_fitting/l1_fit_slides.pdf), with links to code and other resources. A web search for _L1 circle fitting_ turns up lots of resources for both L_1 and L_∞ algorithms. – Ted Hopp Jan 30 '12 at 20:36
  • @Ted, many thanks for the link, very useful. I've had the single outlier issue in the past. – SmacL Jan 31 '12 at 07:32
  • @Lastcoder, I use something very similar to this for a piece of modelling software I develop, and it is fast enough for real time updating of the circle as the mouse moves on a C++ desktop solution. Performance might become an issue on a lower powered platform (e.g. mobile) on a slower language. Note there are many easy optimizations such as when comparing distances <, ==, > you can drop the square root. – SmacL Jan 31 '12 at 07:52
3

Update: with the suggestion from @LastCoder to drop consecutive points too close to the previous one (I set the threshold at the distance of 10; perhaps it can be increased) and the tolerance level set to 0.25 (i.e. discrepancy of 25% from the average distance to the centre point is acceptable), the app I made recognizes my "circles" in more than half cases, and is not deceived by squares anymore. So might be not a bad idea, after all.


I would find the centroid for the given set of points, and check if the distance from the centroid to each point is more or less the same (assuming that you expect an approximation of full circle, not just an arc).

It works for me in practice for the problem of detecting a circle gesture done with mouse; see an example in C# (VS2010, the main form only, the rest of app is automatic boilerplate; ignore the errors at ideone) and a screenshot for it here:

Certainly I am bad at drawing circles with laptop's touch-stick

Alexey Kukanov
  • 12,479
  • 2
  • 36
  • 55
  • +1. Just remember that the radial deviation must be proportional to the radius, or you end up detecting a non-movement as a circle. This algorithm can be efficiently implemented as an online algorithm, where the circularity score is recalculated as new samples arrive. – cyborg Jan 30 '12 at 14:25
  • 3
    This will break if the points are not evenly dispensed around the whole circle, pulling the centroid away from the actual center of the circle. – Rafał Dowgird Jan 30 '12 at 14:29
  • 1
    @RafałDowgird & others: I agree, but for the problem of mouse gesture detection one can expect points to be dispensed pretty evenly; and it is confirmed with the experiment (see updated answer) :) – Alexey Kukanov Jan 30 '12 at 16:32
  • Performance wise, this seems to be the most efficient estimation. To make the centroid more accurate you could cull out consecutive points if they're distance is too close to each other (for gesture sensing this could be calibrated fairly easily and remove the extra weight caused by moving slowly in one part of the circle but not the other). – Louis Ricci Jan 30 '12 at 18:43
  • @LastCoder: thanks for the suggestion, a good idea to try. The problem happened to be interesting and fun, yeah? :) – Alexey Kukanov Jan 30 '12 at 18:49
  • +1 for implementing, testing, coming back with your results, and demonstrating it all works. Well done! – SmacL Jan 31 '12 at 07:58
2

Here's a simple method, with a working implementation I threw together.

http://jsfiddle.net/kBsdW/29/

  • Loop through the points
  • Find a second point with the maximum distance from the first
  • Record the distance
  • Once you have all of the max distances average them and calculate the error tolerance
  • Check all your recorded distances against your error tolerance

This works great for user input like from a mouse or touch sensor. This algorithm is O(n^2) and uses the delta max distance as opposed to finding the center of mass and checking radii distances.

It "seems" to be more efficient than the best-fit-circle method which has to calculate on every combination of 3 points.

This hack~algo takes advantage of the fact that the maximum distance between two points on a circle is the diameter of the circle.

function isCircle(points, error) {
    if(points.length <= 2) return true;
    var weights = [];
    var maxDistance = 0;
    var sumDistance = 0;
    var avgDistance = 0;
    var errorConstraint = 0;
    for(var i=0; i<points.length; i++) {
        var distance = 0;
        for(var j=0; j<points.length; j++) {
            var d = getDistance(points[i], points[j]);
            if(d > distance) {
                distance = d;
            }
        }
        if(distance > 0) {
            if(distance > maxDistance) maxDistance = distance;
            sumDistance += distance;
            weights.push(distance);
        }
    }
    avgDistance = sumDistance / weights.length;
    errorConstraint = error * avgDistance;
    for(var i=0; i<weights.length; i++) {
        if(Math.abs(avgDistance - weights[i]) > errorConstraint) {
            return false;
        }
    }
    return true;
}
Louis Ricci
  • 20,804
  • 5
  • 48
  • 62
  • +1 . but `Math.abs(avgDistance - weights[i]) > errorConstraint` is too simplistic. Just think of someone who start at the middle of the circle. You need a majority of points verifying this. So 2 parameters. – UmNyobe Jan 30 '12 at 17:41
  • @UmNyobe - That's why it returns false in those cases and only returns true if "all" of the lengths are within the tolerance. You could also do some extra processing to the weights array and remove outliers but I didn't do that in the simple example jsfiddle. – Louis Ricci Jan 30 '12 at 18:35
  • I played a little with your implementation. Even with tolerance level set to 0.15, it can consider a square and even a triangle (if close to equilateral) as a circle :) – Alexey Kukanov Jan 30 '12 at 18:41
  • @Alexey. False negatives will always exist. This is pattern recognition. Unless he wants to start recognizing squares too, this is already solid. – UmNyobe Jan 30 '12 at 21:38
  • @Alexy, four points on the corners of a square or any triangle are co-circular. Would it fail for a larger number of points on the same geometry? I don't think so, as you have more varied distances involved. – SmacL Jan 31 '12 at 07:39