mlbench123 commited on
Commit
7412522
Β·
verified Β·
1 Parent(s): 9a51cf4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -38
app.py CHANGED
@@ -61,32 +61,6 @@ def _text_size(draw, text, font):
61
 
62
  # ─── Visual polygon helpers ───────────────────────────────────────────────────
63
 
64
- def _smooth_polygon(pts_xy, window=11):
65
- """
66
- Hanning-weighted circular sliding-window average on polygon vertices.
67
- pts_xy : numpy (N, 2) float β€” already in original image coordinates
68
- from results.masks.xy (no resize, no drift).
69
- Returns : int32 array (N, 1, 2) for cv2.polylines / fillPoly.
70
- VISUAL ONLY β€” backend mask uses raw polygon via _polygon_to_mask().
71
- """
72
- pts = pts_xy.astype(np.float32)
73
- n = len(pts)
74
- if n < 6:
75
- return pts.astype(np.int32).reshape(-1, 1, 2)
76
-
77
- window = min(window | 1, (n - 1) | 1)
78
- half = window // 2
79
- padded = np.vstack([pts[-half:], pts, pts[:half]])
80
- weights = np.hanning(window).astype(np.float32)
81
- weights /= weights.sum()
82
-
83
- smoothed = np.zeros_like(pts)
84
- for i in range(n):
85
- smoothed[i] = (padded[i : i + window] * weights[:, None]).sum(axis=0)
86
-
87
- return smoothed.astype(np.int32).reshape(-1, 1, 2)
88
-
89
-
90
  def _polygon_to_mask(pts_xy, h, w):
91
  """Rasterise raw polygon β†’ binary uint8 mask. BACKEND / measurements only."""
92
  mask = np.zeros((h, w), dtype=np.uint8)
@@ -95,6 +69,21 @@ def _polygon_to_mask(pts_xy, h, w):
95
  return mask
96
 
97
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  # ─────────────────────────────────────────────────────────────────────────────
99
  # STEP 1 β€” Segmentation + visual output
100
  #
@@ -126,10 +115,10 @@ def run_segmentation(img_np):
126
  counts[cls_name] += 1
127
 
128
  # Backend mask: raw polygon fill (measurements only)
129
- mask_np = _polygon_to_mask(poly_xy, h, w)
130
 
131
- # Visual polygon: Hanning-smoothed (display only)
132
- vis_poly = _smooth_polygon(poly_xy, window=11)
133
 
134
  # Bounding box from backend mask for zoom crop
135
  ys, xs = np.where(mask_np == 1)
@@ -139,25 +128,27 @@ def run_segmentation(img_np):
139
  all_x2 = max(all_x2, int(xs.max()))
140
  all_y2 = max(all_y2, int(ys.max()))
141
 
142
- cv2.fillPoly(overlay, [vis_poly], color)
 
143
 
144
  grain_boxes.append({
145
  "cls_id": cls_id,
146
  "cls_name": cls_name,
147
- "mask_np": mask_np, # backend only
148
- "vis_poly": vis_poly, # visual only
149
  })
150
 
151
  # Blend fill
152
  annotated = cv2.addWeighted(annotated, 0.72, overlay, 0.28, 0)
153
 
154
- # Anti-aliased contour lines
155
  for g in grain_boxes:
156
- cv2.polylines(
157
- annotated, [g["vis_poly"]],
158
- isClosed=True, color=CLASS_COLORS[g["cls_id"]], thickness=2,
159
- lineType=cv2.LINE_AA,
160
- )
 
161
 
162
  # Zoom
163
  if all_x2 > all_x1 and all_y2 > all_y1:
 
61
 
62
  # ─── Visual polygon helpers ───────────────────────────────────────────────────
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  def _polygon_to_mask(pts_xy, h, w):
65
  """Rasterise raw polygon β†’ binary uint8 mask. BACKEND / measurements only."""
66
  mask = np.zeros((h, w), dtype=np.uint8)
 
69
  return mask
70
 
71
 
72
+ def _rotated_box_from_mask(mask_np):
73
+ """
74
+ Compute tight rotated bounding box from backend mask.
75
+ Returns the 4 corner points as int32 array (4, 1, 2) β€” ready for
76
+ cv2.polylines / fillPoly.
77
+ """
78
+ pts_y, pts_x = np.where(mask_np == 1)
79
+ if len(pts_x) < 5:
80
+ return None, None
81
+ pts = np.column_stack([pts_x.astype(np.float32), pts_y.astype(np.float32)])
82
+ rect = cv2.minAreaRect(pts)
83
+ box = cv2.boxPoints(rect) # 4 corners (float)
84
+ return box.astype(np.int32).reshape(-1, 1, 2), rect
85
+
86
+
87
  # ─────────────────────────────────────────────────────────────────────────────
88
  # STEP 1 β€” Segmentation + visual output
89
  #
 
115
  counts[cls_name] += 1
116
 
117
  # Backend mask: raw polygon fill (measurements only)
118
+ mask_np = _polygon_to_mask(poly_xy, h, w)
119
 
120
+ # Visual: tight rotated bounding box from the backend mask
121
+ vis_box, rect = _rotated_box_from_mask(mask_np)
122
 
123
  # Bounding box from backend mask for zoom crop
124
  ys, xs = np.where(mask_np == 1)
 
128
  all_x2 = max(all_x2, int(xs.max()))
129
  all_y2 = max(all_y2, int(ys.max()))
130
 
131
+ if vis_box is not None:
132
+ cv2.fillPoly(overlay, [vis_box], color)
133
 
134
  grain_boxes.append({
135
  "cls_id": cls_id,
136
  "cls_name": cls_name,
137
+ "mask_np": mask_np, # backend only
138
+ "vis_box": vis_box, # visual rotated bbox
139
  })
140
 
141
  # Blend fill
142
  annotated = cv2.addWeighted(annotated, 0.72, overlay, 0.28, 0)
143
 
144
+ # Draw crisp rotated bbox outlines (no fill β€” just the border)
145
  for g in grain_boxes:
146
+ if g["vis_box"] is not None:
147
+ cv2.polylines(
148
+ annotated, [g["vis_box"]],
149
+ isClosed=True, color=CLASS_COLORS[g["cls_id"]], thickness=2,
150
+ lineType=cv2.LINE_AA,
151
+ )
152
 
153
  # Zoom
154
  if all_x2 > all_x1 and all_y2 > all_y1: