Views
No views yet

1import json
2from pathlib import Path
3
4import torch
5from PIL import Image, ImageDraw
6from torchvision.transforms import InterpolationMode
7from torchvision.transforms import functional as TF
8
9
10ROOT = Path(__file__).resolve().parent
11IMAGE_SIZE = 518
12DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
13
14
15def prepare_image(filename: str, obb: list[float] | None = None):
16 image = Image.open(ROOT / "images" / filename).convert("RGB")
17 mask = Image.new("L", image.size, 0 if obb else 255)
18
19 if obb:
20 points = [
21 (int(x), int(y))
22 for x, y in zip(obb[::2], obb[1::2])
23 ]
24 ImageDraw.Draw(mask).polygon(points, fill=255)
25
26 width, height = image.size
27 scale = min(IMAGE_SIZE / width, IMAGE_SIZE / height)
28 new_width = min(IMAGE_SIZE, round(width * scale))
29 new_height = min(IMAGE_SIZE, round(height * scale))
30 size = [new_height, new_width]
31
32 image = TF.resize(
33 image,
34 size,
35 interpolation=InterpolationMode.BILINEAR,
36 antialias=True,
37 )
38 mask = TF.resize(
39 mask,
40 size,
41 interpolation=InterpolationMode.NEAREST,
42 )
43
44 pad_x = IMAGE_SIZE - new_width
45 pad_y = IMAGE_SIZE - new_height
46 padding = [
47 pad_x // 2,
48 pad_y // 2,
49 pad_x - pad_x // 2,
50 pad_y - pad_y // 2,
51 ]
52 image = TF.pad(image, padding, fill=255)
53 mask = TF.pad(mask, padding, fill=0)
54
55 image = TF.to_tensor(image)
56 image = TF.normalize(
57 image,
58 mean=[0.485, 0.456, 0.406],
59 std=[0.229, 0.224, 0.225],
60 )
61 mask = TF.to_tensor(mask)
62
63 return image.unsqueeze(0).to(DEVICE), mask.unsqueeze(0).to(DEVICE)
64
65
66with (ROOT / "data.jsonl").open("r", encoding="utf-8") as file:
67 items = [json.loads(line) for line in file if line.strip()]
68
69model = torch.jit.load(ROOT / "dino_hatching.pt", map_location=DEVICE).eval()
70wall_types = list(dict.fromkeys(item["wall_type"] for item in items))
71walls = {name: prepare_image(name) for name in wall_types}
72
73print("\nScore matrix")
74print(" " * 6 + "".join(f"W{i}".rjust(8) for i in range(1, len(wall_types) + 1)))
75
76with torch.inference_mode():
77 for index, item in enumerate(items, start=1):
78 plan_image, plan_mask = prepare_image(
79 item["plan_image"],
80 item["plan_obb"],
81 )
82 scores = []
83
84 for wall_type in wall_types:
85 wall_image, wall_mask = walls[wall_type]
86 logit = model(wall_image, wall_mask, plan_image, plan_mask)
87 scores.append(f"{torch.sigmoid(logit).item():.4f}")
88
89 print(f"P{index:<5}" + "".join(f"{score:>8}" for score in scores))
90
91print("\nRows:")
92for index, item in enumerate(items, start=1):
93 print(f" P{index}: {item['plan_image']}")
94
95print("Columns:")
96for index, wall_type in enumerate(wall_types, start=1):
97 print(f" W{index}: {wall_type}")
98
99# Score matrix
100# W1 W2 W3 W4 W5
101# P1 0.9965 0.0005 0.0006 0.0044 0.0317
102# P2 0.0026 0.9932 0.9916 0.0010 0.0028
103# P3 0.0001 0.9984 0.9988 0.0001 0.0036
104# P4 0.0005 0.0001 0.0002 0.9979 0.0002
105# P5 0.0103 0.0004 0.0006 0.0001 0.9971
106
107# Rows:
108# P1: valid_pair_000001_plan.png
109# P2: valid_pair_000004_plan.png
110# P3: valid_pair_000010_plan.png
111# P4: valid_pair_000013_plan.png
112# P5: valid_pair_000016_plan.png
113# Columns:
114# W1: valid_pair_000001_legend.png
115# W2: valid_pair_000004_legend.png
116# W3: valid_pair_000010_legend.png
117# W4: valid_pair_000013_legend.png
118# W5: valid_pair_000016_legend.pngscore is the probability that both hatchings belong to the same wall type.