Sanshruth commited on
Commit
a8054b3
·
verified ·
1 Parent(s): 29901d7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +174 -131
app.py CHANGED
@@ -1,15 +1,9 @@
1
- # Maximize CPU usage and GPU utilization
2
  import multiprocessing
3
  import cv2
4
 
5
- # Get the number of CPU cores
6
- cpu_cores = multiprocessing.cpu_count()
7
-
8
- # Set OpenCV to use all available cores
9
- cv2.setNumThreads(cpu_cores)
10
-
11
- # Print the number of threads being used (optional)
12
- print(f"OpenCV using {cv2.getNumThreads()} threads out of {cpu_cores} available cores")
13
 
14
  ##############
15
  import torch
@@ -17,171 +11,220 @@ import gradio as gr
17
  import numpy as np
18
  from PIL import Image, ImageDraw
19
  from ultralytics import YOLO
20
- from ultralytics.utils.plotting import Annotator, colors
21
  import logging
22
- import math
23
 
24
- # Set up logging
25
  logging.basicConfig(level=logging.INFO)
26
  logger = logging.getLogger(__name__)
27
 
28
- # Global variables to store line coordinates and line equation
29
- start_point = None
30
- end_point = None
31
- line_params = None # Stores (slope, intercept) of the line
32
-
33
- # Initialize model once
34
- model = YOLO('yolov8n.pt') # Use smaller model if needed
35
- # Check for GPU availability
36
- device = 'cuda' if torch.cuda.is_available() else 'cpu'
37
- model.to(device)
38
- logger.info(f"Using device: {device}")
39
-
40
- # Video processing parameters
41
- FRAME_SKIP = 1 # Process every nth frame
42
- FRAME_SCALE = 0.5 # Scale factor for input frames
 
 
 
43
 
44
  def extract_first_frame(stream_url):
45
- """Extracts the first available frame from the IP camera stream."""
46
- logger.info("Extracting first frame...")
47
- cap = cv2.VideoCapture(stream_url)
48
- if not cap.isOpened():
49
- logger.error("Could not open stream.")
50
- return None, "Error: Could not open stream."
51
-
52
- ret, frame = cap.read()
53
- cap.release()
54
-
55
- if not ret:
56
- logger.error("Could not read frame.")
57
- return None, "Error: Could not read frame."
58
-
59
- frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
60
- return Image.fromarray(frame_rgb), "First frame extracted."
61
 
62
  def update_line(image, evt: gr.SelectData):
63
- """Updates the line based on user interaction."""
64
- global start_point, end_point, line_params
65
-
66
- if start_point is None:
67
- start_point = (evt.index[0], evt.index[1])
 
 
 
68
  draw = ImageDraw.Draw(image)
69
- draw.ellipse((start_point[0]-5, start_point[1]-5, start_point[0]+5, start_point[1]+5),
70
- fill="blue", outline="blue")
71
- return image, f"Line Start: {start_point}"
72
-
73
- end_point = (evt.index[0], evt.index[1])
74
- draw = ImageDraw.Draw(image)
75
- draw.line([start_point, end_point], fill="red", width=2)
76
- draw.ellipse((end_point[0]-5, end_point[1]-5, end_point[0]+5, end_point[1]+5),
77
- fill="green", outline="green")
78
-
79
- # Calculate line parameters
80
- if start_point[0] != end_point[0]:
81
- slope = (end_point[1] - start_point[1]) / (end_point[0] - start_point[0])
82
- intercept = start_point[1] - slope * start_point[0]
83
- line_params = (slope, intercept, start_point, end_point)
84
- else:
85
- line_params = (float('inf'), start_point[0], start_point, end_point)
86
-
87
- start_point = None
88
- return image, f"Line: {line_params[0]:.2f}x + {line_params[1]:.2f}"
 
89
 
90
- def optimized_intersection_check(box, line_params):
91
- """Optimized line-box intersection check using vector math."""
92
- _, _, (x1, y1), (x2, y2) = line_params
93
  box_x1, box_y1, box_x2, box_y2 = box
94
 
95
  # Convert line to parametric form
96
  dx = x2 - x1
97
  dy = y2 - y1
98
 
99
- # Check if any box edge intersects the line
100
- t_near = -float('inf')
101
- t_far = float('inf')
102
 
103
- for i in range(2):
104
- if dx == 0 and dy == 0:
105
- continue
 
 
 
 
 
 
106
 
107
- if i == 0: # X-axis
108
- t0 = (box_x1 - x1) / dx if dx != 0 else 0
109
- t1 = (box_x2 - x1) / dx if dx != 0 else 0
110
- else: # Y-axis
111
- t0 = (box_y1 - y1) / dy if dy != 0 else 0
112
- t1 = (box_y2 - y1) / dy if dy != 0 else 0
113
 
114
- t_min = min(t0, t1)
115
- t_max = max(t0, t1)
 
116
 
117
- if t_min > t_near: t_near = t_min
118
- if t_max < t_far: t_far = t_max
119
 
120
- return t_near <= t_far and t_near <= 1 and t_far >= 0
121
-
122
- def process_video(confidence_threshold=0.5, selected_classes=None, stream_url=None):
123
- """Optimized video processing pipeline."""
124
- global line_params
125
-
126
- # Validation checks
127
- if not line_params or not selected_classes or not stream_url:
128
- return None, "Missing configuration parameters"
129
 
130
- # Convert to set for faster lookups
131
- selected_classes = set(selected_classes)
 
 
 
 
 
 
 
132
 
133
- # Video capture setup
134
  cap = cv2.VideoCapture(stream_url)
135
  if not cap.isOpened():
136
- return None, "Error opening stream"
 
137
 
138
- crossed_objects = set()
139
- frame_count = 0
140
-
141
- while cap.isOpened():
 
 
142
  ret, frame = cap.read()
143
  if not ret:
144
  break
145
-
146
- frame_count += 1
147
- if frame_count % FRAME_SKIP != 0:
148
  continue
149
-
150
- # Preprocess frame
151
- frame = cv2.resize(frame, None, fx=FRAME_SCALE, fy=FRAME_SCALE)
152
 
153
- # Object detection
154
  results = model.track(
155
  frame,
156
  persist=True,
157
- conf=confidence_threshold,
 
158
  verbose=False,
159
- device=device,
160
- tracker="botsort.yaml" # Use optimized tracker config
161
  )
162
 
163
- # Process detections
164
  if results[0].boxes.id is not None:
165
  boxes = results[0].boxes.xyxy.cpu().numpy()
166
- track_ids = results[0].boxes.id.int().cpu().numpy()
167
- classes = results[0].boxes.cls.cpu().numpy()
 
168
 
169
- for box, track_id, cls in zip(boxes, track_ids, classes):
170
- if model.names[int(cls)] not in selected_classes:
171
- continue
172
-
173
- if optimized_intersection_check(box, line_params) and track_id not in crossed_objects:
174
- crossed_objects.add(track_id)
175
- if len(crossed_objects) > 1000:
176
- crossed_objects.clear()
177
-
178
  # Annotation
179
- annotated_frame = results[0].plot()
180
- cv2.line(annotated_frame, line_params[2], line_params[3], (0,255,0), 2)
181
- cv2.putText(annotated_frame, f"COUNT: {len(crossed_objects)}",
182
- (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
183
 
184
- yield annotated_frame, ""
185
 
186
  cap.release()
187
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Maximize performance settings
2
  import multiprocessing
3
  import cv2
4
 
5
+ # Configure OpenCV for multi-core processing
6
+ cv2.setNumThreads(multiprocessing.cpu_count())
 
 
 
 
 
 
7
 
8
  ##############
9
  import torch
 
11
  import numpy as np
12
  from PIL import Image, ImageDraw
13
  from ultralytics import YOLO
 
14
  import logging
15
+ import time
16
 
17
+ # Configure logging
18
  logging.basicConfig(level=logging.INFO)
19
  logger = logging.getLogger(__name__)
20
 
21
+ # Global variables for line coordinates
22
+ line_params = None
23
+ model = None
24
+
25
+ def initialize_yolov11():
26
+ """Initialize YOLOv11 model with error handling"""
27
+ global model
28
+ try:
29
+ model = YOLO('yolov11n.pt') # Make sure this model file exists
30
+ if torch.cuda.is_available():
31
+ model.to('cuda')
32
+ logger.info("YOLOv11 initialized with CUDA acceleration")
33
+ else:
34
+ logger.info("YOLOv11 initialized with CPU")
35
+ return True
36
+ except Exception as e:
37
+ logger.error(f"Model initialization failed: {str(e)}")
38
+ return False
39
 
40
  def extract_first_frame(stream_url):
41
+ """Robust frame extraction with retries"""
42
+ for _ in range(3): # Retry up to 3 times
43
+ cap = cv2.VideoCapture(stream_url)
44
+ if cap.isOpened():
45
+ ret, frame = cap.read()
46
+ cap.release()
47
+ if ret:
48
+ return cv2.cvtColor(frame, cv2.COLOR_BGR2RGB), "First frame extracted"
49
+ time.sleep(1) # Wait before retry
50
+ return None, "Error: Failed to capture initial frame"
 
 
 
 
 
 
51
 
52
  def update_line(image, evt: gr.SelectData):
53
+ """Optimized line drawing with validation"""
54
+ global line_params
55
+
56
+ if not hasattr(image, 'points'):
57
+ image.points = []
58
+
59
+ if len(image.points) < 2:
60
+ image.points.append((evt.index[0], evt.index[1]))
61
  draw = ImageDraw.Draw(image)
62
+ color = "blue" if len(image.points) == 1 else "green"
63
+ draw.ellipse([evt.index[0]-5, evt.index[1]-5, evt.index[0]+5, evt.index[1]+5],
64
+ fill=color, outline=color)
65
+
66
+ if len(image.points) == 2:
67
+ x1, y1 = image.points[0]
68
+ x2, y2 = image.points[1]
69
+ draw = ImageDraw.Draw(image)
70
+ draw.line([(x1,y1), (x2,y2)], fill="red", width=2)
71
+
72
+ # Store line parameters
73
+ if x2 - x1 != 0:
74
+ slope = (y2 - y1) / (x2 - x1)
75
+ intercept = y1 - slope * x1
76
+ else:
77
+ slope = float('inf')
78
+ intercept = x1
79
+ line_params = (slope, intercept, (x1,y1), (x2,y2))
80
+
81
+ status = f"Points: {len(image.points)}/2" if len(image.points) < 2 else "Line set!"
82
+ return image, status
83
 
84
+ def line_intersection(box, line):
85
+ """Fast line-box intersection using vector math"""
86
+ (m, b, (x1,y1), (x2,y2)) = line
87
  box_x1, box_y1, box_x2, box_y2 = box
88
 
89
  # Convert line to parametric form
90
  dx = x2 - x1
91
  dy = y2 - y1
92
 
93
+ # Check box edges
94
+ t0 = 0.0
95
+ t1 = 1.0
96
 
97
+ for edge in [0, 1]: # Check both x and y axes
98
+ if edge == 0: # X-axis boundaries
99
+ dir = dx
100
+ p = box_x1 - x1
101
+ q = box_x2 - x1
102
+ else: # Y-axis boundaries
103
+ dir = dy
104
+ p = box_y1 - y1
105
+ q = box_y2 - y1
106
 
107
+ if dir == 0:
108
+ if p > 0 or q < 0: return False
109
+ continue
 
 
 
110
 
111
+ t_near = p / dir
112
+ t_far = q / dir
113
+ if t_near > t_far: t_near, t_far = t_far, t_near
114
 
115
+ t0 = max(t0, t_near)
116
+ t1 = min(t1, t_far)
117
 
118
+ if t0 > t1: return False
119
+
120
+ return t0 <= 1 and t1 >= 0
 
 
 
 
 
 
121
 
122
+ def process_stream(conf_thresh, classes, stream_url):
123
+ """Optimized video processing pipeline"""
124
+ if not model:
125
+ yield None, "Model not initialized"
126
+ return
127
+
128
+ if not line_params:
129
+ yield None, "No detection line set"
130
+ return
131
 
 
132
  cap = cv2.VideoCapture(stream_url)
133
  if not cap.isOpened():
134
+ yield None, "Failed to open video stream"
135
+ return
136
 
137
+ tracker = {} # {track_id: last_seen}
138
+ crossed = set()
139
+ frame_skip = 2 # Process every 2nd frame
140
+ count = 0
141
+
142
+ while True:
143
  ret, frame = cap.read()
144
  if not ret:
145
  break
146
+
147
+ count += 1
148
+ if count % frame_skip != 0:
149
  continue
 
 
 
150
 
151
+ # Detection
152
  results = model.track(
153
  frame,
154
  persist=True,
155
+ conf=conf_thresh,
156
+ classes=classes,
157
  verbose=False,
158
+ device='cuda' if torch.cuda.is_available() else 'cpu'
 
159
  )
160
 
161
+ # Processing
162
  if results[0].boxes.id is not None:
163
  boxes = results[0].boxes.xyxy.cpu().numpy()
164
+ ids = results[0].boxes.id.int().cpu().numpy()
165
+ scores = results[0].boxes.conf.cpu().numpy()
166
+ labels = results[0].boxes.cls.cpu().numpy()
167
 
168
+ for box, track_id, score, label in zip(boxes, ids, scores, labels):
169
+ if line_intersection(box, line_params) and track_id not in crossed:
170
+ crossed.add(track_id)
171
+ if len(crossed) > 1000:
172
+ crossed.clear()
173
+
 
 
 
174
  # Annotation
175
+ annotated = results[0].plot()
176
+ cv2.line(annotated, line_params[2], line_params[3], (0,255,0), 2)
177
+ cv2.putText(annotated, f"Count: {len(crossed)}", (10,30),
178
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
179
 
180
+ yield cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB), ""
181
 
182
  cap.release()
183
 
184
+ # Gradio Interface
185
+ with gr.Blocks() as app:
186
+ gr.Markdown("# CCTV Smart Monitor - YOLOv11")
187
+
188
+ # Initialization
189
+ if not initialize_yolov11():
190
+ gr.Markdown("**Error**: Failed to initialize YOLOv11 model")
191
+
192
+ # Stream URL input
193
+ stream_url = gr.Textbox(
194
+ label="RTSP Stream URL",
195
+ value="rtsp://example.com/stream",
196
+ visible=True
197
+ )
198
+
199
+ # Frame setup
200
+ with gr.Row():
201
+ frame = gr.Image(label="Setup Frame", interactive=True)
202
+ line_status = gr.Textbox(label="Line Status", interactive=False)
203
+
204
+ # Controls
205
+ with gr.Row():
206
+ class_selector = gr.CheckboxGroup(
207
+ choices=model.names.values() if model else [],
208
+ label="Detection Classes"
209
+ )
210
+ confidence = gr.Slider(0.1, 1.0, value=0.4, label="Confidence Threshold")
211
+
212
+ # Output
213
+ output_video = gr.Image(label="Live Analysis", streaming=True)
214
+ error_box = gr.Textbox(label="System Messages", interactive=False)
215
+
216
+ # Interactions
217
+ frame.select(
218
+ update_line,
219
+ inputs=frame,
220
+ outputs=[frame, line_status]
221
+ )
222
+
223
+ gr.Button("Start Analysis").click(
224
+ process_stream,
225
+ inputs=[confidence, class_selector, stream_url],
226
+ outputs=[output_video, error_box]
227
+ )
228
+
229
+ if __name__ == "__main__":
230
+ app.launch(debug=True, enable_queue=True)