File size: 3,572 Bytes
22a203c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6da2618
 
 
 
 
 
 
 
 
22a203c
 
 
 
 
 
 
 
6da2618
 
22a203c
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""N2 — fill_enclosed_islands must absorb accent-tile islands even when
segmentation noise bridges them to a large protected hole or sprinkles a few
protected pixels over them (the room-4 blue accent tile escaped both ways).

Cases:
  1. plain enclosed island                          -> filled   (regression)
  2. island bridged (1px chain) to big mat hole     -> filled   (N2 fix)
  3. island 30% covered by protect (chair leg)      -> kept     (guard)
  4. island with 2 stray protected pixels (~1%)     -> filled   (N2 fix)
  5. hole touching the image border                 -> kept     (guard)
  6. large hole (over max_area)                     -> kept     (guard)
"""

import cv2
import numpy as np

# --- extract the real implementation from app.py ---------------------------
src = open("app.py").read()
ns = {"np": np, "cv2": cv2}
start = src.index("def fill_enclosed_islands")
end = src.index("\ndef ", start + 10)
exec(compile(src[start:end], "app.py", "exec"), ns)
fill_enclosed_islands = ns["fill_enclosed_islands"]


def main():
    h, w = 400, 600
    mask = np.ones((h, w), np.uint8)
    protect = np.zeros((h, w), np.uint8)

    # 1. plain island, 20x20
    mask[50:70, 50:70] = 0

    # 2. island 16x16 + 1px bridge to a big protected mat hole (90x40)
    mask[200:240, 150:240] = 0          # mat hole
    protect[200:240, 150:240] = 1       # mat is occluder-protected
    mask[210:226, 260:276] = 0          # accent island nearby
    mask[217, 240:260] = 0              # 1px bridge connecting them

    # 3. island 20x20 with 30% protect coverage (a real object remnant)
    mask[300:320, 60:80] = 0
    protect[300:320, 60:66] = 1         # 6/20 columns = 30%

    # 4. island 20x20 with 2 stray protected pixels
    mask[80:100, 400:420] = 0
    protect[85, 405] = 1
    protect[92, 412] = 1

    # 5. border-touching hole
    mask[0:30, 500:540] = 0

    # 6. big hole, over max_area
    mask[280:360, 380:560] = 0          # 80x180 = 14400

    out = fill_enclosed_islands(mask, protect, max_area=2000)

    # 7. final unprotected sweep (N2b): a fully-protected small island — a
    # pattern feature the segmenter calls an object — must fill when the sweep
    # runs without class protection on the finished surface, while the big
    # protected mat hole still survives via max_area.
    mask7 = np.ones((400, 600), np.uint8)
    mask7[150:175, 300:330] = 0            # "blue accent tile" island
    mask7[200:240, 100:190] = 0            # mat-sized hole (3600 px)
    out7 = fill_enclosed_islands(mask7, None, max_area=1000)

    checks = [
        ("1 plain island filled", bool((out[52:68, 52:68] == 1).all())),
        ("2 bridged island filled", bool((out[212:224, 262:274] == 1).all())),
        ("2 mat hole kept", bool((out[205:235, 155:235] == 0).all())),
        ("3 protected island kept", bool((out[302:318, 62:78] == 0).all())),
        ("4 stray-pixel island filled", bool((out[82:98, 402:418] == 1).all())),
        ("5 border hole kept", bool((out[2:28, 502:538] == 0).all())),
        ("6 big hole kept", bool((out[285:355, 385:555] == 0).all())),
        ("7 final sweep fills pattern island", bool((out7[152:173, 302:328] == 1).all())),
        ("7 final sweep keeps big hole", bool((out7[205:235, 105:185] == 0).all())),
    ]
    ok = True
    for name, passed in checks:
        print(f"  [{'PASS' if passed else 'FAIL'}] {name}")
        ok &= passed

    print("\n" + ("ALL N2 CHECKS PASSED" if ok else "N2 CHECKS FAILED"))
    return 0 if ok else 1


if __name__ == "__main__":
    raise SystemExit(main())