Binta26 commited on
Commit
4b91004
·
verified ·
1 Parent(s): a26bcb9

Upload 3 files

Browse files
Files changed (3) hide show
  1. utils/ssd_detector.py +190 -0
  2. utils/yolo_tracker.py +182 -0
  3. utils/yolo_video.py +79 -0
utils/ssd_detector.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ SSD MobileNetV3 – Object detection via torchvision.
3
+ Detects traffic objects AND stop signs.
4
+ GPU forced when available.
5
+ No external model files needed – weights auto-downloaded.
6
+ """
7
+
8
+ import cv2 as cv
9
+ import torch
10
+ import torchvision
11
+ from torchvision.transforms import functional as F
12
+ import numpy as np
13
+ import os
14
+ import csv
15
+ from datetime import datetime
16
+
17
+ COCO_CLASSES = [
18
+ "__background__", "person", "bicycle", "car", "motorcycle", "airplane",
19
+ "bus", "train", "truck", "boat", "traffic light", "fire hydrant",
20
+ "N/A", "stop sign", "parking meter", "bench", "bird", "cat", "dog",
21
+ "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "N/A",
22
+ "backpack", "umbrella", "N/A", "N/A", "handbag", "tie", "suitcase",
23
+ "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat",
24
+ "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle",
25
+ "N/A", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana",
26
+ "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza",
27
+ "donut", "cake", "chair", "couch", "potted plant", "bed", "N/A",
28
+ "dining table", "N/A", "N/A", "toilet", "N/A", "tv", "laptop", "mouse",
29
+ "remote", "keyboard", "cell phone", "microwave", "oven", "toaster",
30
+ "sink", "refrigerator", "N/A", "book", "clock", "vase", "scissors",
31
+ "teddy bear", "hair drier", "toothbrush"
32
+ ]
33
+
34
+ # ✅ "motorbike" aligné avec yolo_tracker.py (cohérence du projet)
35
+ TRAFFIC_CLASSES = {
36
+ "person", "bicycle", "car", "motorbike", "motorcycle",
37
+ "bus", "truck", "stop sign", "traffic light"
38
+ }
39
+
40
+ np.random.seed(42)
41
+ COLORS = np.random.randint(0, 255, size=(len(COCO_CLASSES), 3), dtype="uint8")
42
+ STOP_COLOR = (0, 0, 255)
43
+
44
+
45
+ class SSDDetector:
46
+ """
47
+ SSDLite MobileNetV3 pre-trained on COCO.
48
+ - No training needed
49
+ - Detects 91 COCO classes including stop sign
50
+ - GPU accelerated
51
+ """
52
+ def __init__(self, filepath,
53
+ classes=None,
54
+ confidence_threshold=0.4,
55
+ device="cuda",
56
+ output_dir="logs"):
57
+
58
+ self.filepath = filepath
59
+ self.classes = classes if classes else TRAFFIC_CLASSES
60
+ self.conf_thr = confidence_threshold
61
+ self.output_dir = output_dir
62
+ os.makedirs(output_dir, exist_ok=True)
63
+ os.makedirs("results", exist_ok=True)
64
+
65
+ self.device = torch.device(device if torch.cuda.is_available() else "cpu")
66
+ print(f"Loading SSD MobileNetV3 on {self.device}...")
67
+
68
+ weights = torchvision.models.detection.SSDLite320_MobileNet_V3_Large_Weights.COCO_V1
69
+ self.model = torchvision.models.detection.ssdlite320_mobilenet_v3_large(weights=weights)
70
+ self.model.to(self.device)
71
+ self.model.eval()
72
+ print(f"Model ready on : {self.device}")
73
+
74
+ def forward(self, show=True, save_video=True):
75
+ cap = cv.VideoCapture(self.filepath)
76
+ assert cap.isOpened(), "Cannot open video"
77
+
78
+ w = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
79
+ h = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
80
+ fps = cap.get(cv.CAP_PROP_FPS) or 25
81
+
82
+ video_writer = None
83
+ if save_video:
84
+ video_writer = cv.VideoWriter(
85
+ "results/ssd_detection.avi",
86
+ cv.VideoWriter_fourcc(*"mp4v"),
87
+ fps, (w, h)
88
+ )
89
+
90
+ log_path = os.path.join(
91
+ self.output_dir,
92
+ f"ssd_log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
93
+ )
94
+
95
+ stats = {}
96
+ frame_idx = 0
97
+
98
+ print(f"Running SSD detection...")
99
+ print(f"Video : {w}x{h} @ {fps:.0f}fps")
100
+
101
+ with open(log_path, "w", newline="") as f:
102
+ writer = csv.writer(f)
103
+ # ✅ Ajout timestamp_s pour cohérence avec yolo_tracker logs
104
+ writer.writerow([
105
+ "timestamp_s", "frame", "class",
106
+ "x1", "y1", "x2", "y2", "confidence"
107
+ ])
108
+
109
+ while cap.isOpened():
110
+ success, frame = cap.read()
111
+ if not success:
112
+ break
113
+
114
+ timestamp = round(frame_idx / fps, 3)
115
+ img_rgb = cv.cvtColor(frame, cv.COLOR_BGR2RGB)
116
+ tensor = F.to_tensor(img_rgb).unsqueeze(0).to(self.device)
117
+
118
+ with torch.no_grad():
119
+ outputs = self.model(tensor)[0]
120
+
121
+ boxes = outputs["boxes"].cpu().numpy()
122
+ labels = outputs["labels"].cpu().numpy()
123
+ scores = outputs["scores"].cpu().numpy()
124
+
125
+ no_object = True
126
+
127
+ for box, label, score in zip(boxes, labels, scores):
128
+ if score < self.conf_thr:
129
+ continue
130
+
131
+ cls_name = COCO_CLASSES[label] if label < len(COCO_CLASSES) else "unknown"
132
+ # ✅ Normaliser "motorcycle" → "motorbike"
133
+ if cls_name == "motorcycle":
134
+ cls_name = "motorbike"
135
+ if cls_name not in self.classes:
136
+ continue
137
+
138
+ no_object = False
139
+ x1, y1, x2, y2 = map(int, box)
140
+
141
+ if cls_name == "stop sign":
142
+ color = STOP_COLOR
143
+ thickness = 3
144
+ cv.putText(frame, "STOP SIGN",
145
+ (x1, max(y1 - 20, 0)),
146
+ cv.FONT_HERSHEY_SIMPLEX, 0.9, color, 2)
147
+ else:
148
+ color = [int(c) for c in COLORS[label]]
149
+ thickness = 2
150
+
151
+ cv.rectangle(frame, (x1, y1), (x2, y2), color, thickness)
152
+ cv.putText(frame, f"{cls_name}: {score:.2f}",
153
+ (x1, max(y1 - 8, 0)),
154
+ cv.FONT_HERSHEY_SIMPLEX, 0.5, color, 2)
155
+
156
+ stats[cls_name] = stats.get(cls_name, 0) + 1
157
+ writer.writerow([
158
+ timestamp, frame_idx, cls_name,
159
+ x1, y1, x2, y2, round(float(score), 3)
160
+ ])
161
+
162
+ if no_object:
163
+ cv.putText(frame, "No selected object detected",
164
+ (20, 50), cv.FONT_HERSHEY_SIMPLEX,
165
+ 1.0, (0, 0, 255), 2)
166
+
167
+ if save_video and video_writer:
168
+ video_writer.write(frame)
169
+ if show:
170
+ cv.imshow("SSD Detection", frame)
171
+ if cv.waitKey(1) & 0xFF == ord("q"):
172
+ break
173
+
174
+ frame_idx += 1
175
+
176
+ cap.release()
177
+ if video_writer:
178
+ video_writer.release()
179
+ if show:
180
+ try:
181
+ cv.destroyAllWindows()
182
+ except cv.error:
183
+ pass
184
+
185
+ print(f"\n=== SSD Results ===")
186
+ print(f"Log CSV : {log_path}")
187
+ for cls, count in sorted(stats.items()):
188
+ print(f" {cls} : {count} detections")
189
+
190
+ return log_path, stats
utils/yolo_tracker.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ YOLOv11 + ByteTrack – Detection, tracking, and CSV logging.
3
+ Detects traffic objects AND stop signs.
4
+ GPU forced when available.
5
+ """
6
+
7
+ import cv2 as cv
8
+ from ultralytics import YOLO
9
+ import csv
10
+ import os
11
+ from datetime import datetime
12
+
13
+ TRAFFIC_CLASSES = [
14
+ 'car', 'truck', 'bus', 'motorbike', 'bicycle',
15
+ 'person', 'traffic sign', 'traffic light'
16
+ ]
17
+
18
+ CLASS_ALIASES = {
19
+ "motorcycle": "motorbike",
20
+ "trafficLight": "traffic light",
21
+ "traffic_light": "traffic light",
22
+ "pedestrian": "person",
23
+ }
24
+
25
+
26
+ def normalize_class_name(name: str) -> str:
27
+ return CLASS_ALIASES.get(name, name)
28
+
29
+
30
+ class Tracker:
31
+ """
32
+ YOLOv11 + ByteTrack tracker.
33
+ - Assigns unique IDs to each object
34
+ - Logs every detection to CSV
35
+ - Highlights stop signs with a special color (red)
36
+ - GPU accelerated
37
+ """
38
+ def __init__(self, filepath, classes=None, device="cuda",
39
+ output_dir="logs", conf=0.4, min_box_area=900, min_track_hits=3):
40
+
41
+ self.filepath = filepath
42
+ self.classes = classes if classes else TRAFFIC_CLASSES
43
+ self.device = device
44
+ self.output_dir = output_dir
45
+ self.conf = conf
46
+ self.min_box_area = min_box_area
47
+ self.min_track_hits = min_track_hits
48
+ os.makedirs(output_dir, exist_ok=True)
49
+
50
+ # ✅ Fallback : utilise best.pt si disponible, sinon yolo11n.pt
51
+ finetuned = "models/best.pt"
52
+ base = "models/yolo11n.pt"
53
+ self.model_path = finetuned if os.path.exists(finetuned) else base
54
+ print(f"[Tracker] Model : {self.model_path}")
55
+
56
+ def forward(self, show=True, save_video=True):
57
+ model = YOLO(self.model_path)
58
+ cap = cv.VideoCapture(self.filepath)
59
+ assert cap.isOpened(), "Cannot open video"
60
+
61
+ w = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
62
+ h = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
63
+ fps = cap.get(cv.CAP_PROP_FPS) or 25
64
+ total = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
65
+
66
+ video_writer = None
67
+ if save_video:
68
+ os.makedirs("results", exist_ok=True)
69
+ video_writer = cv.VideoWriter(
70
+ "results/yolo_tracked.avi",
71
+ cv.VideoWriter_fourcc(*"mp4v"), fps, (w, h)
72
+ )
73
+
74
+ log_path = os.path.join(
75
+ self.output_dir,
76
+ f"log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
77
+ )
78
+
79
+ unique_ids = {}
80
+ track_hits = {}
81
+ frame_idx = 0
82
+
83
+ print(f"Running YOLOv11 + ByteTrack on {self.device.upper()}...")
84
+ print(f"Video : {w}x{h} @ {fps:.0f}fps ({total} frames)")
85
+
86
+ with open(log_path, 'w', newline='') as f:
87
+ writer = csv.writer(f)
88
+ writer.writerow([
89
+ 'timestamp_s', 'frame', 'track_id',
90
+ 'class', 'x1', 'y1', 'x2', 'y2', 'confidence'
91
+ ])
92
+
93
+ while cap.isOpened():
94
+ success, frame = cap.read()
95
+ if not success:
96
+ break
97
+
98
+ timestamp = round(frame_idx / fps, 3)
99
+
100
+ results = model.track(
101
+ frame,
102
+ persist=True,
103
+ tracker="bytetrack.yaml",
104
+ conf=self.conf, # ✅ seuil de confiance
105
+ device=self.device,
106
+ verbose=False
107
+ )
108
+
109
+ no_object = True
110
+
111
+ if results[0].boxes is not None and results[0].boxes.id is not None:
112
+ for box in results[0].boxes:
113
+ track_id = int(box.id[0])
114
+ cls_name = normalize_class_name(model.names[int(box.cls[0])])
115
+ conf_val = round(float(box.conf[0]), 3)
116
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
117
+ box_area = max(0, (x2 - x1)) * max(0, (y2 - y1))
118
+
119
+ if cls_name not in self.classes:
120
+ continue
121
+ if conf_val < self.conf:
122
+ continue
123
+ if box_area < self.min_box_area:
124
+ continue
125
+
126
+ no_object = False
127
+ track_hits[track_id] = track_hits.get(track_id, 0) + 1
128
+ if track_hits[track_id] >= self.min_track_hits:
129
+ unique_ids.setdefault(track_id, cls_name)
130
+
131
+ writer.writerow([
132
+ timestamp, frame_idx, track_id,
133
+ cls_name, x1, y1, x2, y2, conf_val
134
+ ])
135
+
136
+ annotated = results[0].plot()
137
+
138
+ # Boîte rouge spéciale pour stop signs
139
+ if results[0].boxes is not None:
140
+ for box in results[0].boxes:
141
+ cls_name = normalize_class_name(model.names[int(box.cls[0])])
142
+ if cls_name == 'stop sign':
143
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
144
+ cv.rectangle(annotated, (x1, y1), (x2, y2), (0, 0, 255), 3)
145
+ cv.putText(annotated, "STOP SIGN",
146
+ (x1, y1 - 10),
147
+ cv.FONT_HERSHEY_SIMPLEX, 0.8,
148
+ (0, 0, 255), 2)
149
+
150
+ if no_object:
151
+ cv.putText(annotated, "No selected object detected",
152
+ (20, 50), cv.FONT_HERSHEY_SIMPLEX,
153
+ 1.0, (0, 0, 255), 2)
154
+
155
+ if save_video and video_writer:
156
+ video_writer.write(annotated)
157
+ if show:
158
+ cv.imshow("YOLOv11 Tracking", annotated)
159
+ if cv.waitKey(1) & 0xFF == ord('q'):
160
+ break
161
+
162
+ frame_idx += 1
163
+
164
+ cap.release()
165
+ if video_writer:
166
+ video_writer.release()
167
+ if show:
168
+ try:
169
+ cv.destroyAllWindows()
170
+ except cv.error:
171
+ pass
172
+
173
+ stats = {}
174
+ for cls in unique_ids.values():
175
+ stats[cls] = stats.get(cls, 0) + 1
176
+
177
+ print(f"\n=== Results ===")
178
+ print(f"Log CSV : {log_path}")
179
+ for cls, count in sorted(stats.items()):
180
+ print(f" {cls} : {count} unique objects")
181
+
182
+ return log_path, stats
utils/yolo_video.py ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ YOLOv11 – Simple detection (no tracking).
3
+ Counts objects per frame inside a region of interest.
4
+ GPU forced when available.
5
+ """
6
+
7
+ import cv2 as cv
8
+ from ultralytics import solutions
9
+ import os
10
+
11
+ DETECTION_CLASSES = [
12
+ 'car', 'truck', 'bus', 'motorbike', 'bicycle',
13
+ 'person', 'traffic sign', 'traffic light'
14
+ ]
15
+
16
+
17
+ class Detector:
18
+ def __init__(self, filepath, device="cuda"):
19
+ self.filepath = filepath
20
+ self.device = device
21
+ os.makedirs("results", exist_ok=True)
22
+
23
+
24
+ finetuned = "models/best.pt"
25
+ base = "models/yolo11n.pt"
26
+ self.model_path = finetuned if os.path.exists(finetuned) else base
27
+ print(f"[Detector] Model : {self.model_path}")
28
+
29
+ def forward(self, show=True):
30
+ cap = cv.VideoCapture(self.filepath)
31
+ assert cap.isOpened(), "Cannot open video/image"
32
+
33
+ w = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
34
+ h = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
35
+ fps = int(cap.get(cv.CAP_PROP_FPS)) or 25
36
+
37
+ # region_points dynamiques selon la résolution de la vidéo
38
+ margin_x = int(w * 0.02)
39
+ margin_y = int(h * 0.05)
40
+ top_y = int(h * 0.35)
41
+ bot_y = int(h * 0.80)
42
+ region_points = [
43
+ (margin_x, bot_y),
44
+ (w - margin_x, bot_y),
45
+ (w - margin_x, top_y),
46
+ (margin_x, top_y)
47
+ ]
48
+
49
+ video_writer = cv.VideoWriter(
50
+ "results/yolo_detection.avi",
51
+ cv.VideoWriter_fourcc(*"mp4v"),
52
+ fps, (w, h)
53
+ )
54
+
55
+ counter = solutions.ObjectCounter(
56
+ show=show,
57
+ region=region_points,
58
+ model=self.model_path,
59
+ device=self.device,
60
+ )
61
+
62
+ print(f"Running YOLOv11 detection on {self.device.upper()}...")
63
+ print(f"Video : {w}x{h} @ {fps}fps | Region : {region_points}")
64
+
65
+ while cap.isOpened():
66
+ success, frame = cap.read()
67
+ if not success:
68
+ break
69
+ results = counter(frame)
70
+ video_writer.write(results.plot_im)
71
+
72
+ cap.release()
73
+ video_writer.release()
74
+ if show:
75
+ try:
76
+ cv.destroyAllWindows()
77
+ except cv.error:
78
+ pass
79
+ print("Done → results/yolo_detection.avi")