正在解析 python 中的 ASCII 平面图图像?
Parsing ASCII floor plan image in python?
我正在尝试在 ASCII 平面图中识别房间和家具的数量(S、C、W、P)。一个典型的平面图看起来像这样,有不同的房间和布局。解决这个问题的最佳方法是什么?
+---------------+-------------------+ +----------+
| | | | |
| (office) | C | | C |
| | | | |
| W | +-----------+ |
| | | | |
| S | (bathroom) S| S | |
| +---+--------+----------+ | |
| /P S| | |
| / | | |
| / (kitchen) | (bedroom) | P |
+-------+ | | |
| \ | | |
| \ SSWP | W W | |
| +-------------+----------------------+ |
| |
| (hallway) |
| W |
+--------------+-------------+-------------+ |
| | \ |
| | \ C |
| P | \ |
| | \ |
+------+ P | +----------+
|S |
| (balcony) C |
+--------------------+
我的方法是:
- 将计划视为网格
- 将每一行拆分为“space”的段 (non-walls)
- 加入 space 共享至少一列的
- 寻找一个名字,如果找到,将关节 space 保存为 roon
- 数一数房间里的家具
这导致以下代码(为了便于阅读,我定义了几个 类):
from dataclasses import dataclass, field
from re import finditer, search
from typing import Dict, List
@dataclass
class RowSegment():
row_no : int
col_start : int
col_end : int
def __repr__(self):
return f'{self.row_no}:{self.col_start}-{self.col_end}'
@dataclass
class Space():
name : str = 'noname'
furn_count : Dict[str, int] = field(default_factory = dict)
segs : List[RowSegment] = field(default_factory = list)
complete : bool = False
def __repr__(self):
return f'{self.name}: {[i for i in self.furn_count.items()]}\n{[s for s in self.segs]}'
plan = """
+---------------+-------------------+ +----------+
| | | | |
| (office) | C | | C |
| | | | |
| W | +-----------+ |
| | | | |
| S | (bathroom) S| S | |
| +---+--------+----------+ | |
| /P S| | |
| / | | |
| / (kitchen) | (bedroom) | P |
+-------+ | | |
| \ | | |
| \ SSWP | W W | |
| +-------------+----------------------+ |
| |
| (hallway) |
| W |
+--------------+-------------+-------------+ |
| | \ |
| | \ C |
| P | \ |
| | \ |
+------+ P | +----------+
|S |
| (balcony) C |
+--------------------+ """
furn_types = 'CPSW'
workspaces = []
rooms = []
rows = plan.split('\n')
rows.pop(0)
for no, row in enumerate(rows):
found = list(finditer(r'[^/\|+-]+', row))
if found:
rsegs = [RowSegment(no, rs.start(), rs.end()) for rs in found]
for ws in workspaces:
for rs in rsegs:
if max(ws.segs[-1].col_start, rs.col_start) < min(ws.segs[-1].col_end, rs.col_end): #add rs to ws
ws.segs.append(rs)
rsegs.remove(rs)
break
else: #no rs added => complete
text = ''.join([rows[s.row_no][s.col_start:s.col_end] for s in ws.segs])
name = search(r'\(\w+\)', text)
if name:
ws.name = name[0][1:-1]
ws.furn_count = {f: text.count(f) for f in furn_types}
rooms.append(ws)
ws.complete = True
#reset ws list to only not complete
workspaces = [ws for ws in workspaces if ws.complete == False]
#create new wss with remaining rss
for rs in rsegs:
newws = Space()
newws.segs.append(rs)
workspaces.append(newws)
for r in rooms:
print(r)
输出(我也在打印组成每个房间的“spaces”)是:
bathroom: [('C', 1), ('P', 0), ('S', 1), ('W', 0)]
[1:17-36, 2:17-36, 3:17-36, 4:17-36, 5:17-36, 6:17-36]
office: [('C', 0), ('P', 0), ('S', 1), ('W', 1)]
[1:1-16, 2:1-16, 3:1-16, 4:1-16, 5:1-16, 6:1-16, 7:1-12, 8:1-11, 9:1-10, 10:1-9]
bedroom: [('C', 0), ('P', 0), ('S', 1), ('W', 2)]
[5:37-48, 6:37-48, 7:37-48, 8:26-48, 9:26-48, 10:26-48, 11:26-48, 12:26-48, 13:26-48]
kitchen: [('C', 0), ('P', 2), ('S', 3), ('W', 1)]
[8:12-25, 9:11-25, 10:10-25, 11:9-25, 12:10-25, 13:11-25]
hallway: [('C', 2), ('P', 1), ('S', 0), ('W', 1)]
[1:49-59, 2:49-59, 3:49-59, 4:49-59, 5:49-59, 6:49-59, 7:49-59, 8:49-59, 9:49-59, 10:49-59, 11:49-59, 12:49-59, 13:49-59, 14:49-59, 15:1-59, 16:1-59, 17:1-59, 18:44-59, 19:45-59, 20:46-59, 21:47-59, 22:48-59]
balcony: [('C', 1), ('P', 2), ('S', 1), ('W', 0)]
[19:16-29, 20:16-29, 21:16-29, 22:16-29, 23:16-29, 24:9-29, 25:9-29]
我建议如下:
将计划视为网格
用空格替换所有字母(和括号)
将每个带有 non-space 字符的单元格设置为墙
将第一个 non-wall 单元格设置为房间 1
- 遍历这个附近的“空气”单元并将它们设置为同一个房间并一次又一次地遍历相邻的单元直到找到墙壁
- 完成后,取一个剩余的单元格并将其设置为房间 X
如此反复,直到所有单元格都归于不同的房间
- 将原始计划(有字母)与您找到的给定房间进行比较
我正在尝试在 ASCII 平面图中识别房间和家具的数量(S、C、W、P)。一个典型的平面图看起来像这样,有不同的房间和布局。解决这个问题的最佳方法是什么?
+---------------+-------------------+ +----------+
| | | | |
| (office) | C | | C |
| | | | |
| W | +-----------+ |
| | | | |
| S | (bathroom) S| S | |
| +---+--------+----------+ | |
| /P S| | |
| / | | |
| / (kitchen) | (bedroom) | P |
+-------+ | | |
| \ | | |
| \ SSWP | W W | |
| +-------------+----------------------+ |
| |
| (hallway) |
| W |
+--------------+-------------+-------------+ |
| | \ |
| | \ C |
| P | \ |
| | \ |
+------+ P | +----------+
|S |
| (balcony) C |
+--------------------+
我的方法是:
- 将计划视为网格
- 将每一行拆分为“space”的段 (non-walls)
- 加入 space 共享至少一列的
- 寻找一个名字,如果找到,将关节 space 保存为 roon
- 数一数房间里的家具
这导致以下代码(为了便于阅读,我定义了几个 类):
from dataclasses import dataclass, field
from re import finditer, search
from typing import Dict, List
@dataclass
class RowSegment():
row_no : int
col_start : int
col_end : int
def __repr__(self):
return f'{self.row_no}:{self.col_start}-{self.col_end}'
@dataclass
class Space():
name : str = 'noname'
furn_count : Dict[str, int] = field(default_factory = dict)
segs : List[RowSegment] = field(default_factory = list)
complete : bool = False
def __repr__(self):
return f'{self.name}: {[i for i in self.furn_count.items()]}\n{[s for s in self.segs]}'
plan = """
+---------------+-------------------+ +----------+
| | | | |
| (office) | C | | C |
| | | | |
| W | +-----------+ |
| | | | |
| S | (bathroom) S| S | |
| +---+--------+----------+ | |
| /P S| | |
| / | | |
| / (kitchen) | (bedroom) | P |
+-------+ | | |
| \ | | |
| \ SSWP | W W | |
| +-------------+----------------------+ |
| |
| (hallway) |
| W |
+--------------+-------------+-------------+ |
| | \ |
| | \ C |
| P | \ |
| | \ |
+------+ P | +----------+
|S |
| (balcony) C |
+--------------------+ """
furn_types = 'CPSW'
workspaces = []
rooms = []
rows = plan.split('\n')
rows.pop(0)
for no, row in enumerate(rows):
found = list(finditer(r'[^/\|+-]+', row))
if found:
rsegs = [RowSegment(no, rs.start(), rs.end()) for rs in found]
for ws in workspaces:
for rs in rsegs:
if max(ws.segs[-1].col_start, rs.col_start) < min(ws.segs[-1].col_end, rs.col_end): #add rs to ws
ws.segs.append(rs)
rsegs.remove(rs)
break
else: #no rs added => complete
text = ''.join([rows[s.row_no][s.col_start:s.col_end] for s in ws.segs])
name = search(r'\(\w+\)', text)
if name:
ws.name = name[0][1:-1]
ws.furn_count = {f: text.count(f) for f in furn_types}
rooms.append(ws)
ws.complete = True
#reset ws list to only not complete
workspaces = [ws for ws in workspaces if ws.complete == False]
#create new wss with remaining rss
for rs in rsegs:
newws = Space()
newws.segs.append(rs)
workspaces.append(newws)
for r in rooms:
print(r)
输出(我也在打印组成每个房间的“spaces”)是:
bathroom: [('C', 1), ('P', 0), ('S', 1), ('W', 0)]
[1:17-36, 2:17-36, 3:17-36, 4:17-36, 5:17-36, 6:17-36]
office: [('C', 0), ('P', 0), ('S', 1), ('W', 1)]
[1:1-16, 2:1-16, 3:1-16, 4:1-16, 5:1-16, 6:1-16, 7:1-12, 8:1-11, 9:1-10, 10:1-9]
bedroom: [('C', 0), ('P', 0), ('S', 1), ('W', 2)]
[5:37-48, 6:37-48, 7:37-48, 8:26-48, 9:26-48, 10:26-48, 11:26-48, 12:26-48, 13:26-48]
kitchen: [('C', 0), ('P', 2), ('S', 3), ('W', 1)]
[8:12-25, 9:11-25, 10:10-25, 11:9-25, 12:10-25, 13:11-25]
hallway: [('C', 2), ('P', 1), ('S', 0), ('W', 1)]
[1:49-59, 2:49-59, 3:49-59, 4:49-59, 5:49-59, 6:49-59, 7:49-59, 8:49-59, 9:49-59, 10:49-59, 11:49-59, 12:49-59, 13:49-59, 14:49-59, 15:1-59, 16:1-59, 17:1-59, 18:44-59, 19:45-59, 20:46-59, 21:47-59, 22:48-59]
balcony: [('C', 1), ('P', 2), ('S', 1), ('W', 0)]
[19:16-29, 20:16-29, 21:16-29, 22:16-29, 23:16-29, 24:9-29, 25:9-29]
我建议如下:
将计划视为网格
用空格替换所有字母(和括号)
将每个带有 non-space 字符的单元格设置为墙
将第一个 non-wall 单元格设置为房间 1
- 遍历这个附近的“空气”单元并将它们设置为同一个房间并一次又一次地遍历相邻的单元直到找到墙壁
- 完成后,取一个剩余的单元格并将其设置为房间 X
如此反复,直到所有单元格都归于不同的房间
- 将原始计划(有字母)与您找到的给定房间进行比较