cuibinge commited on
Commit
e31b8e2
·
verified ·
1 Parent(s): a5813be

Extract polygon targets from masks

Browse files
Files changed (1) hide show
  1. scripts/train_sampoly_polygon.py +43 -2
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
- boundary = torch.zeros_like(mask_tensor)
191
- vertex = torch.zeros_like(mask_tensor)
 
 
 
 
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 {