用于圆检测的最小二乘法

时间:2015-04-24 14:30:20

标签: python algorithm feature-detection least-squares

我正在尝试使用数据点子集上的最小二乘优化从激光扫描执行圆检测。由于测量仅针对圆的一部分获得,因此最小二乘法返回错误结果,报告更接近激光的圆而不是实际。

算法的结果如图所示。散点指示激光测量,圆圈以算法返回的点为中心。灰色半透明形状表示机器人进行扫描(此形状左侧和右侧的激光)。

我只对圆的局部坐标感兴趣,该坐标已知半径RR。

PS。我假设扫描被分成簇(self.clusters [i]是一个簇),这是[x,y]激光点的列表

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

这是图片:

enter image description here

3 个答案:

答案 0 :(得分:2)

1,1离正确的值太远了;你最有可能陷入局部最佳状态。

尝试从更接近真实中心的点开始。您可以通过首先在您的群集中插入一条直线来找到它;然后将这些点分成两半,根据它们投射的线的一半;接下来是两个行,每个行都有一个新的两个子集合;并在中间点找到两个垂线的交点。

这是基于你的星团的弧度不超过180度的跨度,它们看起来就像它们一样。如果没有,只需重复细分,得到四个和弦而不是两个。

答案 1 :(得分:1)

您可以使用Circular Hough Transform查找圆圈 - 如果您事先知道圆的半径,这将非常简单。

skimage上从这个过程的文档中大量借用,我已经将以下代码放在一起运行,但可能需要进行一些调整才能找到圆圈:

Engine

答案 2 :(得分:1)

以下是从this纸张实施的小圈检测的小片段 该算法始终有效,并且不具有线性复杂度的迭代。



 
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>
&#13;
&#13;
&#13;