【问题标题】:Pixels inside a circumference圆周内的像素
【发布时间】:2015-03-04 22:30:42
【问题描述】:

我有一张位图,我需要在上面画一个圆圈。现在我只画了圆周的像素。如何在不使用扩展的距离函数的情况下获得其他像素? 这是我的代码

public void FindMostIntenityPixelInCircle(int x0, int y0, int radius, List<Point> intensities)
{
  Bitmap bitmap = ((Bitmap)(_smartLabForm.pictureBoxGreenImage.Image));
  int x = radius;
  int y = 0;
  int radiusError = 1 - x;
  while (x >= y)
  {
    intensities.Add(new Point(x + x0, y + y0));
    intensities.Add(new Point(y + x0, x + y0));
    intensities.Add(new Point(-x + x0, y + y0));
    intensities.Add(new Point(-y + x0, x + y0));
    intensities.Add(new Point(-x + x0, -y + y0));
    intensities.Add(new Point(-y + x0, -x + y0));
    intensities.Add(new Point(x + x0, -y + y0));
    intensities.Add(new Point(y + x0, -x + y0));
    if (radiusError < 0)
    {
      radiusError += 2 * y + 1;
    }
    else
    {
      x--;
      radiusError += 2 * (y - x) + 1;
    }
  }
}

【问题讨论】:

  • 函数名称中的 "Most" 表明您应该比较值(可能在色彩空间转换之后),而不是将它们全部添加到列表中。
  • 是的,我有一个 MarchingSquare 路径,并且对于路径的每个点,我需要绘制一个以该点为中心和给定射线的圆。在那个圆圈内,我应该找到绿色强度最大的像素
  • 是否有理由不在图形对象中使用draw和fill方法? See here 在您的情况下,您可能使用从位图创建的图形!还有here 用于绘制控件和位图的区别..
  • ..如果您只想找到里面的像素,我建议使用 FloodFill 函数。 Here 是非递归的。请注意,它必须在此过程中更改像素,因此要仅获取像素坐标而不更改它们,您可以使用虚拟 Btimap 来使用洪水填充..
  • ..但如果它始终是一个圆圈,您可以简单地逐行扫描像素通过您的列表..

标签: c# visual-studio-2012 bitmap


【解决方案1】:

要获得给定圆圈内的List&lt;Point&gt; 点,您可以让GDI+ 为您完成工作:

List<Point> PointsInCircle(int diameter)
{
    List<Point> points = new List<Point>();
    Color black = Color.FromArgb(255, 0, 0, 0);
    using (Bitmap bmp = new Bitmap(diameter, diameter))
    using (Graphics g = Graphics.FromImage(bmp))
    {
        g.Clear(Color.White);
        g.FillEllipse(Brushes.Black, 0, 0, diameter, diameter);
        for (int y = 0; y < diameter; y++)
            for (int x = 0; x < diameter; x++)
                if (bmp.GetPixel(x, y) == black) points.Add(new Point(x, y));
    }
    return points;
}

要在位图中某处的圆上使用列表,您只需将圆心的偏移量添加到列表点..

为了使例程更快,您可以使用 LockBits:

List<Point> PointsInCircleFast(int diameter)
{
    List<Point> points = new List<Point>();
    Color black = Color.FromArgb(255, 0, 0, 0);
    using (Bitmap bmp = new Bitmap(diameter, diameter,PixelFormat.Format32bppArgb))
    {
        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.Clear(Color.White);
            g.FillEllipse(Brushes.Black, 0, 0, diameter, diameter);
        }
        Size size0 = bmp.Size;
        Rectangle rect = new Rectangle(Point.Empty, size0);
        BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);

        int size1 = bmpData.Stride * bmpData.Height;
        byte[] data = new byte[size1];
        System.Runtime.InteropServices.Marshal.Copy(bmpData.Scan0, data, 0, size1);

        for (int y = 0; y < diameter; y++)
            for (int x = 0; x < diameter; x++)
            {
                int index =  y * bmpData.Stride + x * 4;
                if (data[index] == 0 ) points.Add(new Point(x, y));
            }
    }
    return points;
}

但是对于非常大的圈子,创建大列表可能是瓶颈。您可以通过仅创建四分之一的点进行优化,也可以内联处理。

内部LockBits 循环内的像素颜色可以这样访问:

Color c = Color.FromArgb(data[index + 3], data[index + 2], data[index + 1], data[index]);

【讨论】:

  • 嗨 Taw,我做了类似的事情,但问题是 GetPixel 函数非常慢......
  • 嗯,有两种选择。如果圆圈的直径都相同,则只需调用一次。如果您需要不同的直径,那么您可以轻松地将其更改为使用 Lockbits。如果你有兴趣,我会告诉你..
  • 我需要第二个选项,请你解释一下方法?
【解决方案2】:

这应该是一种明显更快的方法。它只使用快速运算,没有平方根。

List<int> indices = new List<int>();

for (int x = 0; x < width; x++)
{
    for (int y = 0; y < height; y++)
    {
        double dx = x - m1;
        double dy = y - m2;
        double distanceSquared = dx * dx + dy * dy;

        if (distanceSquared <= radiusSquared)
        {
            indices.Add(x + y * width);
        }
    }
}

此代码取自this answer

【讨论】:

    【解决方案3】:

    您可以将现有列表与边框像素一起使用,然后循环遍历它的 y 坐标,获取圆圈左侧和右侧的坐标。由此,您可以通过从左到右遍历所有 x 坐标来计算所有中间像素的坐标。与依赖 GDI 相比,这应该明显更快且内存占用更少。

    【讨论】:

    • 这就是我在对这个问题的最后评论中的意思,但它依赖于已经拥有列表并且创建该列表对我来说看起来比将圆形算法留给 GDI 更昂贵..
    • 使用 GDI 编码当然更容易,但我不得不认为它相当消耗内存和 CPU。每次调用它时它都必须分配一个新的位图,加上它必须绘制圆,然后你必须循环整个位图寻找像素。如果你只做小圈子可能没问题,但更大的圈子会成为瓶颈。一些快速计算表明,一个 1024x1024 的圆圈将使用超过 32mb 的内存。在您开始绘制之前,所有这些都必须分配和初始化。
    • 你可能想看看这个答案stackoverflow.com/questions/14487322/…。因为它可以只使用加法和乘法来完成所有数学运算,所以它应该会更快。
    • 你说的很对,除了我从 OP 猜测只需要一种尺寸。这对任何事情都足够快。仍然 +1 挖掘快速圈!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-21
    • 1970-01-01
    • 2015-12-18
    相关资源
    最近更新 更多