【问题标题】:Coordinates of every point on a circle's circumference圆周上每个点的坐标
【发布时间】:2016-08-26 10:15:07
【问题描述】:

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

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

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

我找到的最佳解决方案是y:= Round(centerY + radius * sin(angle)), x:= Round(centerX + radius * cos(angle)),而因为仅用360度数是不够的,当圆的半径大于大约60px时,角度是这样计算的angle:= angle + (360 / (2 * 3.14 * currentRadius)) -> 我扫描从 0 到 360 的每个值,同时该值以 360/圆的周长的一小部分(以像素为单位)递增。但这种方法不是很精确。圆越大,需要的角度分数越小,精度会受到 Pi 的不准确以及圆角的影响。

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

  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;

结果是这样的:

这还不错,但考虑到圆形区域中大约三分之一的像素是黑色的,您会意识到很多像素已被“跳过”。再加上仔细观察最后一个圆圈的边缘,可以清楚地看到一些点偏离了实际圆周 - 另一个不准确的结果......

我可以使用公式(x - xorig)^2 + (y - yorig)^2 = r^2 来检查中心周围的矩形区域中的每个可能的像素,如果它确实或不落在圆的圆周上,则略大于圆的直径。但是随着圈子的扩大,一直重复它会很慢。

有什么可以做得更好的吗?任何人都可以帮助我改善这一点吗?我根本不坚持我的解决方案中的任何内容,并且会接受任何其他解决方案,只要它提供所需的结果 => 让我读取 all 的值(或绝大多数 - 95具有给定中心和半径的圆的圆周上的 %+) 个像素。越快越好……

【问题讨论】:

  • 一定要画圆吗??你没有!让别人画,你数像素;如果您不相信其他人,那么有几种方法可以做到这一点
  • @gpasch 我不需要画它,不......我这样做只是为了测试方法。
  • 是否需要分别对待每一个圆(r=60,r=61,r=62...)圆周?
  • @MBo 是的,我愿意。我需要判断半径的各种迭代之间像素强度的变化
  • 只是一个愚蠢的想法,但如果我理解正确,你必须检查所有像素的像素强度,所以你为什么不遍历所有像素并检查它们应该是哪个圆圈?保存结果(是的,这差不多,我知道),然后按圆半径排序。不知道这是否可行,只是一个想法。

标签: algorithm delphi opencv math image-processing


【解决方案1】:

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;

【讨论】:

    【解决方案2】:

    如果你想让你的代码更快,不要在内循环中调用三角函数,使用递增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;
    

    【讨论】:

      【解决方案3】:

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

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

      在计算中使用度数也非常少见。我强烈推荐使用弧度。不确定您在这里使用哪些函数,但 Delphi 的 cos 和 sin 似乎需要弧度!

      【讨论】:

      • 我可以使用更多的 Pi 位数,是的,它可能会略微提高精度,但是当计算结果是像素坐标时,如何停止舍入?
      • @j.kaspar 您可以简单地使用子像素坐标。然后你必须在像素之间进行插值。简单示例:位置 (2.5, 1) 则值为 (g(2,1)+g(3,1)) / 2。
      • 明白了。会试试的
      【解决方案4】:

      首先:你想要一个圆周上的所有点。如果你使用任何(好的)算法,还有一些内置的圆函数,你确实得到了所有的点,因为圆周是连接的。 你的图片显示,相邻圆圈之间有洞,比如 r=100 和 r=101。对于圆周绘图功能也是如此。

      现在,如果您希望像素集中的像素覆盖所有具有递增半径的像素,您可以简单地使用以下方法:

      1. 构建一组实心圆像素,例如 r = 101
      2. r = 100构建一组实心圆像素
      3. 从集合 1 中排除集合 2

      实心圆算法通常比连通圆周更有效、更简单,因此不会损失太多性能。

      所以你会得到一个比 1 px 略厚的圆周,但是这组肯定会覆盖表面,半径越来越大,没有任何孔。但也可能发生以这种方式构建的集合与之前的集合 (r-1) 重叠的像素,因此如果您对其进行测试,您会更好地了解它。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-24
        • 1970-01-01
        • 2017-03-09
        • 2022-01-16
        • 2023-01-07
        • 1970-01-01
        相关资源
        最近更新 更多