Cursor Agent commited on
Commit
84628b3
·
1 Parent(s): f6bb372

Enhance ball rings with neon glow effect (additive blending)

Browse files
Files changed (1) hide show
  1. app.py +26 -8
app.py CHANGED
@@ -794,10 +794,10 @@ FX_GLOW_COLOR = np.array([1.0, 0.1, 0.6], dtype=np.float32)
794
  FX_EPS = 1e-6
795
  GHOST_TRAIL_COLOR = np.array([1.0, 0.0, 1.0], dtype=np.float32)
796
  GHOST_TRAIL_ALPHA = 0.55
797
- BALL_RING_ALPHA = 0.85
798
- BALL_RING_THICKNESS_PX = 4
799
- BALL_RING_FEATHER_SIGMA = 0.75
800
- BALL_RING_INTENSITY_GAMMA = 0.65
801
 
802
 
803
  def _maybe_upscale_for_display(image: Image.Image) -> Image.Image:
@@ -980,8 +980,14 @@ def compose_frame(state: AppState, frame_idx: int, remove_bg: bool = False) -> I
980
  ring_np = ring_np * np.clip(1.0 - current_union_mask, 0.0, 1.0)
981
  if ring_np.max() > FX_EPS:
982
  base_np = np.array(out_img).astype(np.float32) / 255.0
983
- ring_alpha = ring_np[..., None] * BALL_RING_ALPHA
984
- base_np = (1.0 - ring_alpha) * base_np + ring_alpha * GHOST_TRAIL_COLOR
 
 
 
 
 
 
985
  if focus_mask is not None:
986
  focus_alpha = np.clip(focus_mask, 0.0, 1.0)[..., None]
987
  orig_np = np.array(frame).astype(np.float32) / 255.0
@@ -1232,12 +1238,24 @@ def _build_ball_ring_mask(state: AppState, frame_idx: int) -> np.ndarray | None:
1232
  continue
1233
  center = (int(round(cx)), int(round(cy)))
1234
  radius_int = max(1, int(round(radius)))
1235
- thickness = max(1, int(round(BALL_RING_THICKNESS_PX)))
1236
- cv2.circle(ring_mask, center, radius_int, 1.0, thickness=thickness)
 
 
 
 
 
 
 
 
 
 
 
1237
 
1238
  if ring_mask is None or ring_mask.max() <= FX_EPS:
1239
  return None
1240
 
 
1241
  ring_mask = cv2.GaussianBlur(ring_mask, (0, 0), sigmaX=BALL_RING_FEATHER_SIGMA, sigmaY=BALL_RING_FEATHER_SIGMA)
1242
  max_val = float(ring_mask.max())
1243
  if max_val > FX_EPS:
 
794
  FX_EPS = 1e-6
795
  GHOST_TRAIL_COLOR = np.array([1.0, 0.0, 1.0], dtype=np.float32)
796
  GHOST_TRAIL_ALPHA = 0.55
797
+ BALL_RING_ALPHA = 1.5
798
+ BALL_RING_THICKNESS_PX = 3
799
+ BALL_RING_FEATHER_SIGMA = 1.5
800
+ BALL_RING_INTENSITY_GAMMA = 1.0
801
 
802
 
803
  def _maybe_upscale_for_display(image: Image.Image) -> Image.Image:
 
980
  ring_np = ring_np * np.clip(1.0 - current_union_mask, 0.0, 1.0)
981
  if ring_np.max() > FX_EPS:
982
  base_np = np.array(out_img).astype(np.float32) / 255.0
983
+ # Additive blending (Linear Dodge) for neon effect
984
+ # ring_np has the intensity profile (0.0 to 1.0)
985
+ glow_factor = ring_np[..., None] * BALL_RING_ALPHA
986
+ added_light = glow_factor * GHOST_TRAIL_COLOR
987
+
988
+ # Add light to background, clipping at white
989
+ base_np = np.clip(base_np + added_light, 0.0, 1.0)
990
+
991
  if focus_mask is not None:
992
  focus_alpha = np.clip(focus_mask, 0.0, 1.0)[..., None]
993
  orig_np = np.array(frame).astype(np.float32) / 255.0
 
1238
  continue
1239
  center = (int(round(cx)), int(round(cy)))
1240
  radius_int = max(1, int(round(radius)))
1241
+
1242
+ # Multi-pass drawing for neon effect: Glow -> Mid -> Core
1243
+ # 1. Outer Glow
1244
+ t_glow = max(1, int(round(BALL_RING_THICKNESS_PX * 4.0)))
1245
+ cv2.circle(ring_mask, center, radius_int, 0.3, thickness=t_glow)
1246
+
1247
+ # 2. Inner Glow
1248
+ t_mid = max(1, int(round(BALL_RING_THICKNESS_PX * 2.0)))
1249
+ cv2.circle(ring_mask, center, radius_int, 0.6, thickness=t_mid)
1250
+
1251
+ # 3. Core (Sharp)
1252
+ t_core = max(1, int(round(BALL_RING_THICKNESS_PX)))
1253
+ cv2.circle(ring_mask, center, radius_int, 1.0, thickness=t_core)
1254
 
1255
  if ring_mask is None or ring_mask.max() <= FX_EPS:
1256
  return None
1257
 
1258
+ # Soft blur to blend the stepped layers into a smooth gradient
1259
  ring_mask = cv2.GaussianBlur(ring_mask, (0, 0), sigmaX=BALL_RING_FEATHER_SIGMA, sigmaY=BALL_RING_FEATHER_SIGMA)
1260
  max_val = float(ring_mask.max())
1261
  if max_val > FX_EPS: