#!/usr/bin/env python3 """ 按像素精确解析 PNG 平面图 —— 精确颜色分类 + 高分辨率网格化 颜色规则(依据实际采样校准): W 黑色(0,0,0) = 墙壁 O 灰色(176,176,176) = 办公室 P 蓝色(64,176,240) = 过道 R 绿色(80,176,96) = 休息厅 Y 橙色(240,112,16) = 共享工位区 / 楼道 C 青绿(80,224,192) = 玻璃顶 . 白色(240,240,240) = 空白/底 """ import zlib, struct, sys, collections def read_png(path): with open(path, 'rb') as f: data = f.read() assert data[:8] == b'\x89PNG\r\n\x1a\n', 'Not a PNG' pos = 8 width = height = None bit_depth = color_type = None idat = b'' while pos < len(data): ln = struct.unpack('>I', data[pos:pos+4])[0] typ = data[pos+4:pos+8] chunk = data[pos+8:pos+8+ln] pos += 12 + ln if typ == b'IHDR': width, height, bit_depth, color_type, _, _, _ = struct.unpack('>IIBBBBB', chunk) elif typ == b'IDAT': idat += chunk elif typ == b'IEND': break assert bit_depth == 8 and color_type in (2, 6) and width and height raw = zlib.decompress(idat) channels = 4 if color_type == 6 else 3 bpp = channels stride = width * bpp rows = [] off = 0 prev = bytearray(stride) for y in range(height): ft = raw[off]; off += 1 line = bytearray(raw[off:off+stride]); off += stride if ft == 1: for i in range(bpp, stride): line[i] = (line[i] + line[i-bpp]) & 0xFF elif ft == 2: for i in range(stride): line[i] = (line[i] + prev[i]) & 0xFF elif ft == 3: for i in range(stride): a = line[i-bpp] if i >= bpp else 0 line[i] = (line[i] + ((a + prev[i]) >> 1)) & 0xFF elif ft == 4: for i in range(stride): a = line[i-bpp] if i >= bpp else 0 b = prev[i] c = prev[i-bpp] if i >= bpp else 0 p = a + b - c pa, pb, pc = abs(p-a), abs(p-b), abs(p-c) pr = a if (pa <= pb and pa <= pc) else (b if pb <= pc else c) line[i] = (line[i] + pr) & 0xFF rows.append(bytes(line)) prev = line rgba = [] for line in rows: if channels == 4: rgba.append([(line[i], line[i+1], line[i+2], line[i+3]) for i in range(0, stride, 4)]) else: rgba.append([(line[i], line[i+1], line[i+2], 255) for i in range(0, stride, 3)]) return width, height, rgba # 精确色板(中心色 + 容差) PALETTE = [ ('W', (0, 0, 0), 70), # 黑 = 墙壁 ('O', (176, 176, 176), 40), # 灰 = 办公室 ('P', (64, 176, 240), 45), # 蓝 = 过道 ('R', (80, 176, 96), 40), # 绿 = 休息厅 ('Y', (240, 112, 16), 55), # 橙 = 共享工位/楼道 ('C', (80, 224, 192), 55), # 青绿 = 玻璃顶 ('.', (240, 240, 240), 40), # 白 = 空白 ] def classify(rgb): r, g, b = rgb[:3] best, best_d = '?', 1e9 for tag, (cr, cg, cb), tol in PALETTE: d = abs(r-cr) + abs(g-cg) + abs(b-cb) if d <= tol * 3 and d < best_d: best, best_d = tag, d return best def main(path, grid_cols=48, grid_rows=28, sub=3): w, h, rgba = read_png(path) print(f'PNG: {w}x{h} 网格: {grid_cols}x{grid_rows}') cw, ch = w / grid_cols, h / grid_rows grid = [] for gy in range(grid_rows): row = [] for gx in range(grid_cols): cx = int((gx + 0.5) * cw) cy = int((gy + 0.5) * ch) votes = collections.Counter() rad_x, rad_y = max(2, int(cw*0.3)), max(2, int(ch*0.3)) for dy in range(-rad_y, rad_y+1, sub): for dx in range(-rad_x, rad_x+1, sub): yy, xx = cy+dy, cx+dx if 0 <= yy < h and 0 <= xx < w: votes[classify(rgba[yy][xx])] += 1 row.append(votes.most_common(1)[0][0]) grid.append(row) # 打印 print(' ' + ''.join(str(c % 10) for c in range(grid_cols))) for gy, row in enumerate(grid): print(f'{gy:2d} ' + ''.join(row)) return grid if __name__ == '__main__': path = sys.argv[1] if len(sys.argv) > 1 else '详细的布局.png' main(path)