圆周周围每个点的坐标

时间:2016-05-01 16:30:30

标签: algorithm delphi opencv math image-processing

首先,请注意,此问题与以下问题不重复:1st2nd3rd

我正在使用delphi和openCV,但我正在寻找一种算法,一种解决方案,无论语言如何。

为了进行精确的图像分析,我需要检查圆形区域中像素强度的变化。所以我在连续增长的圆周上读取像素值。为了能够做到这一点,我当然需要知道像素的坐标。

我找到的最佳解决方案是y:= Round(centerY + radius * sin(angle)), x:= Round(centerX + radius * cos(angle)),而因为仅用360度计算是不够的,当圆的半径大于大约60px时,角度计算如此{{1} } - >我扫描从0到360的每个值,而值的增量是圆的360 /圆周的一小部分(以像素为单位)。但这种方法不是很精确。圆圈越大,角度的分数越小,精度就会受到Pi的不精确性和圆角的影响。

如果我使用上述方法,并尝试使用此代码绘制计算的像素:

angle:= angle + (360 / (2 * 3.14 * currentRadius))

结果如下: circles

这还不错,但是考虑到,圆形区域中所有像素的三分之一都是黑色的,你会发现,很多像​​素已被"跳过"。另外,仔细观察最后一个圆圈的边缘,可以清楚地看到,有些圆点偏离实际圆周 - 这是另一个不准确的结果......

我可以使用公式 centerX:= 1700; centerY:= 1200; maxRadius:= 500; for currentRadius:= 80 to maxRadius do begin angle:= 0; while angle < 360 do begin xI:= Round(centerX + currentRadius * cos(angle)); yI:= Round(centerY + currentRadius * sin(angle)); angle:= angle + (360 / (2 * 3.14 * currentRadius)); //this is openCV function, to test the code, you can use anything, that will draw a dot... cvLine(image,cvPoint(xI,yI),cvPoint(xI,yI),CV_RGB(0, 255, 0)); end; end; 来检查中心周围的矩形区域中的每个可能像素,比圆的直径略大,如果是的话,或者不会落在圆周的圆周。但随着圈子的增长,重复它会非常缓慢。

有什么东西可以做得更好吗?谁能帮助我改善这个?我根本不会从我的解决方案中坚持任何事情,并且会接受任何其他解决方案,只要它能给出所需的结果=&gt;让我读一个具有给定中心和半径的圆周上的全部(或绝大多数 - 95%+)像素的值。越快越好......

4 个答案:

答案 0 :(得分:4)

1)建立半径最小的像素列表。这足够了 保持圆的第一个八分圆(坐标系的第一象限中的范围0..Pi / 4),并获得具有反射的对称点。 例如,您可以使用Bresenham圆算法或仅使用圆等式。

2)对于下一次迭代,遍历列表中的所有坐标(如果有两个具有相同Y值的点,则使用右边的一个坐标)并检查右邻居(或两个邻居!)是否位于下一个半径内。对于最后一点,还要检查顶部,右上角邻居(在Pi / 4对角线处)。 将好邻居(一个或两个)插入下一个坐标列表。

 Example for Y=5.
 R=8   X=5,6 //note that (5,5) point is not inside r=7 circle
 R=9   X=7
 R=10  X=8
 R=11  X=9
 R=12  X=10
 R=13  X=11,12 //!
 R=14  X=13

使用这种方法,您将使用最大半径圆中的所有像素而没有间隙,并且检查列表生成过程相当快。

修改 代码实现了另一种方法,它使用较低的行像素限制来构建上行。

它会在给定范围内生成圆圈,将它们绘制成迷幻色彩。所有数学都是整数,没有浮点数,没有三角函数! Pixels仅用于演示目的。

procedure TForm1.Button16Click(Sender: TObject);

  procedure FillCircles(CX, CY, RMin, RMax: Integer);

    //control painting, slow due to Pixels using
    procedure PaintPixels(XX, YY, rad: Integer);
    var
      Color: TColor;
      r, g, B: Byte;
    begin
      g := (rad mod 16) * 16;
      r := (rad mod 7) * 42;
      B := (rad mod 11) * 25;
      Color := RGB(r, g, B);
     // Memo1.Lines.Add(Format('%d  %d  %d', [rad, XX, YY]));
      Canvas.Pixels[CX + XX, CY + YY] := Color;
      Canvas.Pixels[CX - YY, CY + XX] := Color;
      Canvas.Pixels[CX - XX, CY - YY] := Color;
      Canvas.Pixels[CX + YY, CY - XX] := Color;
      if XX <> YY then begin
        Canvas.Pixels[CX + YY, CY + XX] := Color;
        Canvas.Pixels[CX - XX, CY + YY] := Color;
        Canvas.Pixels[CX - YY, CY - XX] := Color;
        Canvas.Pixels[CX + XX, CY - YY] := Color;
      end;
    end;

  var
    Pts: array of array [0 .. 1] of Integer;
    iR, iY, SqD, SqrLast, SqrCurr, MX, LX, cnt: Integer;
  begin
    SetLength(Pts, RMax);

    for iR := RMin to RMax do begin
      SqrLast := Sqr(iR - 1) + 1;
      SqrCurr := Sqr(iR);
      LX := iR; // the most left X to check

      for iY := 0 to RMax do begin
        cnt := 0;
        Pts[iY, 1] := 0; // no second point at this Y-line
        for MX := LX to LX + 1 do begin
          SqD := MX * MX + iY * iY;
          if InRange(SqD, SqrLast, SqrCurr) then begin
            Pts[iY, cnt] := MX;
            Inc(cnt);
          end;
        end;

        PaintPixels(Pts[iY, 0], iY, iR);
        if cnt = 2 then
          PaintPixels(Pts[iY, 1], iY, iR);

        LX := Pts[iY, 0] - 1; // update left limit
        if LX < iY then // angle Pi/4 is reached
          Break;
      end;
    end;
    // here Pts contains all point coordinates for current iR radius
    //if list is not needed, remove Pts, just use PaintPixels-like output
  end;

begin
  FillCircles(100, 100, 10, 100);
  //enlarge your first quadrant to check for missed points
  StretchBlt(Canvas.Handle, 0, 200, 800, 800, Canvas.Handle, 100, 100, 100,
    100, SRCCOPY);
end;

答案 1 :(得分:1)

如果你想让你的代码更快,不要在内部循环中调用三角函数,使用

递增sin(angle)cos(angle)
sin(n*step)=sin((n-1)*step)*cos(step)+sin(step)*cos((n-1)*step)
cos(n*step)=cos((n-1)*step)*cos(step)-sin(step)*sin((n-1)*step)

  ...
  for currentRadius:= 80 to maxRadius do
  begin

    sinangle:= 0;
    cosangle:= 1;
    step:= 1 / currentRadius; // ?
    sinstep:= sin(step);
    cosstep:= cos(step);
    while {? } do
    begin

      xI:= Round(centerX + currentRadius * cosangle);
      yI:= Round(centerY + currentRadius * sinangle);

      newsin:= sinangle*cosstep + sinstep*cosangle;
      newcos:= cosangle*cosstep - sinstep*sinangle;

      sinangle:= newsin;
      cosangle:= newcos;

      ...    
    end;

  end;

答案 2 :(得分:1)

首先:你想要圆周上的所有点。如果你使用任何(好)算法,也有一些内置的圆函数,你会得到所有的点,因为圆周是连通的。 你的照片显示的是,邻居圈之间有洞,比如r = 100和r = 101。圆周绘制功能也是如此
现在,如果您希望像素集中的像素覆盖所有具有递增半径的像素,则可以使用以下方法:

  1. 构建一组实心圆圈像素,例如 r = 101
  2. 使用 r = 100
  3. 构建一组实心圆形像素
  4. 1
  5. 集中排除 2

    填充圆算法通常比连接圆周更有效,更简单,因此您不会失去太多性能
    所以你得到的圆周略厚于1像素,但是这个套装肯定会覆盖表面,半径越来越大,没有任何孔。但是也可能发生以这种方式构建的集合与先前集合(r-1)重叠的像素,因此如果你测试它,你会更好地了解它。

    PS :还不清楚代码中是否出现任何三角函数。我不知道任何有效的圆算法使用除平方根以外的任何东西。

答案 3 :(得分:0)

为什么不简单地为Pi使用更多数字并停止舍入以提高准确度?

此外,如果您能负担插值,我建议您使用子像素坐标来获得更准确的强度值。

在计算中使用度数也很常见。我强烈建议使用弧度。不确定你在这里使用哪些功能,但是德尔福的cos和sin似乎期望弧度!