【问题标题】:randomly draw pixels on a screen with unequal probability distribution在不等概率分布的屏幕上随机绘制像素
【发布时间】:2021-10-07 05:09:19
【问题描述】:

我想在屏幕上随机绘制像素,因此概率不应该平均分布。

示例:假设屏幕有 1920 x 1080 像素。在绘图事件中,位于位置 (500,500) 的 100 x 100 矩形中的像素被绘制的概率应该是矩形外像素的 10 倍。

为了实现这一点,我首先创建了一个包含概率的数组。矩形内的位置的值为 10,所有其他位置的值为 1。

 for i := 1 to 1920 do
 begin
   for j := 1 to 1080 do
   begin
     FProbability[i, j]:=1;
     if InRange(i, 500, 600) and InRange(j, 500, 600) then
     begin
       FProbability[i, j]:=10;
     end;
   end;
 end;

然后我列出所有像素:

 FPixelList:=TList<TPoint>.Create;
 for i := 1 to 1920 do
 begin
   for j := 1 to 1080 do
   begin
     for k := 1 to FProbabilty[i, j] do
     begin
       FPixelList.Add(TPoint.Create(i,j))
     end;
   end;
 end;

像素列表现在对于矩形内的每个像素有 10 个条目,对于所有其他像素位置有 1 个条目。

在绘制事件中,我得到要绘制的像素位置

FPixelList[RandomRange(0, FPixelList.Count-1)]

这很好用。

但是我想知道这个问题是否有其他解决方案。如果屏幕尺寸变大,我的解决方案会使用大量内存,并且我只能使用整数值作为概率。

【问题讨论】:

  • 请注意,这本质上是一个与语言无关的问题;答案在 C++、C#、Rust 或 x86 汇编中(基本上)是相同的。事实上,这只是一个编程问题:它本质上是一个纯数学问题。
  • 您不需要任何内存。假设问题是你想要正面和反面,但反面的可能性是两倍。然后使用 Random(3) 和 0 表示正面,1 或 2 表示反面。扩展这个概念,你就完成了。

标签: delphi random probability


【解决方案1】:

正如 David Heffernan 在 cmets 中指出的那样,没有必要为此使用额外的存储空间。

相反,我们可以将随机选择的范围扩展到图像大小之外,以包括(regionOptions-1)*regionSize 附加值,其中regionOptions 表示选择像素的可能性增加,在您的示例中,这是一个区域中的 10大小为regionSize。如果随机选择的值小于图像大小,那么我们直接将其用作图像中的位置。如果它更大,那么我们执行计算以确定感兴趣区域中的相应位置。

恐怕我对Delphi不熟悉,所以这里有一些Java代码来说明。

int imWidth = 1920;
int imHeight = 1000;
int[][] image = new int[imWidth][imHeight];

int regionOptions = 10;
Rectangle region = new Rectangle(500, 500, 100, 100);

int imSize = imWidth * imHeight;

int randomRange = imSize + (regionOptions-1)*region.width*region.height;
        
int max = 0;
Random rand = new Random();
for(int i=0; i<region.width*region.height*1000; i++)
{
    int randPos = rand.nextInt(randomRange);
    
    int x, y;
    if(randPos < imSize)
    {
        x = randPos % imWidth;
        y = randPos / imWidth;
    }
    else
    {
        randPos = (randPos - imSize) / (regionOptions - 1);
        x = region.x + randPos % region.width;
        y = region.x + randPos / region.height;             
    }

    image[x][y] += 1;
    max = Math.max(max, image[x][y]);
}

为了演示的目的,我使用了以下代码创建了一个规范化的图像:

BufferedImage im = new BufferedImage(imWidth, imHeight, BufferedImage.TYPE_BYTE_GRAY);
for(int i=0; i<imWidth; i++)
{
    for(int j=0; j<imHeight; j++)
    {
        int level = (int)(255.0*image[i][j]/max);
        im.setRGB(i, j, (level << 16) | (level << 8) | level);
    }
}       
try
{
    ImageIO.write(im, "png", new File("probIm.png"));
}
catch(Exception e)
{
    e.printStackTrace();
}

这是使用regionOptions = 2 从图像中截取的区域。值为 10 时,归一化像素在感兴趣区域中过于聚集,无法显示任何背景。

【讨论】:

    【解决方案2】:

    为什么不使用不同的方法,首先在整个区域中绘制一定数量的随机位置像素,计算其中有多少是在焦点区域绘制的,有多少是在正常区域绘制的。

    然后您可以简单地将正常区域的像素数乘以 10 并减去已在焦点区域中绘制的像素数,以获得仍需要在焦点区域中绘制的像素数才能满足所需的口粮。获得此数字后,您只需在焦点区域内额外渲染所需数量的随机定位像素。

    执行此操作的代码如下所示:

    procedure TForm2.Button1Click(Sender: TObject);
    var X,Y,N: Integer;
        Rect: Trect;
        Pt: Tpoint;
        NormalAreaCount, FocusedAreaCount: Integer;
    begin
      //Set the rectagle boundaries where pixels needs to be more focused
      Rect.Left := 500;
      Rect.Top := 500;
      Rect.Width := 100;
      Rect.Height := 100;
    
      //Initialize pixels count variables to 0. Othervise they might end up bein unitialized.
      NormalAreaCount := 0;
      FocusedAreaCount := 0;
    
      //Use first loop do draw certain number of random pixels across the whole area
      for N := 0 to 200 do
      begin
        X := Random(PaintBox1.Width);
        Y := Random(PaintBox1.Height);
        PaintBox1.Canvas.Pixels[X,Y] := clBlack;
        Pt.X := X;
        Pt.Y := Y;
        if PtInRect(Rect,Pt) then
        begin
          //if the drawn pixel was in are of focus rectangle increase FocusedAreaCount
          Inc(FocusedAreaCount);
        end
        //else increase NormalAreaCount
        else Inc(NormalAreaCount);
      end;
    
      //In scond loop draw more random pixels only in focused rectangle area
      //You get the number of pixels taht needs to be drawn by multiplying the number of pixels
      //taht were drawn outsite focused area by 10 and then substract the number of pixels that
      //were already drawn within the focused area
      for N := 0 to (NormalAreaCount*10)-FocusedAreaCount-1 do
      begin
        X := RandomRange(Rect.Left,Rect.Right);
        Y := RandomRange(Rect.Top,Rect.Bottom);
        PaintBox1.Canvas.Pixels[X,Y] := clBlack;
        //Here I increase the FocusedArea count so I can verify the corect number of pixels drawn
        Inc(FocusedAreaCount);
      end;
    
      //Simply display the number of pixels drawn in focused and normal area for visual verificatiuon
      Form2.Caption := IntToStr(NormalAreaCount)+':'+IntToStr(FocusedAreaCount);
    end;
    

    当然,这种方法不能保证您固定的总像素数,因为在正常区域中绘制的初始像素数有点随机。此外,此代码不保证焦点中的像素不会在第二个循环中被绘制。

    【讨论】:

      猜你喜欢
      • 2022-06-27
      • 1970-01-01
      • 1970-01-01
      • 2016-10-19
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-10
      相关资源
      最近更新 更多