基于Python将2D俯视楼层平面图转换为正立面视图的实现问题
提问内容
我需要为2D楼层平面图的外墙映射立面效果,但目前无法基于2D俯视平面图生成对应正视图。
我已尝试使用opencv分割所需区域,虽然可以轻松识别门和窗,但始终找不到分割生成正视图的方法。
左侧为待处理的楼层平面图,右侧为预期效果:高亮显示的平面图正视图,门和窗已标记颜色。
提前感谢各位的帮助。
补充内容
我参考评论区的建议更新了代码:从上到下逐行垂直遍历,识别每行最右侧的黑色或黄色像素,随后反转列表得到从左(底部)到右(顶部)的序列,遍历该序列构建二维数组,其中墙体高度设为255像素,门的高度设为204像素。
但运行后得到的结果不符合预期。
代码如下:
from PIL import Image import inspect floorplan_path = 'data/floorplan.png' class Wall(object): colorIdentifier = (79,79,79) def __init__(self, x,y): self.position = (x,y) self.color = (79,79,79) self.height = 255 self.drawColor = (65,105,225) class Door(object): colorIdentifier = (255,255,25) def __init__(self, x,y): self.position = (x,y) self.color = (255,255,25) self.height = 204 self.drawColor = (255, 255, 25) # 尚未实现 class Window: position = None color = None # 暂时不确定如何识别 drawColor = None # 浅蓝色 height = 204 def main(): image = Image.open(floorplan_path) pixels = list(image.getdata()) image = image.convert('RGB') width, height = image.size rowItems = [] for y in range(0, height): blacks = [] for x in range(0, width): pixel = image.getpixel((x,y)) color = pixel if(color == Wall.colorIdentifier): blacks.append(Wall(x, y)) elif (color == Door.colorIdentifier): blacks.append(Door(x, y)) # 取每行最后一个元素,因为我们要生成正视图的是右侧墙面 if blacks != []: rowItems.append(blacks[-1]) # 移除无值的行 y = [x for x in rowItems if x != []] # 反转列表让墙面从左到右(对应原平面图从上到下)排列 leftToRight = list(reversed(y)) # 二维数组存储正视图的行列像素,包含对应颜色和高度信息 imageArray = [] for item in leftToRight: if isinstance(item, Wall): column = [item.drawColor] * item.height imageArray.append(column) elif isinstance(item, Door): column = [item.drawColor] * item.height column.append(Wall.drawColor) * (255-item.height) imageArray.append(column) img = Image.new('RGB', (255, 255)) for i in range(len(imageArray)): for j in range(len(imageArray[i])): img.putpixel((i, j), (imageArray[i][j])) img.save('output.png') if __name__ == '__main__': main()
使用左侧的楼层平面图运行代码后,输出结果不符合预期。
回答
问题排查与修正方案
现有代码存在4处核心问题,修正后即可得到符合预期的结果:
- 语法错误:门区域补全墙体的代码写法错误,
column.append(Wall.drawColor) * (255-item.height)逻辑无效,append方法返回None,无法进行乘法运算,应改为列表拼接写法 - 坐标映射错误:PIL的像素坐标系Y轴从上到下递增,当前直接将列数组的索引作为Y坐标赋值,会导致门出现在立面图顶部,和预期的门在底部的效果相反,需要调整列数组的像素顺序
- 输出尺寸固定问题:输出图像宽度写死为255,若原平面图的高度(即立面图的总宽度)不等于255,会出现内容截断或大面积留白,需要动态匹配输出尺寸
- 冗余代码:第一次遍历已经过滤了无有效像素的行,后续的空行过滤步骤无作用可删除
修正后代码
from PIL import Image floorplan_path = 'data/floorplan.png' class Wall(object): colorIdentifier = (79,79,79) def __init__(self, x,y): self.position = (x,y) self.color = (79,79,79) self.height = 255 self.drawColor = (65,105,225) class Door(object): colorIdentifier = (255,255,25) def __init__(self, x,y): self.position = (x,y) self.color = (255,255,25) self.height = 204 self.drawColor = (255, 255, 25) # 尚未实现 class Window: position = None color = None # 暂时不确定如何识别 drawColor = None # 浅蓝色 height = 204 def main(): image = Image.open(floorplan_path).convert('RGB') width, height = image.size rowItems = [] for y in range(0, height): blacks = [] for x in range(0, width): color = image.getpixel((x,y)) if color == Wall.colorIdentifier: blacks.append(Wall(x, y)) elif color == Door.colorIdentifier: blacks.append(Door(x, y)) if blacks: rowItems.append(blacks[-1]) # 反转列表对应立面图从左到右的顺序 leftToRight = list(reversed(rowItems)) # 动态设置输出尺寸:宽度为立面图总列数,高度为255像素 output_width = len(leftToRight) img = Image.new('RGB', (output_width, 255)) for col_idx, item in enumerate(leftToRight): if isinstance(item, Wall): column = [item.drawColor] * 255 elif isinstance(item, Door): # 门在下方,上方为墙体,匹配PIL坐标从上到下的规则 column = [Wall.drawColor] * (255 - item.height) + [item.drawColor] * item.height # 逐像素写入 for row_idx, pixel_color in enumerate(column): img.putpixel((col_idx, row_idx), pixel_color) img.save('output.png') if __name__ == '__main__': main()
优化建议
如果需要适配带转角的外墙,可先做轮廓提取获取外墙的矢量路径,再沿路径方向采样像素生成多段立面图拼接即可。
内容的提问来源于stack exchange,提问作者Luca Jeevanjee
相关产品推荐
相关产品推荐

