Sanshruth commited on
Commit
6e3fd3f
·
verified ·
1 Parent(s): ac55573

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +166 -111
app.py CHANGED
@@ -2,171 +2,226 @@
2
  import multiprocessing
3
  import cv2
4
 
 
 
 
5
  # Set OpenCV to use all available cores
6
- cv2.setNumThreads(multiprocessing.cpu_count())
 
 
 
7
 
8
  ##############
 
9
  import gradio as gr
10
  import numpy as np
11
  from PIL import Image, ImageDraw
12
  from ultralytics import YOLO
 
13
  import logging
 
 
14
 
15
  # Set up logging
16
  logging.basicConfig(level=logging.INFO)
17
  logger = logging.getLogger(__name__)
18
 
19
- # Global variables
20
- start_point = end_point = line_params = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
  def extract_first_frame(stream_url):
23
- """Extracts first frame from IP camera"""
 
24
  cap = cv2.VideoCapture(stream_url)
25
  if not cap.isOpened():
26
  return None, "Error: Could not open stream."
27
-
28
  ret, frame = cap.read()
29
  cap.release()
30
-
31
  if not ret:
32
  return None, "Error: Could not read frame."
33
-
34
- return Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)), "First frame extracted."
 
35
 
36
  def update_line(image, evt: gr.SelectData):
37
  """Handles line drawing interactions"""
38
  global start_point, end_point, line_params
39
-
40
- if not start_point:
41
  start_point = (evt.index[0], evt.index[1])
42
  draw = ImageDraw.Draw(image)
43
  draw.ellipse((start_point[0]-5, start_point[1]-5, start_point[0]+5, start_point[1]+5),
44
  fill="blue", outline="blue")
45
- return image, f"Start: {start_point}"
46
-
47
  end_point = (evt.index[0], evt.index[1])
 
 
48
  draw = ImageDraw.Draw(image)
49
  draw.line([start_point, end_point], fill="red", width=2)
50
  draw.ellipse((end_point[0]-5, end_point[1]-5, end_point[0]+5, end_point[1]+5),
51
  fill="green", outline="green")
52
-
53
- # Calculate line parameters
54
- if start_point[0] != end_point[0]:
55
- slope = (end_point[1] - start_point[1]) / (end_point[0] - start_point[0])
56
- intercept = start_point[1] - slope * start_point[0]
57
- line_params = (slope, intercept, start_point, end_point)
58
- else:
59
- line_params = (float('inf'), start_point[0], start_point, end_point)
60
-
61
  start_point = None
62
- return image, f"Line: {line_params[2]} to {line_params[3]}"
63
 
64
- def intersect(A, B, C, D):
65
- """Check line segment intersection"""
66
- def ccw(A, B, C):
67
- return (C[1]-A[1])*(B[0]-A[0]) > (B[1]-A[1])*(C[0]-A[0])
68
-
69
- return ccw(A,C,D) != ccw(B,C,D) and ccw(A,B,C) != ccw(A,B,D)
70
 
71
- def is_crossing(box, line_params):
72
- """Check if box crosses line"""
73
  if not line_params:
74
  return False
75
 
76
- (x1, y1), (x2, y2) = line_params[2], line_params[3]
77
- box_edges = [
78
- ((box[0], box[1]), (box[2], box[1])),
79
- ((box[2], box[1]), (box[2], box[3])),
80
- ((box[2], box[3]), (box[0], box[3])),
81
- ((box[0], box[3]), (box[0], box[1]))
82
- ]
83
-
84
- intersections = 0
85
- for edge in box_edges:
86
- if intersect((x1,y1), (x2,y2), edge[0], edge[1]):
87
- intersections += 1
88
- if intersections >= 2:
89
- return True
90
- return False
91
-
92
- def process_video(conf=0.5, classes=None, stream_url=None):
93
- """Main processing function"""
94
  global line_params
95
-
96
- # Initialize YOLOv11
97
- model = YOLO('yolo11n.pt')
98
-
 
 
 
 
 
 
 
 
 
 
99
  cap = cv2.VideoCapture(stream_url)
100
- crossed = set()
101
-
 
 
 
 
 
102
  while cap.isOpened():
103
  ret, frame = cap.read()
104
  if not ret:
105
  break
106
-
107
- # Run inference
108
- results = model.track(frame, persist=True, conf=conf, classes=classes)
109
-
110
- # Process results
 
 
 
 
 
 
111
  if results[0].boxes.id is not None:
112
- boxes = results[0].boxes.xyxy.cpu().numpy()
113
- ids = results[0].boxes.id.cpu().numpy().astype(int)
114
- clss = results[0].boxes.cls.cpu().numpy().astype(int)
115
-
116
- for box, tid, cls in zip(boxes, ids, clss):
117
- if is_crossing(box, line_params) and tid not in crossed:
118
- crossed.add(tid)
119
-
120
- # Draw overlays
121
- annotated = results[0].plot()
122
- if line_params:
123
- cv2.line(annotated, line_params[2], line_params[3], (0,255,0), 2)
124
- cv2.putText(annotated, f"Count: {len(crossed)}", (10,30),
125
- cv2.FONT_HERSHEY_SIMPLEX, 1, (0,255,0), 2)
126
 
127
- yield cv2.cvtColor(annotated, cv2.COLOR_BGR2RGB), ""
 
 
 
 
 
 
 
128
 
129
  cap.release()
130
 
131
- # Gradio Interface
132
- with gr.Blocks() as app:
133
- gr.Markdown("# CCTV Object Counter - YOLOv11")
134
-
135
- # Stream setup
136
- url = gr.Textbox(label="Stream URL", value="https://example.com/stream.m3u8")
137
- frame_btn = gr.Button("Get First Frame")
138
-
139
- # Image components
140
- img = gr.Image(label="Draw Detection Line", interactive=True)
141
- line_info = gr.Textbox(label="Line Coordinates")
142
-
143
- # Controls
144
- classes = gr.CheckboxGroup(label="Classes", choices=[
145
- "person", "car", "truck", "motorcycle"
146
- ], value=["person"])
147
- conf = gr.Slider(0.1, 1.0, value=0.4, label="Confidence Threshold")
148
-
149
- # Output
150
- video_out = gr.Image(label="Live View", streaming=True)
151
- status = gr.Textbox(label="Status")
152
-
153
- # Interactions
154
- frame_btn.click(
155
- extract_first_frame,
156
- inputs=url,
157
- outputs=[img, status]
158
- )
159
-
160
- img.select(
161
- update_line,
162
- inputs=img,
163
- outputs=[img, line_info]
164
  )
165
 
166
- gr.Button("Start Counting").click(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
167
  process_video,
168
- inputs=[conf, classes, url],
169
- outputs=[video_out, status]
170
  )
171
 
172
- app.launch()
 
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 cv2
16
  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
+ import torch
24
 
25
  # Set up logging
26
  logging.basicConfig(level=logging.INFO)
27
  logger = logging.getLogger(__name__)
28
 
29
+ # Global variables to store line coordinates and line equation
30
+ start_point = None
31
+ end_point = None
32
+ line_params = None # Stores (start_point, end_point)
33
+
34
+ # Load model once globally
35
+ model = YOLO("yolo11n.pt")
36
+ device = 'cuda' if torch.cuda.is_available() else 'cpu'
37
+ model = model.to(device)
38
+
39
+ def liang_barsky(line, bbox):
40
+ """Optimized line-rectangle intersection check using Liang-Barsky algorithm"""
41
+ x1, y1 = line[0]
42
+ x2, y2 = line[1]
43
+ xmin, ymin, xmax, ymax = bbox
44
+
45
+ dx = x2 - x1
46
+ dy = y2 - y1
47
+ p = [-dx, dx, -dy, dy]
48
+ q = [x1 - xmin, xmax - x1, y1 - ymin, ymax - y1]
49
+ u1 = 0.0
50
+ u2 = 1.0
51
+
52
+ for i in range(4):
53
+ if p[i] == 0:
54
+ if q[i] < 0:
55
+ return False
56
+ continue
57
+ t = q[i] / p[i]
58
+ if p[i] < 0:
59
+ if t > u1:
60
+ u1 = t
61
+ else:
62
+ if t < u2:
63
+ u2 = t
64
+
65
+ return u1 <= u2
66
 
67
  def extract_first_frame(stream_url):
68
+ """Extracts the first available frame from the IP camera stream"""
69
+ logger.info("Extracting first frame...")
70
  cap = cv2.VideoCapture(stream_url)
71
  if not cap.isOpened():
72
  return None, "Error: Could not open stream."
73
+
74
  ret, frame = cap.read()
75
  cap.release()
76
+
77
  if not ret:
78
  return None, "Error: Could not read frame."
79
+
80
+ frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
81
+ return Image.fromarray(frame_rgb), "First frame extracted successfully."
82
 
83
  def update_line(image, evt: gr.SelectData):
84
  """Handles line drawing interactions"""
85
  global start_point, end_point, line_params
86
+
87
+ if start_point is None:
88
  start_point = (evt.index[0], evt.index[1])
89
  draw = ImageDraw.Draw(image)
90
  draw.ellipse((start_point[0]-5, start_point[1]-5, start_point[0]+5, start_point[1]+5),
91
  fill="blue", outline="blue")
92
+ return image, f"Line Coordinates:\nStart: {start_point}, End: None"
93
+
94
  end_point = (evt.index[0], evt.index[1])
95
+ line_params = (start_point, end_point)
96
+
97
  draw = ImageDraw.Draw(image)
98
  draw.line([start_point, end_point], fill="red", width=2)
99
  draw.ellipse((end_point[0]-5, end_point[1]-5, end_point[0]+5, end_point[1]+5),
100
  fill="green", outline="green")
101
+
 
 
 
 
 
 
 
 
102
  start_point = None
103
+ return image, f"Line Coordinates:\nStart: {line_params[0]}, End: {line_params[1]}"
104
 
105
+ def reset_line():
106
+ """Resets line coordinates"""
107
+ global start_point, end_point, line_params
108
+ start_point = end_point = line_params = None
109
+ return None, "Line reset. Click to draw a new line."
 
110
 
111
+ def is_object_crossing_line(box, line_params):
112
+ """Optimized line crossing check using Liang-Barsky algorithm"""
113
  if not line_params:
114
  return False
115
 
116
+ line_start, line_end = line_params
117
+ x1, y1, x2, y2 = box
118
+ return liang_barsky((line_start, line_end), (x1, y1, x2, y2))
119
+
120
+ def draw_angled_line(image, line_params, color=(0, 255, 0), thickness=2):
121
+ """Draws the user-defined line on the frame"""
122
+ start, end = line_params
123
+ cv2.line(image, start, end, color, thickness)
124
+
125
+ def process_video(confidence_threshold=0.5, selected_classes=None, stream_url=None):
126
+ """Main video processing function with optimizations"""
 
 
 
 
 
 
 
127
  global line_params
128
+ errors = []
129
+
130
+ if not line_params:
131
+ errors.append("Error: No line drawn.")
132
+ if not selected_classes:
133
+ errors.append("Error: No classes selected.")
134
+ if not stream_url:
135
+ errors.append("Error: No stream URL provided.")
136
+ if errors:
137
+ return None, "\n".join(errors)
138
+
139
+ # Convert class names to indices once
140
+ selected_class_indices = {i for i, name in model.names.items() if name in selected_classes}
141
+
142
  cap = cv2.VideoCapture(stream_url)
143
+ cap.set(cv2.CAP_PROP_BUFFERSIZE, 1) # Reduce buffer size
144
+ if not cap.isOpened():
145
+ return None, "Error: Could not open stream."
146
+
147
+ crossed_objects = {}
148
+ max_tracked_objects = 1000
149
+
150
  while cap.isOpened():
151
  ret, frame = cap.read()
152
  if not ret:
153
  break
154
+
155
+ # Optimized inference
156
+ results = model.track(
157
+ frame,
158
+ persist=True,
159
+ conf=confidence_threshold,
160
+ half=True,
161
+ device=device,
162
+ verbose=False
163
+ )
164
+
165
  if results[0].boxes.id is not None:
166
+ boxes = results[0].boxes
167
+ track_ids = boxes.id.int().cpu().tolist()
168
+ clss = boxes.cls.cpu().tolist()
169
+
170
+ for box, cls, t_id in zip(boxes.xyxy.cpu(), clss, track_ids):
171
+ if cls in selected_class_indices and t_id not in crossed_objects:
172
+ if is_object_crossing_line(box.numpy(), line_params):
173
+ crossed_objects[t_id] = True
174
+ if len(crossed_objects) > max_tracked_objects:
175
+ crossed_objects.clear()
176
+
177
+ # Visualization
178
+ annotated_frame = results[0].plot()
179
+ draw_angled_line(annotated_frame, line_params)
180
 
181
+ # Draw count
182
+ count = len(crossed_objects)
183
+ (w, h), _ = cv2.getTextSize(f"COUNT: {count}", cv2.FONT_HERSHEY_SIMPLEX, 1, 2)
184
+ cv2.rectangle(annotated_frame, (10, 10), (20 + w, 40 + h), (0, 0, 0), -1)
185
+ cv2.putText(annotated_frame, f"COUNT: {count}", (20, 40),
186
+ cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
187
+
188
+ yield annotated_frame, ""
189
 
190
  cap.release()
191
 
192
+ # Gradio interface remains unchanged
193
+ with gr.Blocks() as demo:
194
+ gr.Markdown("<h1>Real-time monitoring, object tracking, and line-crossing detection for CCTV camera streams.</h1>")
195
+ gr.Markdown("## https://github.com/SanshruthR/CCTV_SENTRY_YOLO11")
196
+
197
+ stream_url = gr.Textbox(
198
+ label="IP Camera Stream URL",
199
+ value="https://s104.ipcamlive.com/streams/68idokwtondsqpmkr/stream.m3u8",
200
+ visible=False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  )
202
 
203
+ # First frame extraction
204
+ first_frame, status = extract_first_frame(stream_url.value)
205
+ image = gr.Image(value=first_frame, label="First Frame", type="pil") if first_frame else gr.Markdown(f"**Error:** {status}")
206
+ line_info = gr.Textbox(label="Line Coordinates", value="Line Coordinates:\nStart: None, End: None")
207
+ image.select(update_line, inputs=image, outputs=[image, line_info])
208
+
209
+ # Class selection
210
+ class_names = list(model.names.values())
211
+ selected_classes = gr.CheckboxGroup(choices=class_names, label="Select Classes to Detect")
212
+
213
+ # Confidence threshold
214
+ confidence_threshold = gr.Slider(0.0, 1.0, value=0.2, label="Confidence Threshold")
215
+
216
+ # Process button
217
+ process_button = gr.Button("Process Stream")
218
+ output_image = gr.Image(label="Processed Frame", streaming=True)
219
+ error_box = gr.Textbox(label="Errors/Warnings", interactive=False)
220
+
221
+ process_button.click(
222
  process_video,
223
+ inputs=[confidence_threshold, selected_classes, stream_url],
224
+ outputs=[output_image, error_box]
225
  )
226
 
227
+ demo.launch(debug=True)