简体   繁体   中英

Least squares for circle detection

I'm trying to perform a circle detection from a laser scan using least squares optimization over a subset of data points. Since the measurements are only obtained for a part of a circle, the least squares method returns faulty result, reporting a circle much closer to the laser than it actually is.

The outcome of the algorithm is shown in the picture. Scatter points indicate the laser measurements, circles are centered on the points returned by the algorithm. Gray semi-transparent shape indicates the robot taking a scan (lasers on the left and on the right of this shape).

I'm interested only in the local coordinates of the circle, which has known radius RR.

PS. I assume that the scan is separated into clusters (self.clusters[i] is one cluster), which are the lists of [x,y] laser points

def circle(x, scan):
    xc, yc = x
    f = sqrt((scan[:,0] - xc)**2 + (scan[:,1] - yc)**2) - RR
    return f


def optimize_detect_circles(self):

    centre = [1,1]

    for i in range(0, self.number_of_clusters):
        range_points = np.array(self.clusters[i])

        sol = optimize.root(circle, centre, args=(range_points), method='lm')
        self.circle_candidates.append(sol.x)
        print sol.x

Here's the picture:

在此处输入图片说明

1,1 is too far from the correct value; you most probably fall into some local optimum.

Try starting from a point much closer to the real center. You find it by first fitting a straight line to your cluster; then separate the points into two halves, according to which half of the line they project onto; fit two lines next, each to one of your new two subclusters; and find an intersection point for the two perpendiculars at their middle points.

This is predicated on your clusters being arcs not bigger than 180 degrees in span, which they seem like they are. If not, just repeat the subdivision, to get four chords instead of two.

You can find circles using a Circular Hough Transform - this is especially easy if you know the radius of the circle in advance.

Borrowing liberally from the documentation for this process at skimage , I have put together the following code which runs, but might need a bit of tweaking to locate the circles:

import numpy as np
import matplotlib.pyplot as plt

import skimage
from skimage import data, filter, io
from skimage.transform import hough_circle
from skimage.feature import peak_local_max
from skimage import data, color
from skimage.draw import circle_perimeter

theImage = np.sum(io.imread("w1s31.png"),2)/4 # Image to greyscale

hough_radii = np.arange(61, 69, 2) # These are the radii to search for
hough_res = hough_circle(theImage, hough_radii)
centers = []
accums = []
radii = []

for radius, h in zip(hough_radii, hough_res):
    # For each radius, extract two circles
    num_peaks = 2
    peaks = peak_local_max(h, num_peaks=num_peaks)
    centers.extend(peaks)
    accums.extend(h[peaks[:, 0], peaks[:, 1]])
    radii.extend([radius] * num_peaks)

# Draw the most prominent 5 circles
image = color.gray2rgb(theImage)
for idx in np.argsort(accums)[::-1][:5]:
    center_x, center_y = centers[idx]
    radius = radii[idx]
    cx, cy = circle_perimeter(center_y, center_x, radius)
    theImage[cy, cx] = (1)

plt.imshow(theImage, cmap=plt.cm.gray)
plt.show()

Here is a small snippet of the circle detection implemented from this paper.
The algorithm always works and is not iterative with a linear complexity.

 var add = function(a,b){ return a+b; }; var add_uu = function(a,b){ return a+b[0]*b[0]; }; var add_uv = function(a,b){ return a+b[0]*b[1]; }; var add_vv = function(a,b){ return a+b[1]*b[1]; }; var add_uuu = function(a,b){ return a+b[0]*b[0]*b[0]; }; var add_vvv = function(a,b){ return a+b[1]*b[1]*b[1]; }; var add_uvv = function(a,b){ return a+b[0]*b[1]*b[1]; }; var add_uuv = function(a,b){ return a+b[0]*b[0]*b[1]; }; var getx = function(e){ return e[0]; }; var gety = function(e){ return e[1]; }; $(document).ready(function () { var paper = Raphael("canvas"); var points=[]; $("#canvas").mousedown(function (e) { var x = e.offsetX; var y = e.offsetY; points.push([x,y]); paper.circle(x, y, 1); }); $("#clear").click(function(){ paper.clear(); points = []; }); $("#go").click(function(){ var N = points.length; var xb = points.map(getx).reduce(add,0) / N; var yb = points.map(gety).reduce(add,0) / N; var u = points.map(function(e){return [e[0]-xb,e[1]-yb];}); var a1 = u.reduce(add_uu,0); var b1 = u.reduce(add_uv,0); var c1 = 0.5*(u.reduce(add_uuu,0) + u.reduce(add_uvv,0)); var a2 = u.reduce(add_uv,0); var b2 = u.reduce(add_vv,0); var c2 = 0.5*(u.reduce(add_vvv,0) + u.reduce(add_uuv,0)); var q = a2/a1; var vc = (c2-q*c1)/(b2-q*b1); var uc = (c1-b1*vc)/a1; var r = Math.sqrt(uc*uc+vc*vc+(a1+b2)/N); var x = uc+xb; var y = vc+yb; paper.circle(x, y, r).attr({"stroke":"#f00","stroke-width":2}); var e = points.reduce(function(p,c) { var t = r*r - (c[0]-x)*(c[0]-x) - (c[1]-y)*(c[1]-y); return p+t*t; },0); console.log("Residue = " + e); }); }); 
 #canvas { width: 600px; height: 400px; border: 2px dotted #ccc; cursor: crosshair; } 
 <script src="http://cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <button id="clear">Clear</button> <button id="go">Compute circle</button> <div id="canvas"></div> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM