计算圆(图像)内的随机点(像素)


19

我有一个图像,该图像在特定位置包含一个圆,并具有特定的直径。我需要做的是能够计算圆内的随机点,然后操纵与这些点相关的像素。我已经有以下代码:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * ( Math.PI * 2 );
    var x = _originX + ( _radius * Math.Cos( angle ) );
    var y = _originY + ( _radius * Math.Sin( angle ) );
    return new Point( ( int )x, ( int )y );
}

这样可以很好地找到圆的所有点,但是我需要圆中任何位置的所有点。如果这没有意义,请告诉我,我会尽力澄清。


检查更新。
David Gouveia

3
从无意中创建加权分布是一个常见错误,这是一个好问题。
蒂姆·霍尔特

Answers:


35

如果您想要一个简单的解决方案,只需将半径随机化:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var radius = _random.NextDouble() * _radius;
    var x = _originX + radius * Math.Cos(angle);
    var y = _originY + radius * Math.Sin(angle);
    return new Point((int)x,(int)y);
}

但是,这会使您的点更加集中于圆心:

在此处输入图片说明

为了获得均匀分布,请对该算法进行以下更改:

var radius = Math.Sqrt(_random.NextDouble()) * _radius;

这将产生以下结果:

在此处输入图片说明

有关更多信息,请检查以下链接:MathWorld-Disk Point Picking

最后,这是一个简单的JsFiddle演示,比较了两种算法。


1
极好的答案。只需添加一件事:别忘了给随机数生成器添加种子:)
kevintodisco 2012年

糟糕,您遇到了麻烦-当我发布我的信息时,没有看到此信息。Wolfram网站是此类事情的绝佳资源。
蒂姆·霍尔特

1
@TimHolt发生的时间:)
大卫·戈维亚

这是否假设圆心在0,0?
jjxtra 2013年

@PsychoDad圈子的中心是(_originX,_originY)
David Gouveia

5

不要只使用随机r和theta!这将创建一个加权分布,并在中心具有更多点。此页面很好地说明了这一点...

http://mathworld.wolfram.com/DiskPointPicking.html

这是创建非加权分布的方法...

var r = rand(0,1)
var theta = rand(0,360)

var x = sqrt(r) * cos(theta)
var y = sqrt(r) * sin(theta)

糟糕,所选答案重复:P
蒂姆·霍尔特

我很困惑,因为您说不要使用随机r和theta,因为它会创建加权分布,然后显示的代码表明您创建了非加权分布会在[0,1]范围内生成r。您是否打算对随机数求平方?
PeteUK '16

是的,进行半径的平方根(只要是0-1)就可以减少中间点的意外集中。请参阅我发布的Wolfram链接,该链接对此进行了说明并以数学方式比我能更好地对其进行了说明。
蒂姆·霍尔特

我的错。我看到您在计算x和y时执行sqrt(r)。
PeteUK '16

4

你在那儿 除了生成随机角度外,只需生成一个小于或等于半径的随机距离,然后对其进行加权即可获得均匀分布:

private Point CalculatePoint()
{
    var angle = _random.NextDouble() * Math.PI * 2;
    var distance = Math.Sqrt(_random.NextDouble()) * _radius;
    var x = _originX + (distance * Math.Cos(angle));
    var y = _originY + (distance * Math.Sin(angle));
    return new Point((int)x, (int)y);
}

现在您正在考虑Polar

您也可以像这样加权距离,以避免平方根:

var distance = _random.NextDouble() + _random.NextDouble();
distance = (distance <= 1 ? distance : 2 - distance) * _radius;

好吧,所以我们在相差几秒钟内给出了完全相同的答案。怎么办?:)
David Gouveia'4

@DavidGouveia我们俩都因正确而受到赞誉。每个人都赢了!:D
乔恩·普迪

两个答案都非常感谢(链接也是如此!)。男人我是个白痴,虽然我没看到自己,但对我-1 :(再次感谢你们两个!
DMills 2012年

这将生成随机点,但它们不会均匀分布在磁盘上,对吗?而是将它们偏重于中心。只是检查我没有错过什么。
PeteUK '16

1
@PeteUK:没错,距离应该加权。让我更新。
乔恩·普迪

3

如果性能是一个问题,那么一个替代解决方案是在一个具有圆的宽度/高度的框中生成一个随机位置,然后丢弃不在圆区域内的所有点。

此方法的优点是您没有执行cos / sin / sqrt函数,这可能会节省很多速度,具体取决于您的平台。

var x = _random.NextDouble();
var y = _random.NextDouble();
if (x*x + y*y < 1.0f)
{
    // we have a usable point inside a circle
    x = x * diameter - _radius + _OriginX;
    y = y * diameter - _radius + _OriginY;
    // use the point(x,y)
}

我将尝试一下,看看它是否可以加快速度。我不确定我是否有性能问题,但是无论如何我都会尝试,谢谢!
DMills

0

我采用了所列评论之一的方法,并扩展了功能以创建一个甜甜圈形的点生成系统。

            private Vector2 CalculatePosition()
            {
                double angle = _rnd.NextDouble() * Math.PI * 2;
                double radius = InnerCircleRadius + (Math.Sqrt(_rnd.NextDouble()) * (OuterCircleRadius - InnerCircleRadius));
                double x = (_context.ScreenLayout.Width * 0.5f) + (radius * Math.Cos(angle));
                double y = (_context.ScreenLayout.Height * 0.5f) + (radius * Math.Sin(angle));
                return new Vector2((int)x, (int)y);
            }

它与前面提到的方法类似,但是提供了不同的结果。圆的内部将不留任何空白。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.