Dominant-Flow Visualizer Adjustments
Browse files
app.py
CHANGED
|
@@ -13,7 +13,7 @@ def extract_motion_vectors(data):
|
|
| 13 |
continue
|
| 14 |
diffs = np.diff(pts, axis=0)
|
| 15 |
for d in diffs:
|
| 16 |
-
if np.linalg.norm(d) > 1:
|
| 17 |
vectors.append(d)
|
| 18 |
return np.array(vectors)
|
| 19 |
|
|
@@ -34,7 +34,8 @@ def learn_flows(vectors, n_clusters=2):
|
|
| 34 |
# 🎨 3. Visualization utility
|
| 35 |
# ------------------------------------------------------------
|
| 36 |
def draw_flow_overlay(vectors, labels, centers, bg_img=None):
|
| 37 |
-
|
|
|
|
| 38 |
bg = cv2.imread(bg_img)
|
| 39 |
if bg is None:
|
| 40 |
bg = np.ones((600,900,3),dtype=np.uint8)*40
|
|
@@ -42,41 +43,60 @@ def draw_flow_overlay(vectors, labels, centers, bg_img=None):
|
|
| 42 |
bg = np.ones((600,900,3),dtype=np.uint8)*40
|
| 43 |
|
| 44 |
overlay = bg.copy()
|
| 45 |
-
colors = [(
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
cv2.arrowedLine(overlay, start, end, colors[lab%2], 1, tipLength=0.3)
|
| 51 |
|
| 52 |
-
#
|
|
|
|
|
|
|
| 53 |
for i, c in enumerate(centers):
|
| 54 |
-
|
| 55 |
-
center_pt
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
-
|
| 61 |
-
cv2.imwrite(
|
| 62 |
-
return
|
| 63 |
|
| 64 |
|
| 65 |
# ------------------------------------------------------------
|
| 66 |
# 🚀 4. Combined pipeline
|
| 67 |
# ------------------------------------------------------------
|
| 68 |
def process_json(json_file, background=None):
|
| 69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
vectors = extract_motion_vectors(data)
|
| 71 |
if len(vectors)==0:
|
| 72 |
return None, {"error":"No motion vectors found."}
|
|
|
|
| 73 |
labels, centers = learn_flows(vectors)
|
| 74 |
if labels is None:
|
| 75 |
return None, {"error":"Insufficient data for clustering."}
|
|
|
|
| 76 |
img_path = draw_flow_overlay(vectors, labels, centers, background)
|
| 77 |
stats = {
|
| 78 |
-
"num_vectors": len(vectors),
|
| 79 |
-
"dominant_flows": len(centers),
|
| 80 |
"flow_centers": centers.tolist()
|
| 81 |
}
|
| 82 |
return img_path, stats
|
|
@@ -88,7 +108,7 @@ def process_json(json_file, background=None):
|
|
| 88 |
description_text = """
|
| 89 |
### 🧭 Dominant Flow Learning (Stage 2)
|
| 90 |
Upload the **trajectories JSON** from Stage 1 (vehicle tracking).
|
| 91 |
-
Optionally upload a background road frame image
|
| 92 |
"""
|
| 93 |
|
| 94 |
example_json = "trajectories_sample.json" if os.path.exists("trajectories_sample.json") else None
|
|
|
|
| 13 |
continue
|
| 14 |
diffs = np.diff(pts, axis=0)
|
| 15 |
for d in diffs:
|
| 16 |
+
if np.linalg.norm(d) > 1: # ignore jitter / static points
|
| 17 |
vectors.append(d)
|
| 18 |
return np.array(vectors)
|
| 19 |
|
|
|
|
| 34 |
# 🎨 3. Visualization utility
|
| 35 |
# ------------------------------------------------------------
|
| 36 |
def draw_flow_overlay(vectors, labels, centers, bg_img=None):
|
| 37 |
+
# background setup
|
| 38 |
+
if bg_img and os.path.exists(bg_img):
|
| 39 |
bg = cv2.imread(bg_img)
|
| 40 |
if bg is None:
|
| 41 |
bg = np.ones((600,900,3),dtype=np.uint8)*40
|
|
|
|
| 43 |
bg = np.ones((600,900,3),dtype=np.uint8)*40
|
| 44 |
|
| 45 |
overlay = bg.copy()
|
| 46 |
+
colors = [(0,0,255),(255,255,0)] # red & yellow for lanes
|
| 47 |
|
| 48 |
+
# Normalize arrow lengths
|
| 49 |
+
norms = np.linalg.norm(vectors, axis=1, keepdims=True)
|
| 50 |
+
vectors = np.divide(vectors, norms + 1e-6) * 10
|
| 51 |
+
|
| 52 |
+
# Draw sampled small flow lines
|
| 53 |
+
for i, ((vx,vy), lab) in enumerate(zip(vectors, labels)):
|
| 54 |
+
if i % 15 != 0: # draw 1 of every 15 for clarity
|
| 55 |
+
continue
|
| 56 |
+
start = (np.random.randint(0, overlay.shape[1]),
|
| 57 |
+
np.random.randint(0, overlay.shape[0]))
|
| 58 |
+
end = (int(start[0]+vx), int(start[1]+vy))
|
| 59 |
cv2.arrowedLine(overlay, start, end, colors[lab%2], 1, tipLength=0.3)
|
| 60 |
|
| 61 |
+
# Draw two main flow centroids (green)
|
| 62 |
+
h,w = overlay.shape[:2]
|
| 63 |
+
center_pt = (w//2, h//2)
|
| 64 |
for i, c in enumerate(centers):
|
| 65 |
+
end = (int(center_pt[0]+c[0]*40), int(center_pt[1]+c[1]*40))
|
| 66 |
+
cv2.arrowedLine(overlay, center_pt, end, (0,255,0), 4, tipLength=0.4)
|
| 67 |
+
cv2.putText(overlay, f"Flow {i+1}", (end[0]+10, end[1]),
|
| 68 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
|
| 69 |
+
|
| 70 |
+
# Blend overlay with background (transparency)
|
| 71 |
+
alpha = 0.6
|
| 72 |
+
combined = cv2.addWeighted(bg, alpha, overlay, 1-alpha, 0)
|
| 73 |
|
| 74 |
+
out_path = tempfile.NamedTemporaryFile(suffix=".jpg", delete=False).name
|
| 75 |
+
cv2.imwrite(out_path, combined)
|
| 76 |
+
return out_path
|
| 77 |
|
| 78 |
|
| 79 |
# ------------------------------------------------------------
|
| 80 |
# 🚀 4. Combined pipeline
|
| 81 |
# ------------------------------------------------------------
|
| 82 |
def process_json(json_file, background=None):
|
| 83 |
+
try:
|
| 84 |
+
data = json.load(open(json_file))
|
| 85 |
+
except Exception as e:
|
| 86 |
+
return None, {"error": f"Invalid JSON file: {e}"}
|
| 87 |
+
|
| 88 |
vectors = extract_motion_vectors(data)
|
| 89 |
if len(vectors)==0:
|
| 90 |
return None, {"error":"No motion vectors found."}
|
| 91 |
+
|
| 92 |
labels, centers = learn_flows(vectors)
|
| 93 |
if labels is None:
|
| 94 |
return None, {"error":"Insufficient data for clustering."}
|
| 95 |
+
|
| 96 |
img_path = draw_flow_overlay(vectors, labels, centers, background)
|
| 97 |
stats = {
|
| 98 |
+
"num_vectors": int(len(vectors)),
|
| 99 |
+
"dominant_flows": int(len(centers)),
|
| 100 |
"flow_centers": centers.tolist()
|
| 101 |
}
|
| 102 |
return img_path, stats
|
|
|
|
| 108 |
description_text = """
|
| 109 |
### 🧭 Dominant Flow Learning (Stage 2)
|
| 110 |
Upload the **trajectories JSON** from Stage 1 (vehicle tracking).
|
| 111 |
+
Optionally upload a background road frame image for overlay visualization.
|
| 112 |
"""
|
| 113 |
|
| 114 |
example_json = "trajectories_sample.json" if os.path.exists("trajectories_sample.json") else None
|