如何使用 NumPy 用 non-overlapping 个矩形随机填充一个区域?
How to randomly fill a region with non-overlapping rectangles using NumPy?
如何使用 NumPy 用随机大小的矩形随机填充给定的矩形区域,而不让矩形相互重叠?
我的想法是创建一个与区域形状相同的二维数组,用零填充数组,然后对于每个需要的矩形,随机select数组内两个未设置的坐标,将这两个点做成一个矩形,然后用1.
填充矩形对应的数组里面的区域
不知何故它不起作用:
代码:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
from random import randbytes, randrange
def random_rectangles(width=1920, height=1080, number=24):
fig = plt.figure(figsize=(width/100, height/100), dpi=100, facecolor='black')
ax = fig.add_subplot(111)
ax.set_axis_off()
grid = np.zeros((height, width))
for i in range(number):
free = np.transpose(np.nonzero(grid == 0))
y1, x1 = free[randrange(free.shape[0])]
y2, x2 = free[randrange(free.shape[0])]
if x1 > x2: x1, x2 = x2, x1
if y1 > y2: y1, y2 = y2, y1
grid[y1:y2, x1:x2] = 1
w, h = x2-x1, y2-y1
x, y = x1, -y2
color = '#'+randbytes(3).hex()
ax.add_patch(Rectangle((x, y), w, h, fill=True,facecolor=color,edgecolor='#808080',lw=1))
plt.xlim(0, width)
plt.ylim(-height, 0)
plt.axis('scaled')
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.show()
我不明白,我试过这个:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
import random
class Grid:
def __init__(self, x1, x2, y1, y2):
assert x2 > x1 and y2 > y1
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
self.subgrids = []
self.divisions = dict()
self.last_subgrid = None
def random(self):
if not self.subgrids:
x = self.x1 + random.random() * (self.x2 - self.x1)
y = self.y1 + random.random() * (self.y2 - self.y1)
return x, y
else:
if not self.last_subgrid:
subgrid = random.choice(self.subgrids)
self.last_subgrid = subgrid
return subgrid.random()
else:
x, y = self.last_subgrid.random()
self.last_subgrid = None
return x, y
def set_subgrid(self, shape):
x1, x2, y1, y2 = shape
assert x2 > x1 and y2 > y1
assert self.x1 <= x2 <= self.x2 and self.y1 <= y2 <= self.y2
if not self.subgrids:
eight = [
(self.x1, x1, self.y1, y1),
(x1, x2, self.y1, y1),
(x2, self.x2, self.y1, y1),
(x1, x2, y1, y2),
(x2, self.x2, y1, y2),
(self.x1, x1, y2, self.y2),
(x1, x2, y2, self.y2),
(x2, self.x2, y2, self.y2)
]
for a, b, c, d in eight:
if a != b and c != d:
subgrid = Grid(a, b, c, d)
self.subgrids.append(subgrid)
self.divisions[(a, b, c, d)] = subgrid
else:
for a, b, c, d in self.divisions:
if a <= x1 < x2 <= b and c <= y1 < y2 <= d:
self.divisions[(a, b, c, d)].set_subgrid((x1, x2, y1, y2))
def random_rectangles(width=1920, height=1080, number=24):
fig = plt.figure(figsize=(width/100, height/100), dpi=100, facecolor='black')
ax = fig.add_subplot(111)
ax.set_axis_off()
grid = Grid(0, width, 0, height)
for i in range(number):
x1, y1 = grid.random()
x2, y2 = grid.random()
if x1 > x2: x1, x2 = x2, x1
if y1 > y2: y1, y2 = y2, y1
grid.set_subgrid((x1, x2, y1, y2))
w, h = x2-x1, y2-y1
color = '#'+random.randbytes(3).hex()
ax.add_patch(Rectangle((x1, y1), w, h, fill=True,facecolor=color,edgecolor='#808080',lw=1))
plt.xlim(0, width)
plt.ylim(0, height)
plt.axis('scaled')
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.show()
没用:
我做到了
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import random
class Grid:
def __init__(self, x1, x2, y1, y2):
assert x2 > x1 and y2 > y1
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
self.subgrids = []
def random(self):
if not self.subgrids:
x = self.x1 + random.random() * (self.x2 - self.x1)
y = self.y1 + random.random() * (self.y2 - self.y1)
four = [
(self.x1, x, self.y1, y),
(x, self.x2, self.y1, y),
(self.x1, x, y, self.y2),
(x, self.x2, y, self.y2)
]
for a, b, c, d in four:
if a != b and c != d:
subgrid = Grid(a, b, c, d)
self.subgrids.append(subgrid)
else:
random.choice(self.subgrids).random()
def flatten(self):
if not self.subgrids:
return
result = []
for subgrid in self.subgrids:
if not subgrid.subgrids:
result.append((subgrid.x1, subgrid.x2, subgrid.y1, subgrid.y2))
else:
result.extend(subgrid.flatten())
return result
def random_rectangles(width=1920, height=1080, number=24):
fig = plt.figure(figsize=(width/100, height/100), dpi=100, facecolor='black')
ax = fig.add_subplot(111)
ax.set_axis_off()
grid = Grid(0, width, 0, height)
for i in range(number): grid.random()
rectangles = grid.flatten()
for x1, x2, y1, y2 in rectangles:
w, h = x2-x1, y2-y1
color = '#'+random.randbytes(3).hex()
ax.add_patch(Rectangle((x1, y1), w, h, fill=True,facecolor=color,edgecolor='#808080',lw=1))
plt.xlim(0, width)
plt.ylim(0, height)
plt.axis('scaled')
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.show()
我终于做到了,但是结果不是我想象的那样,我觉得我的实现还不够好。谁能帮帮我?
从另一个方向解决问题可以获得更好的结果。
你所做的是在每次采样时将样本 space 分成子 spaces,然后你再次在所述子spaces 中采样(或类似的东西类似于那个)。
如果您事先拆分样本 space,您可以获得均匀采样 space 的网格,您可以从中采样:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.patches import Rectangle
from numpy.typing import ArrayLike
from numpy.random import default_rng
rng = default_rng(42069)
def get_rects(n: int) -> np.ndarray:
"""
Params
------
n: number of rectangles
"""
ceilsqrtn = int(np.ceil(np.sqrt(n)))
n_grids = ceilsqrtn * ceilsqrtn
# Create rectangles in the "full space", that is the [0, 1] space
rects = rng.uniform(size = (n, 4))
# To ensure that the rectangles are in (x1, x2, y1, y2) format where
# Upper left corner is (x1, y1) and bottom right corner (x2, y2)
# Result looks fine without this, but it's a nice to have
rects[:,:2].sort(1)
rects[:,2:].sort(1)
# Create a ceilsqrtn x ceilsqrtn even grid space
flat_grid_indices = rng.choice(n_grids, n, False)
offsets = np.unravel_index(flat_grid_indices, (ceilsqrtn, ceilsqrtn))
# Move each rectangle into their own randomly assigned grid
# This will result with triangles in a space that is ceilsqrtn times larger than the [0, 1] space
rects[:,:2] += offsets[1][..., None]
rects[:,2:] += offsets[0][..., None]
# Scale everything down to the [0, 1] space
rects /= ceilsqrtn
return rects
def plot_rects(rects: ArrayLike, width: int = 10, height: int = 10):
fig, ax = plt.subplots(figsize=(width, height))
for x1, x2, y1, y2 in rects:
ax.add_patch(Rectangle((x1, y1), x2 - x1, y2 - y1, facecolor='gray', edgecolor='black', fill=True))
plt.show()
rects = get_rects(150)
plot_rects(rects)
结果:
至少这是一个好的开始。要获得具有其他特征的矩形,您可以简单地从另一个分布(例如高斯分布)中采样。这样,您将获得更多相似的矩形,但您需要将溢出的矩形剪裁到其他 subspaces 或类似的东西中。另一种选择是 [0,1] 域内的截断分布。
编辑:代码清理和更好的评论
如何使用 NumPy 用随机大小的矩形随机填充给定的矩形区域,而不让矩形相互重叠?
我的想法是创建一个与区域形状相同的二维数组,用零填充数组,然后对于每个需要的矩形,随机select数组内两个未设置的坐标,将这两个点做成一个矩形,然后用1.
填充矩形对应的数组里面的区域不知何故它不起作用:
代码:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
from random import randbytes, randrange
def random_rectangles(width=1920, height=1080, number=24):
fig = plt.figure(figsize=(width/100, height/100), dpi=100, facecolor='black')
ax = fig.add_subplot(111)
ax.set_axis_off()
grid = np.zeros((height, width))
for i in range(number):
free = np.transpose(np.nonzero(grid == 0))
y1, x1 = free[randrange(free.shape[0])]
y2, x2 = free[randrange(free.shape[0])]
if x1 > x2: x1, x2 = x2, x1
if y1 > y2: y1, y2 = y2, y1
grid[y1:y2, x1:x2] = 1
w, h = x2-x1, y2-y1
x, y = x1, -y2
color = '#'+randbytes(3).hex()
ax.add_patch(Rectangle((x, y), w, h, fill=True,facecolor=color,edgecolor='#808080',lw=1))
plt.xlim(0, width)
plt.ylim(-height, 0)
plt.axis('scaled')
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.show()
我不明白,我试过这个:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.patches import Rectangle
import random
class Grid:
def __init__(self, x1, x2, y1, y2):
assert x2 > x1 and y2 > y1
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
self.subgrids = []
self.divisions = dict()
self.last_subgrid = None
def random(self):
if not self.subgrids:
x = self.x1 + random.random() * (self.x2 - self.x1)
y = self.y1 + random.random() * (self.y2 - self.y1)
return x, y
else:
if not self.last_subgrid:
subgrid = random.choice(self.subgrids)
self.last_subgrid = subgrid
return subgrid.random()
else:
x, y = self.last_subgrid.random()
self.last_subgrid = None
return x, y
def set_subgrid(self, shape):
x1, x2, y1, y2 = shape
assert x2 > x1 and y2 > y1
assert self.x1 <= x2 <= self.x2 and self.y1 <= y2 <= self.y2
if not self.subgrids:
eight = [
(self.x1, x1, self.y1, y1),
(x1, x2, self.y1, y1),
(x2, self.x2, self.y1, y1),
(x1, x2, y1, y2),
(x2, self.x2, y1, y2),
(self.x1, x1, y2, self.y2),
(x1, x2, y2, self.y2),
(x2, self.x2, y2, self.y2)
]
for a, b, c, d in eight:
if a != b and c != d:
subgrid = Grid(a, b, c, d)
self.subgrids.append(subgrid)
self.divisions[(a, b, c, d)] = subgrid
else:
for a, b, c, d in self.divisions:
if a <= x1 < x2 <= b and c <= y1 < y2 <= d:
self.divisions[(a, b, c, d)].set_subgrid((x1, x2, y1, y2))
def random_rectangles(width=1920, height=1080, number=24):
fig = plt.figure(figsize=(width/100, height/100), dpi=100, facecolor='black')
ax = fig.add_subplot(111)
ax.set_axis_off()
grid = Grid(0, width, 0, height)
for i in range(number):
x1, y1 = grid.random()
x2, y2 = grid.random()
if x1 > x2: x1, x2 = x2, x1
if y1 > y2: y1, y2 = y2, y1
grid.set_subgrid((x1, x2, y1, y2))
w, h = x2-x1, y2-y1
color = '#'+random.randbytes(3).hex()
ax.add_patch(Rectangle((x1, y1), w, h, fill=True,facecolor=color,edgecolor='#808080',lw=1))
plt.xlim(0, width)
plt.ylim(0, height)
plt.axis('scaled')
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.show()
没用:
我做到了
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle
import random
class Grid:
def __init__(self, x1, x2, y1, y2):
assert x2 > x1 and y2 > y1
self.x1 = x1
self.x2 = x2
self.y1 = y1
self.y2 = y2
self.subgrids = []
def random(self):
if not self.subgrids:
x = self.x1 + random.random() * (self.x2 - self.x1)
y = self.y1 + random.random() * (self.y2 - self.y1)
four = [
(self.x1, x, self.y1, y),
(x, self.x2, self.y1, y),
(self.x1, x, y, self.y2),
(x, self.x2, y, self.y2)
]
for a, b, c, d in four:
if a != b and c != d:
subgrid = Grid(a, b, c, d)
self.subgrids.append(subgrid)
else:
random.choice(self.subgrids).random()
def flatten(self):
if not self.subgrids:
return
result = []
for subgrid in self.subgrids:
if not subgrid.subgrids:
result.append((subgrid.x1, subgrid.x2, subgrid.y1, subgrid.y2))
else:
result.extend(subgrid.flatten())
return result
def random_rectangles(width=1920, height=1080, number=24):
fig = plt.figure(figsize=(width/100, height/100), dpi=100, facecolor='black')
ax = fig.add_subplot(111)
ax.set_axis_off()
grid = Grid(0, width, 0, height)
for i in range(number): grid.random()
rectangles = grid.flatten()
for x1, x2, y1, y2 in rectangles:
w, h = x2-x1, y2-y1
color = '#'+random.randbytes(3).hex()
ax.add_patch(Rectangle((x1, y1), w, h, fill=True,facecolor=color,edgecolor='#808080',lw=1))
plt.xlim(0, width)
plt.ylim(0, height)
plt.axis('scaled')
fig.subplots_adjust(left=0, bottom=0, right=1, top=1, wspace=0, hspace=0)
plt.show()
我终于做到了,但是结果不是我想象的那样,我觉得我的实现还不够好。谁能帮帮我?
从另一个方向解决问题可以获得更好的结果。
你所做的是在每次采样时将样本 space 分成子 spaces,然后你再次在所述子spaces 中采样(或类似的东西类似于那个)。
如果您事先拆分样本 space,您可以获得均匀采样 space 的网格,您可以从中采样:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.patches import Rectangle
from numpy.typing import ArrayLike
from numpy.random import default_rng
rng = default_rng(42069)
def get_rects(n: int) -> np.ndarray:
"""
Params
------
n: number of rectangles
"""
ceilsqrtn = int(np.ceil(np.sqrt(n)))
n_grids = ceilsqrtn * ceilsqrtn
# Create rectangles in the "full space", that is the [0, 1] space
rects = rng.uniform(size = (n, 4))
# To ensure that the rectangles are in (x1, x2, y1, y2) format where
# Upper left corner is (x1, y1) and bottom right corner (x2, y2)
# Result looks fine without this, but it's a nice to have
rects[:,:2].sort(1)
rects[:,2:].sort(1)
# Create a ceilsqrtn x ceilsqrtn even grid space
flat_grid_indices = rng.choice(n_grids, n, False)
offsets = np.unravel_index(flat_grid_indices, (ceilsqrtn, ceilsqrtn))
# Move each rectangle into their own randomly assigned grid
# This will result with triangles in a space that is ceilsqrtn times larger than the [0, 1] space
rects[:,:2] += offsets[1][..., None]
rects[:,2:] += offsets[0][..., None]
# Scale everything down to the [0, 1] space
rects /= ceilsqrtn
return rects
def plot_rects(rects: ArrayLike, width: int = 10, height: int = 10):
fig, ax = plt.subplots(figsize=(width, height))
for x1, x2, y1, y2 in rects:
ax.add_patch(Rectangle((x1, y1), x2 - x1, y2 - y1, facecolor='gray', edgecolor='black', fill=True))
plt.show()
rects = get_rects(150)
plot_rects(rects)
结果:
至少这是一个好的开始。要获得具有其他特征的矩形,您可以简单地从另一个分布(例如高斯分布)中采样。这样,您将获得更多相似的矩形,但您需要将溢出的矩形剪裁到其他 subspaces 或类似的东西中。另一种选择是 [0,1] 域内的截断分布。
编辑:代码清理和更好的评论