sparsh007 commited on
Commit
8082e63
·
verified ·
1 Parent(s): 71f299e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +188 -80
app.py CHANGED
@@ -1,123 +1,231 @@
1
  import gradio as gr
2
- from azure.storage.blob import BlobServiceClient
3
  import os
4
  import cv2
5
  import tempfile
6
  from ultralytics import YOLO
7
- import numpy as np
8
  from datetime import datetime
9
 
10
- # Azure Storage config
11
- SAS_TOKEN = "sv=2024-11-04&ss=bfqt&srt=sco&sp=rwdlacupiytfx&se=2025-04-30T04:25:22Z&st=2025-04-16T20:25:22Z&spr=https&sig=HYrJBoOYc4PRe%2BoqBMl%2FmoL5Kz4ZYugbTLuEh63sbeo%3D"
12
- ACCOUNT_NAME = "assentian"
13
- CONTAINER_URL = f"https://{ACCOUNT_NAME}.blob.core.windows.net/logs"
 
 
 
14
  CONTAINER_NAME = "logs"
15
- VIDEOS_FOLDER = ""
 
 
 
 
 
 
 
 
16
 
17
- # YOLO model
18
- YOLO_MODEL = YOLO("./best_yolov11.pt") # Ensure model is uploaded
 
 
 
 
19
 
20
- def list_videos():
21
  try:
22
- blob_service_client = BlobServiceClient(account_url=f"https://{ACCOUNT_NAME}.blob.core.windows.net", credential=SAS_TOKEN)
23
  container_client = blob_service_client.get_container_client(CONTAINER_NAME)
24
- blobs = container_client.list_blobs(name_starts_with=VIDEOS_FOLDER)
25
- videos = [blob.name for blob in blobs if blob.name.endswith(".mp4")]
26
- return videos if videos else ["No videos found"]
27
  except Exception as e:
28
- return [f"Error: {str(e)}"]
 
29
 
30
- def get_latest_video():
31
  try:
32
- blob_service_client = BlobServiceClient(account_url=f"https://{ACCOUNT_NAME}.blob.core.windows.net", credential=SAS_TOKEN)
33
  container_client = blob_service_client.get_container_client(CONTAINER_NAME)
34
- blobs = container_client.list_blobs(name_starts_with=VIDEOS_FOLDER)
 
35
  latest_blob = None
36
  latest_time = None
 
37
  for blob in blobs:
38
- if blob.name.endswith(".mp4"):
39
  blob_client = container_client.get_blob_client(blob.name)
40
- props = blob_client.get_blob_properties()
41
- if not latest_time or props.last_modified > latest_time:
42
- latest_time = props.last_modified
43
  latest_blob = blob.name
44
- return latest_blob if latest_blob else "No videos found"
 
45
  except Exception as e:
46
- return f"Error: {str(e)}"
 
47
 
48
- def annotate_video_with_bboxes(video_path):
49
  try:
50
- if not video_path or video_path == "No videos found":
51
- return "Error: No video selected"
 
 
 
52
 
53
- # Download video from Azure
54
- blob_service_client = BlobServiceClient(account_url=f"https://{ACCOUNT_NAME}.blob.core.windows.net", credential=SAS_TOKEN)
55
- blob_client = blob_service_client.get_blob_client(CONTAINER_NAME, video_path)
56
- video_data = blob_client.download_blob().readall()
57
 
58
- # Save to temp file
59
- temp_input = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
60
- temp_input.write(video_data)
61
- temp_input.close()
62
-
63
- # Process video with YOLO
64
- cap = cv2.VideoCapture(temp_input.name)
 
 
 
 
65
  if not cap.isOpened():
66
- os.remove(temp_input.name)
67
- return "Error: Could not open video"
68
-
 
 
 
69
  fps = cap.get(cv2.CAP_PROP_FPS)
70
- w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
71
- h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
72
- out_file = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
73
- annotated_video_path = out_file.name
74
- out_file.close()
75
- fourcc = cv2.VideoWriter_fourcc(*'mp4v')
76
- writer = cv2.VideoWriter(annotated_video_path, fourcc, fps, (w, h))
77
 
78
- while True:
 
 
 
 
 
 
 
79
  ret, frame = cap.read()
80
  if not ret:
81
  break
 
 
82
  results = YOLO_MODEL(frame)
83
- frame_counts = {}
84
- for r in results:
85
- boxes = r.boxes
86
- for box in boxes:
87
  cls_id = int(box.cls[0])
88
  conf = float(box.conf[0])
89
  if conf < 0.5:
90
  continue
91
- x1, y1, x2, y2 = box.xyxy[0]
 
 
92
  class_name = YOLO_MODEL.names[cls_id]
93
- x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
94
- color = (0, 255, 0)
 
95
  cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
96
- label_text = f"{class_name} {conf:.2f}"
97
- cv2.putText(frame, label_text, (x1, y1 - 6),
98
- cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 1)
99
- frame_counts[class_name] = frame_counts.get(class_name, 0) + 1
100
- summary_str = ", ".join(f"{cls_name}: {count}" for cls_name, count in frame_counts.items())
101
- cv2.putText(frame, summary_str, (15, 30),
102
- cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 0), 2)
 
 
 
 
 
 
 
103
  writer.write(frame)
104
-
 
105
  cap.release()
106
  writer.release()
107
- os.remove(temp_input.name)
108
- return annotated_video_path
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  except Exception as e:
110
- return f"Error: {str(e)}"
111
-
112
- # Gradio UI
113
- with gr.Blocks() as demo:
114
- gr.Markdown("## PRISM Site Diary V3 - Video Annotator")
115
- video_dropdown = gr.Dropdown(label="Select Video from Azure Blob", choices=list_videos(), interactive=True, allow_custom_value=True)
116
- auto_btn = gr.Button("Annotate Latest Video")
117
- manual_btn = gr.Button("Annotate Selected Video")
118
- output_video = gr.Video(label="Annotated Video")
119
- auto_btn.click(fn=annotate_video_with_bboxes, inputs=gr.State(get_latest_video()), outputs=output_video)
120
- manual_btn.click(fn=annotate_video_with_bboxes, inputs=video_dropdown, outputs=output_video)
121
- video_dropdown.change(fn=list_videos, outputs=video_dropdown)
122
-
123
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ from azure.storage.blob import BlobServiceClient, BlobClient
3
  import os
4
  import cv2
5
  import tempfile
6
  from ultralytics import YOLO
7
+ import logging
8
  from datetime import datetime
9
 
10
+ # Configure logging
11
+ logging.basicConfig(level=logging.INFO)
12
+ logger = logging.getLogger(__name__)
13
+
14
+ # Azure Storage Configuration
15
+ AZURE_ACCOUNT_NAME = "assentian"
16
+ AZURE_SAS_TOKEN = "sv=2024-11-04&ss=bfqt&srt=sco&sp=rwdlacupiytfx&se=2025-04-30T04:25:22Z&st=2025-04-16T20:25:22Z&spr=https&sig=HYrJBoOYc4PRe%2BoqBMl%2FmoL5Kz4ZYugbTLuEh63sbeo%3D"
17
  CONTAINER_NAME = "logs"
18
+ VIDEO_PREFIX = ""
19
+
20
+ # Initialize YOLO Model
21
+ try:
22
+ YOLO_MODEL = YOLO("./best_yolov11.pt")
23
+ logger.info("YOLO model loaded successfully")
24
+ except Exception as e:
25
+ logger.error(f"Failed to load YOLO model: {e}")
26
+ raise
27
 
28
+ # Azure Blob Service Client
29
+ def get_blob_service_client():
30
+ return BlobServiceClient(
31
+ account_url=f"https://{AZURE_ACCOUNT_NAME}.blob.core.windows.net",
32
+ credential=AZURE_SAS_TOKEN
33
+ )
34
 
35
+ def list_azure_videos():
36
  try:
37
+ blob_service_client = get_blob_service_client()
38
  container_client = blob_service_client.get_container_client(CONTAINER_NAME)
39
+ blobs = container_client.list_blobs(name_starts_with=VIDEO_PREFIX)
40
+ return [blob.name for blob in blobs if blob.name.lower().endswith(".mp4")]
 
41
  except Exception as e:
42
+ logger.error(f"Error listing videos: {e}")
43
+ return []
44
 
45
+ def get_latest_azure_video():
46
  try:
47
+ blob_service_client = get_blob_service_client()
48
  container_client = blob_service_client.get_container_client(CONTAINER_NAME)
49
+ blobs = container_client.list_blobs(name_starts_with=VIDEO_PREFIX)
50
+
51
  latest_blob = None
52
  latest_time = None
53
+
54
  for blob in blobs:
55
+ if blob.name.lower().endswith(".mp4"):
56
  blob_client = container_client.get_blob_client(blob.name)
57
+ properties = blob_client.get_blob_properties()
58
+ if not latest_time or properties.last_modified > latest_time:
59
+ latest_time = properties.last_modified
60
  latest_blob = blob.name
61
+
62
+ return latest_blob if latest_blob else None
63
  except Exception as e:
64
+ logger.error(f"Error finding latest video: {e}")
65
+ return None
66
 
67
+ def download_azure_video(blob_name):
68
  try:
69
+ blob_service_client = get_blob_service_client()
70
+ blob_client = blob_service_client.get_blob_client(
71
+ container=CONTAINER_NAME,
72
+ blob=blob_name
73
+ )
74
 
75
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_file:
76
+ download_stream = blob_client.download_blob()
77
+ temp_file.write(download_stream.readall())
78
+ return temp_file.name
79
 
80
+ except Exception as e:
81
+ logger.error(f"Download failed: {e}")
82
+ return None
83
+
84
+ def annotate_video(input_video_path):
85
+ try:
86
+ if not input_video_path or not os.path.exists(input_video_path):
87
+ logger.error("Invalid input video path")
88
+ return None
89
+
90
+ cap = cv2.VideoCapture(input_video_path)
91
  if not cap.isOpened():
92
+ logger.error("Failed to open video file")
93
+ return None
94
+
95
+ # Video writer setup
96
+ frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
97
+ frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
98
  fps = cap.get(cv2.CAP_PROP_FPS)
 
 
 
 
 
 
 
99
 
100
+ with tempfile.NamedTemporaryFile(suffix=".mp4", delete=False) as temp_output:
101
+ output_path = temp_output.name
102
+
103
+ fourcc = cv2.VideoWriter_fourcc(*'mp4v')
104
+ writer = cv2.VideoWriter(output_path, fourcc, fps, (frame_width, frame_height))
105
+
106
+ # Processing loop
107
+ while cap.isOpened():
108
  ret, frame = cap.read()
109
  if not ret:
110
  break
111
+
112
+ # YOLO inference
113
  results = YOLO_MODEL(frame)
114
+ class_counts = {}
115
+
116
+ for result in results:
117
+ for box in result.boxes:
118
  cls_id = int(box.cls[0])
119
  conf = float(box.conf[0])
120
  if conf < 0.5:
121
  continue
122
+
123
+ # Bounding box
124
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
125
  class_name = YOLO_MODEL.names[cls_id]
126
+ color = (0, 255, 0) # BGR format
127
+
128
+ # Draw rectangle
129
  cv2.rectangle(frame, (x1, y1), (x2, y2), color, 2)
130
+
131
+ # Text label
132
+ label = f"{class_name} {conf:.2f}"
133
+ cv2.putText(frame, label, (x1, y1 - 10),
134
+ cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2)
135
+
136
+ # Update counts
137
+ class_counts[class_name] = class_counts.get(class_name, 0) + 1
138
+
139
+ # Add summary overlay
140
+ summary_text = " | ".join([f"{k}: {v}" for k, v in class_counts.items()])
141
+ cv2.putText(frame, summary_text, (10, 30),
142
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 255), 2)
143
+
144
  writer.write(frame)
145
+
146
+ # Cleanup
147
  cap.release()
148
  writer.release()
149
+ os.remove(input_video_path)
150
+
151
+ return output_path
152
+
153
+ except Exception as e:
154
+ logger.error(f"Annotation failed: {e}")
155
+ if 'cap' in locals(): cap.release()
156
+ if 'writer' in locals(): writer.release()
157
+ return None
158
+
159
+ def process_video(blob_name):
160
+ try:
161
+ local_path = download_azure_video(blob_name)
162
+ if not local_path:
163
+ return None
164
+ return annotate_video(local_path)
165
  except Exception as e:
166
+ logger.error(f"Processing failed: {e}")
167
+ return None
168
+
169
+ # Gradio Interface
170
+ with gr.Blocks(title="PRISM Video Annotator", theme=gr.themes.Soft()) as demo:
171
+ gr.Markdown("# 🎥 PRISM Site Diary - Video Analyzer")
172
+
173
+ with gr.Row():
174
+ with gr.Column(scale=1):
175
+ gr.Markdown("## Azure Storage Controls")
176
+ refresh_btn = gr.Button("🔄 Refresh Video List", variant="secondary")
177
+ video_dropdown = gr.Dropdown(
178
+ label="Available Videos",
179
+ choices=list_azure_videos(),
180
+ interactive=True
181
+ )
182
+ latest_btn = gr.Button("⏩ Process Latest Video", variant="primary")
183
+ selected_btn = gr.Button("✅ Process Selected Video", variant="primary")
184
+
185
+ with gr.Column(scale=2):
186
+ gr.Markdown("## Annotated Output")
187
+ output_video = gr.Video(
188
+ label="Processed Video",
189
+ format="mp4",
190
+ interactive=False
191
+ )
192
+ status = gr.Textbox(label="Processing Status")
193
+
194
+ def update_ui():
195
+ new_choices = list_azure_videos()
196
+ return gr.Dropdown.update(choices=new_choices)
197
+
198
+ def handle_latest():
199
+ latest = get_latest_azure_video()
200
+ if latest:
201
+ output = process_video(latest)
202
+ return output if output else None
203
+ return None
204
+
205
+ # Event handlers
206
+ refresh_btn.click(
207
+ fn=update_ui,
208
+ outputs=video_dropdown,
209
+ queue=False
210
+ )
211
+
212
+ latest_btn.click(
213
+ fn=handle_latest,
214
+ outputs=output_video,
215
+ api_name="process_latest"
216
+ )
217
+
218
+ selected_btn.click(
219
+ fn=lambda x: process_video(x),
220
+ inputs=video_dropdown,
221
+ outputs=output_video,
222
+ api_name="process_selected"
223
+ )
224
+
225
+ if __name__ == "__main__":
226
+ demo.launch(
227
+ server_name="0.0.0.0",
228
+ server_port=7860,
229
+ show_error=True,
230
+ share=False
231
+ )