Cursor Agent
commited on
Commit
·
84628b3
1
Parent(s):
f6bb372
Enhance ball rings with neon glow effect (additive blending)
Browse files
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 =
|
| 798 |
-
BALL_RING_THICKNESS_PX =
|
| 799 |
-
BALL_RING_FEATHER_SIGMA =
|
| 800 |
-
BALL_RING_INTENSITY_GAMMA = 0
|
| 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 |
-
|
| 984 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 1236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|