Skip to content

Add show_points option to show_anns - #511

Merged
giswqs merged 1 commit into
mainfrom
show-points-in-show-anns
Mar 21, 2026
Merged

Add show_points option to show_anns#511
giswqs merged 1 commit into
mainfrom
show-points-in-show-anns

Conversation

@giswqs

@giswqs giswqs commented Mar 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Add show_points parameter to show_anns() that overlays point prompts as 5-pointed star markers on the annotation image, matching the style of show_points()
  • Store point_coords and point_labels in predict_inst() so show_anns() can automatically display them without re-specifying
  • Supports custom colors, sizes, and explicit point overrides via parameters

Usage

# Automatic - uses points from the last predict_inst() / generate_masks_by_points() call
sam.show_anns(show_points=True)

# Explicit points
sam.show_anns(show_points=True, point_coords=[[520, 375]], point_labels=[1])

Test plan

  • Verified show_anns(show_points=True) renders star markers after generate_masks_by_points()
  • Verified foreground (dark green) and background (red) point colors
  • Verified white edge outline matches show_points() style
  • Verified show_anns() without show_points is unchanged (backward compatible)
  • Pre-commit checks pass

…nnotations

- Add show_points parameter to show_anns() that renders point prompts as
  star markers on top of the annotation image
- Store point_coords and point_labels in predict_inst() so show_anns()
  can automatically use them
- Points are drawn as filled 5-pointed stars with white edge outlines,
  matching the style of the show_points() method
Copilot AI review requested due to automatic review settings March 21, 2026 02:39
@giswqs
giswqs merged commit 7eae64e into main Mar 21, 2026
11 checks passed
@giswqs
giswqs deleted the show-points-in-show-anns branch March 21, 2026 02:40
@github-actions

Copy link
Copy Markdown

@github-actions
github-actions Bot temporarily deployed to pull request March 21, 2026 02:43 Inactive

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends SamGeo3.show_anns() to optionally overlay point prompts (as star markers) on the rendered annotation image, using either explicitly provided points or the most recently used prompts stored from interactive prediction.

Changes:

  • Added show_points and related point-visualization parameters to show_anns() to draw star markers with configurable colors/sizing.
  • Persisted point_coords/point_labels in predict_inst() for reuse by show_anns(show_points=True).
  • Updated show_anns() docstring to describe the new point overlay behavior and parameters.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread samgeo/samgeo3.py
Comment on lines +2749 to +2752
if labels is None:
labels = [1] * len(coords_arr)
labels_arr = np.array(labels)

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When show_points=True, point_labels is converted to a numpy array and then zipped with coords_arr. If point_labels length doesn’t match point_coords, zip() will silently drop extra points; if point_labels is passed as a scalar (common elsewhere in this repo), iterating a 0-d numpy array will raise. Consider normalizing/broadcasting labels (e.g., scalar or length-1 -> repeat) and raising a clear ValueError when lengths still don’t match.

Suggested change
if labels is None:
labels = [1] * len(coords_arr)
labels_arr = np.array(labels)
n_points = len(coords_arr)
# Normalize/broadcast labels to match the number of points.
if labels is None:
# Default: all points are treated as foreground prompts.
labels_arr = np.ones(n_points, dtype=int)
else:
labels_arr = np.array(labels)
# Handle scalar labels (0-D array) by broadcasting.
if labels_arr.ndim == 0:
labels_arr = np.full(n_points, int(labels_arr), dtype=int)
else:
# Ensure 1-D and handle single-element sequences.
labels_arr = labels_arr.reshape(-1)
if labels_arr.size == 1 and n_points > 1:
labels_arr = np.full(n_points, int(labels_arr[0]), dtype=int)
elif labels_arr.size != n_points:
raise ValueError(
f"Number of point_labels ({labels_arr.size}) does not match "
f"number of point_coords ({n_points})."
)

Copilot uses AI. Check for mistakes.
Comment thread samgeo/samgeo3.py
Comment on lines +2753 to +2770
for pt, lbl in zip(coords_arr, labels_arr):
x, y = int(pt[0]), int(pt[1])
color = foreground_color if lbl == 1 else background_color

# Build a 5-pointed star polygon
def _star_pts(cx, cy, r_outer, r_inner, n=5):
pts = []
for i in range(2 * n):
r = r_outer if i % 2 == 0 else r_inner
angle = np.pi / 2 + i * np.pi / n
pts.append(
[
int(cx + r * np.cos(angle)),
int(cy - r * np.sin(angle)),
]
)
return np.array(pts, dtype=np.int32)

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_star_pts is defined inside the per-point loop, so it is re-created for every point rendered. Moving this helper outside the loop (or precomputing a template and translating it per point) would reduce overhead and keep the rendering logic simpler.

Suggested change
for pt, lbl in zip(coords_arr, labels_arr):
x, y = int(pt[0]), int(pt[1])
color = foreground_color if lbl == 1 else background_color
# Build a 5-pointed star polygon
def _star_pts(cx, cy, r_outer, r_inner, n=5):
pts = []
for i in range(2 * n):
r = r_outer if i % 2 == 0 else r_inner
angle = np.pi / 2 + i * np.pi / n
pts.append(
[
int(cx + r * np.cos(angle)),
int(cy - r * np.sin(angle)),
]
)
return np.array(pts, dtype=np.int32)
# Build a 5-pointed star polygon once per call, reuse for each point
def _star_pts(cx, cy, r_outer, r_inner, n=5):
pts = []
for i in range(2 * n):
r = r_outer if i % 2 == 0 else r_inner
angle = np.pi / 2 + i * np.pi / n
pts.append(
[
int(cx + r * np.cos(angle)),
int(cy - r * np.sin(angle)),
]
)
return np.array(pts, dtype=np.int32)
for pt, lbl in zip(coords_arr, labels_arr):
x, y = int(pt[0]), int(pt[1])
color = foreground_color if lbl == 1 else background_color

Copilot uses AI. Check for mistakes.
Comment thread samgeo/samgeo3.py
points (label=1). Defaults to dark green (0, 128, 0).
background_color (Tuple[int, int, int]): RGB color for background
points (label=0). Defaults to red (255, 0, 0).
point_size (int): Size of star markers in pixels. Defaults to 15.

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

point_size is documented as “Size of star markers in pixels”, but it’s used as the outer radius when computing the star vertices (the marker ends up roughly ~2*point_size across). Please clarify the docstring (e.g., outer radius vs diameter) or rename the parameter to avoid confusion for callers tuning marker size.

Suggested change
point_size (int): Size of star markers in pixels. Defaults to 15.
point_size (int): Outer radius of star markers in pixels (marker
diameter is roughly 2 * point_size). Defaults to 15.

Copilot uses AI. Check for mistakes.
Comment thread samgeo/samgeo3.py
Comment on lines +3291 to +3300
self.point_coords = (
point_coords.tolist()
if isinstance(point_coords, np.ndarray)
else point_coords
)
self.point_labels = (
point_labels.tolist()
if isinstance(point_labels, np.ndarray)
else point_labels
)

Copilot AI Mar 21, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

predict_inst() now stores self.point_coords/self.point_labels for later visualization, but it doesn’t normalize or validate point_labels relative to point_coords before storing. If a caller provides a scalar label or a mismatched length (both patterns supported in other modules), show_anns(show_points=True) can later fail or render incorrectly. Consider broadcasting/validating labels here before saving them (or saving a normalized Nx2 / length-N representation only).

Suggested change
self.point_coords = (
point_coords.tolist()
if isinstance(point_coords, np.ndarray)
else point_coords
)
self.point_labels = (
point_labels.tolist()
if isinstance(point_labels, np.ndarray)
else point_labels
)
# Normalize point coordinates to a Python list and infer number of points.
if isinstance(point_coords, np.ndarray):
coords_array = point_coords
coords_list = point_coords.tolist()
else:
coords_array = np.asarray(point_coords)
coords_list = point_coords
# Try to determine how many points we have.
n_points: int
if coords_array.ndim == 2:
# Typical case: (N, 2)
n_points = int(coords_array.shape[0])
elif coords_array.ndim == 1:
# Treat a flat 1D coordinate as a single point (e.g., [x, y]).
n_points = 1
else:
# Fallback: rely on Python's len().
try:
n_points = len(coords_list)
except TypeError:
n_points = 1
# Normalize point labels to match the number of points.
labels_norm = None
if point_labels is not None:
if isinstance(point_labels, np.ndarray):
labels_raw = point_labels.tolist()
else:
labels_raw = point_labels
# Handle scalar label by broadcasting to all points.
if isinstance(
labels_raw,
(int, float, bool, np.integer, np.floating),
):
labels_norm = [labels_raw] * max(n_points, 0)
else:
# Ensure we have a list for length checks/broadcasting.
labels_list = list(labels_raw)
if n_points <= 1:
# Single point: take the first label if available.
if labels_list:
labels_norm = [labels_list[0]]
else:
labels_norm = []
else:
if len(labels_list) == 1:
# Broadcast single label to all points.
labels_norm = labels_list * n_points
elif len(labels_list) == n_points:
labels_norm = labels_list
else:
raise ValueError(
f"Length of point_labels ({len(labels_list)}) does not match "
f"number of point_coords ({n_points})."
)
self.point_coords = coords_list
self.point_labels = labels_norm

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants