在图像中找到最小数量的矩形
Find minimal number of rectangles in the image
我有二进制图像,其中随机放置了矩形,我想获取这些矩形的位置和大小。
如果可能的话,我希望使用最少数量的矩形 完全重新创建图像。
左边是我的原始图像,右边是我应用后得到的图像scipys.find_objects()
(就像 this question 的建议)。
import scipy
# image = scipy.ndimage.zoom(image, 9, order=0)
labels, n = scipy.ndimage.measurements.label(image, np.ones((3, 3)))
bboxes = scipy.ndimage.measurements.find_objects(labels)
img_new = np.zeros_like(image)
for bb in bboxes:
img_new[bb[0], bb[1]] = 1
如果矩形相距很远,则此方法工作正常,但如果它们重叠并构建更复杂的结构,则此算法只会给我最大的边界框(对图像进行上采样没有区别)。我感觉应该已经存在执行此操作的 scipy
或 opencv
方法。
我很高兴知道是否有人知道如何解决这个问题,或者更好地了解现有的解决方案。
As result I want a list of rectangles (ie. lower-left-corner : upper-righ-corner) in the image. The condition is that when I redraw those filled rectangles I want to get exactly the same image as before. If possible the number of rectangles should be minimal.
这里是生成样本图片的代码(还有一个更复杂的例子original vs scipy)
import numpy as np
def random_rectangle_image(grid_size, n_obstacles, rectangle_limits):
n_dim = 2
rect_pos = np.random.randint(low=0, high=grid_size-rectangle_limits[0]+1,
size=(n_obstacles, n_dim))
rect_size = np.random.randint(low=rectangle_limits[0],
high=rectangle_limits[1]+1,
size=(n_obstacles, n_dim))
# Crop rectangle size if it goes over the boundaries of the world
diff = rect_pos + rect_size
ex = np.where(diff > grid_size, True, False)
rect_size[ex] -= (diff - grid_size)[ex].astype(int)
img = np.zeros((grid_size,)*n_dim, dtype=bool)
for i in range(n_obstacles):
p_i = np.array(rect_pos[i])
ps_i = p_i + np.array(rect_size[i])
img[tuple(map(slice, p_i, ps_i))] = True
return img
img = random_rectangle_image(grid_size=64, n_obstacles=30,
rectangle_limits=[4, 10])
这里有一些可以帮助您入门的东西:一种简单的算法,它可以遍历您的图像并创建尽可能大的矩形。就像现在一样,它仅 标记 矩形,但不报告返回坐标或计数。这是为了单独可视化算法。
保存为 PNG 时,不需要任何外部库来加载和访问左侧图像,除了 PIL。我假设周围 15 像素的边框可以忽略。
from PIL import Image
def fill_rect (pixels,xp,yp,w,h):
for y in range(h):
for x in range(w):
pixels[xp+x,yp+y] = (255,0,0,255)
for y in range(h):
pixels[xp,yp+y] = (255,192,0,255)
pixels[xp+w-1,yp+y] = (255,192,0,255)
for x in range(w):
pixels[xp+x,yp] = (255,192,0,255)
pixels[xp+x,yp+h-1] = (255,192,0,255)
def find_rect (pixels,x,y,maxx,maxy):
# assume we're at the top left
# get max horizontal span
width = 0
height = 1
while x+width < maxx and pixels[x+width,y] == (0,0,0,255):
width += 1
# now walk down, adjusting max width
while y+height < maxy:
for w in range(x,x+width,1):
if pixels[x,y+height] != (0,0,0,255):
break
if pixels[x,y+height] != (0,0,0,255):
break
height += 1
# fill rectangle
fill_rect (pixels,x,y,width,height)
image = Image.open('A.png')
pixels = image.load()
width, height = image.size
print (width,height)
for y in range(16,height-15,1):
for x in range(16,width-15,1):
if pixels[x,y] == (0,0,0,255):
find_rect (pixels,x,y,width,height)
image.show()
从输出
你可以观察到检测算法可以改进,例如,"obvious"左上角的两个矩形被拆分为3个。类似的,中间较大的结构也包含一个矩形多于绝对需要。
可能的改进是调整 find_rect
例程以找到最合适的 ¹,或者存储坐标并使用数学(超出我的知识范围)来查找可以连接哪些矩形。
¹ 对此有进一步的想法。目前所有找到的矩形都会立即填充 "found" 颜色。您可以尝试检测明显的多个矩形,然后,在标记第一个矩形之后,要检查的其他矩形可能是黑色 或 红色。即兴我会说你需要尝试不同的扫描顺序(从上到下或反向,从左到右或反向)以实际找到任意组合中最少需要的矩形数。
我有二进制图像,其中随机放置了矩形,我想获取这些矩形的位置和大小。 如果可能的话,我希望使用最少数量的矩形 完全重新创建图像。
左边是我的原始图像,右边是我应用后得到的图像scipys.find_objects()
(就像 this question 的建议)。
import scipy
# image = scipy.ndimage.zoom(image, 9, order=0)
labels, n = scipy.ndimage.measurements.label(image, np.ones((3, 3)))
bboxes = scipy.ndimage.measurements.find_objects(labels)
img_new = np.zeros_like(image)
for bb in bboxes:
img_new[bb[0], bb[1]] = 1
如果矩形相距很远,则此方法工作正常,但如果它们重叠并构建更复杂的结构,则此算法只会给我最大的边界框(对图像进行上采样没有区别)。我感觉应该已经存在执行此操作的 scipy
或 opencv
方法。
我很高兴知道是否有人知道如何解决这个问题,或者更好地了解现有的解决方案。
As result I want a list of rectangles (ie. lower-left-corner : upper-righ-corner) in the image. The condition is that when I redraw those filled rectangles I want to get exactly the same image as before. If possible the number of rectangles should be minimal.
这里是生成样本图片的代码(还有一个更复杂的例子original vs scipy)
import numpy as np
def random_rectangle_image(grid_size, n_obstacles, rectangle_limits):
n_dim = 2
rect_pos = np.random.randint(low=0, high=grid_size-rectangle_limits[0]+1,
size=(n_obstacles, n_dim))
rect_size = np.random.randint(low=rectangle_limits[0],
high=rectangle_limits[1]+1,
size=(n_obstacles, n_dim))
# Crop rectangle size if it goes over the boundaries of the world
diff = rect_pos + rect_size
ex = np.where(diff > grid_size, True, False)
rect_size[ex] -= (diff - grid_size)[ex].astype(int)
img = np.zeros((grid_size,)*n_dim, dtype=bool)
for i in range(n_obstacles):
p_i = np.array(rect_pos[i])
ps_i = p_i + np.array(rect_size[i])
img[tuple(map(slice, p_i, ps_i))] = True
return img
img = random_rectangle_image(grid_size=64, n_obstacles=30,
rectangle_limits=[4, 10])
这里有一些可以帮助您入门的东西:一种简单的算法,它可以遍历您的图像并创建尽可能大的矩形。就像现在一样,它仅 标记 矩形,但不报告返回坐标或计数。这是为了单独可视化算法。
保存为 PNG 时,不需要任何外部库来加载和访问左侧图像,除了 PIL。我假设周围 15 像素的边框可以忽略。
from PIL import Image
def fill_rect (pixels,xp,yp,w,h):
for y in range(h):
for x in range(w):
pixels[xp+x,yp+y] = (255,0,0,255)
for y in range(h):
pixels[xp,yp+y] = (255,192,0,255)
pixels[xp+w-1,yp+y] = (255,192,0,255)
for x in range(w):
pixels[xp+x,yp] = (255,192,0,255)
pixels[xp+x,yp+h-1] = (255,192,0,255)
def find_rect (pixels,x,y,maxx,maxy):
# assume we're at the top left
# get max horizontal span
width = 0
height = 1
while x+width < maxx and pixels[x+width,y] == (0,0,0,255):
width += 1
# now walk down, adjusting max width
while y+height < maxy:
for w in range(x,x+width,1):
if pixels[x,y+height] != (0,0,0,255):
break
if pixels[x,y+height] != (0,0,0,255):
break
height += 1
# fill rectangle
fill_rect (pixels,x,y,width,height)
image = Image.open('A.png')
pixels = image.load()
width, height = image.size
print (width,height)
for y in range(16,height-15,1):
for x in range(16,width-15,1):
if pixels[x,y] == (0,0,0,255):
find_rect (pixels,x,y,width,height)
image.show()
从输出
你可以观察到检测算法可以改进,例如,"obvious"左上角的两个矩形被拆分为3个。类似的,中间较大的结构也包含一个矩形多于绝对需要。
可能的改进是调整 find_rect
例程以找到最合适的 ¹,或者存储坐标并使用数学(超出我的知识范围)来查找可以连接哪些矩形。
¹ 对此有进一步的想法。目前所有找到的矩形都会立即填充 "found" 颜色。您可以尝试检测明显的多个矩形,然后,在标记第一个矩形之后,要检查的其他矩形可能是黑色 或 红色。即兴我会说你需要尝试不同的扫描顺序(从上到下或反向,从左到右或反向)以实际找到任意组合中最少需要的矩形数。