LiangLabUMB commited on
Commit
3c037f1
·
verified ·
1 Parent(s): edfb9e4

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +5 -68
app.py CHANGED
@@ -1052,12 +1052,10 @@ def prepare_export(stored_masks, stored_image, threshold_bias):
1052
  # Tab builder
1053
  # ---------------------------------------------------------------------------
1054
 
1055
- def draw_polygon_overlay(image_pil, points, stem_px=0):
1056
  """
1057
  Draw numbered vertex dots and polygon edges onto a copy of image_pil.
1058
  points: list of (x, y) tuples in preview pixel space.
1059
- stem_px: if non-zero, draw a tail from the last vertex down to the touch
1060
- point it was offset from (see add_crop_point).
1061
  Returns a new PIL image.
1062
  """
1063
  img = image_pil.copy().convert("RGBA")
@@ -1080,15 +1078,6 @@ def draw_polygon_overlay(image_pil, points, stem_px=0):
1080
  fill=(74, 170, 255, 255), outline=(255, 255, 255, 255))
1081
  draw.text((x, y), str(i + 1), fill=(255, 255, 255, 255), anchor="mm")
1082
 
1083
- # In offset mode the marker sits above the fingertip. Without something
1084
- # connecting the two it reads as "the app put my point in the wrong place",
1085
- # so draw the stem down to where the touch actually landed.
1086
- if stem_px and points:
1087
- x, y = points[-1]
1088
- draw.line([(x, y + r), (x, y + stem_px)], fill=(74, 170, 255, 140), width=2)
1089
- draw.ellipse([x - 5, y + stem_px - 5, x + 5, y + stem_px + 5],
1090
- outline=(255, 255, 255, 170), width=2)
1091
-
1092
  combined = Image.alpha_composite(img, overlay)
1093
  return combined.convert("RGB")
1094
 
@@ -1111,7 +1100,6 @@ def make_preview(image_pil):
1111
  return preview, (preview.width / float(image_pil.width)) if image_pil.width else 1.0
1112
 
1113
 
1114
- OFFSET_PCT_DEFAULT = 0.0 # % of picker height; 0 = marker under the finger
1115
  ZOOM_FACTOR = 6 # zoom window is 1/6 of the image width
1116
 
1117
 
@@ -1146,47 +1134,6 @@ def make_zoom_view(full_img, cx_full, cy_full):
1146
  return view, {"x0": x0, "y0": y0, "zscale": zscale}
1147
 
1148
 
1149
- def add_crop_point(preview_pil, points, scale, offset_pct, evt: gr.SelectData):
1150
- """
1151
- Called by gr.Image .select(). Appends the clicked coordinate,
1152
- redraws the overlay on the PREVIEW, returns (updated_preview, updated_points).
1153
-
1154
- `points` are kept in ORIGINAL image pixels so run_segmentation's warp is
1155
- unaffected; only the drawing happens in preview space.
1156
- Ignores clicks once 4 points are set.
1157
- """
1158
- if points is None:
1159
- points = []
1160
- scale = scale or 1.0
1161
-
1162
- def to_preview(pts):
1163
- return [(int(px * scale), int(py * scale)) for px, py in pts]
1164
-
1165
- if preview_pil is None:
1166
- return preview_pil, points
1167
-
1168
- # Vertical offset so the marker is not hidden under the fingertip.
1169
- # Expressed as a share of picker height, which keeps it consistent across
1170
- # phone sizes instead of being tied to the image's pixel dimensions.
1171
- stem = int(preview_pil.height * (float(offset_pct or 0.0) / 100.0))
1172
-
1173
- if len(points) >= 4:
1174
- return draw_polygon_overlay(preview_pil, to_preview(points), stem), points
1175
-
1176
- # Gradio can deliver an empty selection (see gradio-app/gradio#5945);
1177
- # dropping it beats raising a red error box the user cannot interpret.
1178
- if evt is None or evt.index is None or evt.index[0] is None:
1179
- return draw_polygon_overlay(preview_pil, to_preview(points)), points
1180
-
1181
- tx, ty = int(evt.index[0]), int(evt.index[1])
1182
- # Lift the marker above the touch, clamped so a tap near the top edge
1183
- # cannot place a vertex off the image.
1184
- my = max(0, min(preview_pil.height - 1, ty - stem))
1185
- x, y = int(tx / scale), int(my / scale)
1186
- new_points = points + [(x, y)]
1187
- return draw_polygon_overlay(preview_pil, to_preview(new_points), stem), new_points
1188
-
1189
-
1190
  def clear_crop_points(image_pil):
1191
  """Reset polygon — return original image with no overlay and empty points."""
1192
  return image_pil, []
@@ -1397,13 +1344,6 @@ def build_tab(tab_index, masks_state, image_state, result_state):
1397
  cancel_zoom_btn = gr.Button("↩ Back to full view", size="sm",
1398
  visible=False)
1399
 
1400
- touch_offset = gr.Slider(
1401
- minimum=0, maximum=25, value=OFFSET_PCT_DEFAULT, step=1,
1402
- label="Fingertip offset (optional, % of picker height)",
1403
- info="Lifts the marker above your finger. Leave at 0 — "
1404
- "the zoom step already solves the occlusion problem.",
1405
- )
1406
-
1407
  model_dropdown = gr.Dropdown(
1408
  choices=list(MODEL_OPTIONS.keys()),
1409
  label="Select Model",
@@ -1594,7 +1534,7 @@ def build_tab(tab_index, masks_state, image_state, result_state):
1594
  pv = [(int(x * scale), int(y * scale)) for x, y in points]
1595
  return draw_polygon_overlay(preview, pv)
1596
 
1597
- def on_crop_click(full_img, preview, points, scale, zoom, offset_pct,
1598
  evt: gr.SelectData):
1599
  """Two stages per corner: rough tap -> zoomed view -> precise tap."""
1600
  points = list(points or [])
@@ -1615,18 +1555,15 @@ def build_tab(tab_index, masks_state, image_state, result_state):
1615
  return (_overview(preview, points, scale), points, None,
1616
  "*4 points set ✓ — **✕ Clear** to redo, or run segmentation*",
1617
  gr.update(visible=False))
1618
- stem = int(preview.height * (float(offset_pct or 0) / 100.0))
1619
- my = max(0, min(preview.height - 1, ty - stem))
1620
- view, z = make_zoom_view(full_img, tx / scale, my / scale)
1621
  return (view, points, z,
1622
  f"*Zoomed in — now tap corner {len(points) + 1} precisely "
1623
  f"(the red cross is your rough tap)*",
1624
  gr.update(visible=True))
1625
 
1626
  # ---- stage 2: precise tap inside the zoomed view -----------------
1627
- stem = int((PREVIEW_MAX * 0.75) * (float(offset_pct or 0) / 100.0))
1628
  x_full = zoom["x0"] + tx / zoom["zscale"]
1629
- y_full = zoom["y0"] + max(0, ty - stem) / zoom["zscale"]
1630
  W, H = full_img.size
1631
  new_points = points + [(int(min(max(0, x_full), W - 1)),
1632
  int(min(max(0, y_full), H - 1)))]
@@ -1639,7 +1576,7 @@ def build_tab(tab_index, masks_state, image_state, result_state):
1639
 
1640
  crop_display.select(fn=on_crop_click,
1641
  inputs=[img_input, base_image_state, crop_points_state,
1642
- preview_scale_state, zoom_state, touch_offset],
1643
  outputs=[crop_display, crop_points_state, zoom_state, crop_status,
1644
  cancel_zoom_btn])
1645
 
 
1052
  # Tab builder
1053
  # ---------------------------------------------------------------------------
1054
 
1055
+ def draw_polygon_overlay(image_pil, points):
1056
  """
1057
  Draw numbered vertex dots and polygon edges onto a copy of image_pil.
1058
  points: list of (x, y) tuples in preview pixel space.
 
 
1059
  Returns a new PIL image.
1060
  """
1061
  img = image_pil.copy().convert("RGBA")
 
1078
  fill=(74, 170, 255, 255), outline=(255, 255, 255, 255))
1079
  draw.text((x, y), str(i + 1), fill=(255, 255, 255, 255), anchor="mm")
1080
 
 
 
 
 
 
 
 
 
 
1081
  combined = Image.alpha_composite(img, overlay)
1082
  return combined.convert("RGB")
1083
 
 
1100
  return preview, (preview.width / float(image_pil.width)) if image_pil.width else 1.0
1101
 
1102
 
 
1103
  ZOOM_FACTOR = 6 # zoom window is 1/6 of the image width
1104
 
1105
 
 
1134
  return view, {"x0": x0, "y0": y0, "zscale": zscale}
1135
 
1136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1137
  def clear_crop_points(image_pil):
1138
  """Reset polygon — return original image with no overlay and empty points."""
1139
  return image_pil, []
 
1344
  cancel_zoom_btn = gr.Button("↩ Back to full view", size="sm",
1345
  visible=False)
1346
 
 
 
 
 
 
 
 
1347
  model_dropdown = gr.Dropdown(
1348
  choices=list(MODEL_OPTIONS.keys()),
1349
  label="Select Model",
 
1534
  pv = [(int(x * scale), int(y * scale)) for x, y in points]
1535
  return draw_polygon_overlay(preview, pv)
1536
 
1537
+ def on_crop_click(full_img, preview, points, scale, zoom,
1538
  evt: gr.SelectData):
1539
  """Two stages per corner: rough tap -> zoomed view -> precise tap."""
1540
  points = list(points or [])
 
1555
  return (_overview(preview, points, scale), points, None,
1556
  "*4 points set ✓ — **✕ Clear** to redo, or run segmentation*",
1557
  gr.update(visible=False))
1558
+ view, z = make_zoom_view(full_img, tx / scale, ty / scale)
 
 
1559
  return (view, points, z,
1560
  f"*Zoomed in — now tap corner {len(points) + 1} precisely "
1561
  f"(the red cross is your rough tap)*",
1562
  gr.update(visible=True))
1563
 
1564
  # ---- stage 2: precise tap inside the zoomed view -----------------
 
1565
  x_full = zoom["x0"] + tx / zoom["zscale"]
1566
+ y_full = zoom["y0"] + ty / zoom["zscale"]
1567
  W, H = full_img.size
1568
  new_points = points + [(int(min(max(0, x_full), W - 1)),
1569
  int(min(max(0, y_full), H - 1)))]
 
1576
 
1577
  crop_display.select(fn=on_crop_click,
1578
  inputs=[img_input, base_image_state, crop_points_state,
1579
+ preview_scale_state, zoom_state],
1580
  outputs=[crop_display, crop_points_state, zoom_state, crop_status,
1581
  cancel_zoom_btn])
1582