Extract polygon targets from masks
Browse files
scripts/train_sampoly_polygon.py
CHANGED
|
@@ -12,6 +12,7 @@ from dataclasses import asdict, dataclass
|
|
| 12 |
from pathlib import Path
|
| 13 |
|
| 14 |
import torch
|
|
|
|
| 15 |
from PIL import Image, ImageDraw
|
| 16 |
from torch import Tensor, nn
|
| 17 |
import torch.nn.functional as F
|
|
@@ -140,6 +141,42 @@ def draw_targets(polygons: list[Tensor], size: int) -> tuple[Tensor, Tensor, Ten
|
|
| 140 |
return mask, boundary, vertex
|
| 141 |
|
| 142 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
class PolygonDataset(Dataset):
|
| 144 |
def __init__(self, root: str | Path, split: str, image_size: int, vertices_per_polygon: int) -> None:
|
| 145 |
self.root = Path(root)
|
|
@@ -187,8 +224,12 @@ class PolygonDataset(Dataset):
|
|
| 187 |
if not polygons and mask_path.exists():
|
| 188 |
mask = Image.open(mask_path).convert("L").resize((self.image_size, self.image_size), Image.NEAREST)
|
| 189 |
mask_tensor = (TF.to_tensor(mask) > 0.5).float()
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
else:
|
| 193 |
mask_tensor, boundary, vertex = draw_targets(polygons, self.image_size)
|
| 194 |
return {
|
|
|
|
| 12 |
from pathlib import Path
|
| 13 |
|
| 14 |
import torch
|
| 15 |
+
import numpy as np
|
| 16 |
from PIL import Image, ImageDraw
|
| 17 |
from torch import Tensor, nn
|
| 18 |
import torch.nn.functional as F
|
|
|
|
| 141 |
return mask, boundary, vertex
|
| 142 |
|
| 143 |
|
| 144 |
+
def polygons_from_binary_mask(mask: Image.Image, vertices_per_polygon: int) -> list[Tensor]:
|
| 145 |
+
mask_np = np.asarray(mask.convert("L"))
|
| 146 |
+
binary = (mask_np > 0).astype(np.uint8)
|
| 147 |
+
if binary.max() == 0:
|
| 148 |
+
return []
|
| 149 |
+
try:
|
| 150 |
+
import cv2
|
| 151 |
+
|
| 152 |
+
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
| 153 |
+
polygons = []
|
| 154 |
+
h, w = binary.shape
|
| 155 |
+
min_area = max(4.0, 0.0005 * h * w)
|
| 156 |
+
for contour in contours:
|
| 157 |
+
if cv2.contourArea(contour) < min_area:
|
| 158 |
+
continue
|
| 159 |
+
pts = [(float(p[0][0]) / max(w - 1, 1), float(p[0][1]) / max(h - 1, 1)) for p in contour]
|
| 160 |
+
sampled = resample_polygon(pts, vertices_per_polygon)
|
| 161 |
+
polygons.append(torch.tensor(sampled, dtype=torch.float32).clamp(0, 1))
|
| 162 |
+
return polygons
|
| 163 |
+
except Exception:
|
| 164 |
+
ys, xs = np.where(binary > 0)
|
| 165 |
+
if len(xs) == 0:
|
| 166 |
+
return []
|
| 167 |
+
h, w = binary.shape
|
| 168 |
+
x1, x2 = xs.min() / max(w - 1, 1), xs.max() / max(w - 1, 1)
|
| 169 |
+
y1, y2 = ys.min() / max(h - 1, 1), ys.max() / max(h - 1, 1)
|
| 170 |
+
sampled = resample_polygon([(x1, y1), (x2, y1), (x2, y2), (x1, y2)], vertices_per_polygon)
|
| 171 |
+
return [torch.tensor(sampled, dtype=torch.float32).clamp(0, 1)]
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def boundary_from_mask(mask_tensor: Tensor) -> Tensor:
|
| 175 |
+
pooled_max = F.max_pool2d(mask_tensor.unsqueeze(0), kernel_size=3, stride=1, padding=1)
|
| 176 |
+
pooled_min = -F.max_pool2d(-mask_tensor.unsqueeze(0), kernel_size=3, stride=1, padding=1)
|
| 177 |
+
return (pooled_max - pooled_min).squeeze(0).clamp(0, 1)
|
| 178 |
+
|
| 179 |
+
|
| 180 |
class PolygonDataset(Dataset):
|
| 181 |
def __init__(self, root: str | Path, split: str, image_size: int, vertices_per_polygon: int) -> None:
|
| 182 |
self.root = Path(root)
|
|
|
|
| 224 |
if not polygons and mask_path.exists():
|
| 225 |
mask = Image.open(mask_path).convert("L").resize((self.image_size, self.image_size), Image.NEAREST)
|
| 226 |
mask_tensor = (TF.to_tensor(mask) > 0.5).float()
|
| 227 |
+
polygons = polygons_from_binary_mask(mask, self.vertices_per_polygon)
|
| 228 |
+
if polygons:
|
| 229 |
+
_, boundary, vertex = draw_targets(polygons, self.image_size)
|
| 230 |
+
else:
|
| 231 |
+
boundary = boundary_from_mask(mask_tensor)
|
| 232 |
+
vertex = torch.zeros_like(mask_tensor)
|
| 233 |
else:
|
| 234 |
mask_tensor, boundary, vertex = draw_targets(polygons, self.image_size)
|
| 235 |
return {
|