"""Silhouette masks of the sheet's SIDE, TOP and FRONT views: flood fill from the view box's border, blocked by the dark ink (dilated to close small gaps); what the fill does not reach is the silhouette. Writes reference/mask_.png and reference/mask__over.png (mask tinted over the sheet) for checking by eye.""" import numpy as np from PIL import Image def dilate(m, it=1): for _ in range(it): n = m.copy(); n[1:] |= m[:-1]; n[:-1] |= m[1:]; n[:, 1:] |= m[:, :-1]; n[:, :-1] |= m[:, 1:]; m = n return m def erode(m, it=1): return ~dilate(~m, it) def reach(seed, free): """Grow seed through free pixels (4-connected) until stable.""" cur = seed & free while True: nxt = dilate(cur) & free if (nxt == cur).all(): return cur cur = nxt A = np.asarray(Image.open('reference/user_blueprint.png')).astype(int) INK = A.sum(2) < 235 # SIDE: the leaders of THRUSTER MODULE and MANIPULATOR ARM cut the aft underside outline (white over ink); redraw it # there from the 3x zoom (reference/z3_sideaft.png) so the fill does not leak in. from PIL import ImageDraw _patch = Image.new('L', (A.shape[1], A.shape[0]), 0) ImageDraw.Draw(_patch).line([(660, 217), (653, 247), (643, 280), (633, 313), (623, 330), (607, 340), (593, 343)], fill=255, width=3) INK |= np.asarray(_patch) > 0 BOXES = {'side': (175, 85, 712, 400), 'top': (150, 455, 680, 773), 'front': (795, 85, 1135, 400)} def mask(view, dil=1): x0, y0, x1, y1 = BOXES[view] ink = INK[y0:y1, x0:x1] if dil: ink = dilate(ink, dil) free = ~ink seed = np.zeros_like(free); seed[0] = seed[-1] = True; seed[:, 0] = seed[:, -1] = True outside = reach(seed, free) sil = ~outside sil = dilate(erode(sil, 2), 2) full = np.zeros(A.shape[:2], bool); full[y0:y1, x0:x1] = sil return full if __name__ == '__main__': for v in BOXES: m = mask(v, 2 if v == 'side' else 1) Image.fromarray((m * 255).astype(np.uint8)).save(f'reference/mask_{v}.png') o = A.copy(); o[m] = (o[m] * 0.5 + np.array([255, 120, 0]) * 0.5).astype(int) x0, y0, x1, y1 = BOXES[v] Image.fromarray(o[y0:y1, x0:x1].astype(np.uint8)).resize(((x1 - x0) * 2, (y1 - y0) * 2)).save(f'reference/mask_{v}_over.png') ys, xs = np.nonzero(m); print(v, m.sum(), xs.min(), xs.max(), ys.min(), ys.max())