首先,请注意,这个问题不是这些的重复: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到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;

结果是这样的:
algorithm - 圆圆周上每个点的坐标-LMLPHP

不错,但考虑到圆形区域中所有像素的三分之一几乎都是黑色,这是可以理解的,很多像素已被“跳过”。再加上仔细观察最后一个圆的边缘,可以清楚地看到一些点偏离实际圆周,这是不准确的另一个结果...

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

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

最佳答案

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;

关于algorithm - 圆圆周上每个点的坐标,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/36969039/

10-12 05:33