computer_vision / utils /yolo_tracker.py
Binta26's picture
Upload 3 files
4b91004 verified
Raw
History Blame
6.36 kB
"""
YOLOv11 + ByteTrack – Detection, tracking, and CSV logging.
Detects traffic objects AND stop signs.
GPU forced when available.
"""
import cv2 as cv
from ultralytics import YOLO
import csv
import os
from datetime import datetime
TRAFFIC_CLASSES = [
'car', 'truck', 'bus', 'motorbike', 'bicycle',
'person', 'traffic sign', 'traffic light'
]
CLASS_ALIASES = {
"motorcycle": "motorbike",
"trafficLight": "traffic light",
"traffic_light": "traffic light",
"pedestrian": "person",
}
def normalize_class_name(name: str) -> str:
return CLASS_ALIASES.get(name, name)
class Tracker:
"""
YOLOv11 + ByteTrack tracker.
- Assigns unique IDs to each object
- Logs every detection to CSV
- Highlights stop signs with a special color (red)
- GPU accelerated
"""
def __init__(self, filepath, classes=None, device="cuda",
output_dir="logs", conf=0.4, min_box_area=900, min_track_hits=3):
self.filepath = filepath
self.classes = classes if classes else TRAFFIC_CLASSES
self.device = device
self.output_dir = output_dir
self.conf = conf
self.min_box_area = min_box_area
self.min_track_hits = min_track_hits
os.makedirs(output_dir, exist_ok=True)
# ✅ Fallback : utilise best.pt si disponible, sinon yolo11n.pt
finetuned = "models/best.pt"
base = "models/yolo11n.pt"
self.model_path = finetuned if os.path.exists(finetuned) else base
print(f"[Tracker] Model : {self.model_path}")
def forward(self, show=True, save_video=True):
model = YOLO(self.model_path)
cap = cv.VideoCapture(self.filepath)
assert cap.isOpened(), "Cannot open video"
w = int(cap.get(cv.CAP_PROP_FRAME_WIDTH))
h = int(cap.get(cv.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv.CAP_PROP_FPS) or 25
total = int(cap.get(cv.CAP_PROP_FRAME_COUNT))
video_writer = None
if save_video:
os.makedirs("results", exist_ok=True)
video_writer = cv.VideoWriter(
"results/yolo_tracked.avi",
cv.VideoWriter_fourcc(*"mp4v"), fps, (w, h)
)
log_path = os.path.join(
self.output_dir,
f"log_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
)
unique_ids = {}
track_hits = {}
frame_idx = 0
print(f"Running YOLOv11 + ByteTrack on {self.device.upper()}...")
print(f"Video : {w}x{h} @ {fps:.0f}fps ({total} frames)")
with open(log_path, 'w', newline='') as f:
writer = csv.writer(f)
writer.writerow([
'timestamp_s', 'frame', 'track_id',
'class', 'x1', 'y1', 'x2', 'y2', 'confidence'
])
while cap.isOpened():
success, frame = cap.read()
if not success:
break
timestamp = round(frame_idx / fps, 3)
results = model.track(
frame,
persist=True,
tracker="bytetrack.yaml",
conf=self.conf, # ✅ seuil de confiance
device=self.device,
verbose=False
)
no_object = True
if results[0].boxes is not None and results[0].boxes.id is not None:
for box in results[0].boxes:
track_id = int(box.id[0])
cls_name = normalize_class_name(model.names[int(box.cls[0])])
conf_val = round(float(box.conf[0]), 3)
x1, y1, x2, y2 = map(int, box.xyxy[0])
box_area = max(0, (x2 - x1)) * max(0, (y2 - y1))
if cls_name not in self.classes:
continue
if conf_val < self.conf:
continue
if box_area < self.min_box_area:
continue
no_object = False
track_hits[track_id] = track_hits.get(track_id, 0) + 1
if track_hits[track_id] >= self.min_track_hits:
unique_ids.setdefault(track_id, cls_name)
writer.writerow([
timestamp, frame_idx, track_id,
cls_name, x1, y1, x2, y2, conf_val
])
annotated = results[0].plot()
# Boîte rouge spéciale pour stop signs
if results[0].boxes is not None:
for box in results[0].boxes:
cls_name = normalize_class_name(model.names[int(box.cls[0])])
if cls_name == 'stop sign':
x1, y1, x2, y2 = map(int, box.xyxy[0])
cv.rectangle(annotated, (x1, y1), (x2, y2), (0, 0, 255), 3)
cv.putText(annotated, "STOP SIGN",
(x1, y1 - 10),
cv.FONT_HERSHEY_SIMPLEX, 0.8,
(0, 0, 255), 2)
if no_object:
cv.putText(annotated, "No selected object detected",
(20, 50), cv.FONT_HERSHEY_SIMPLEX,
1.0, (0, 0, 255), 2)
if save_video and video_writer:
video_writer.write(annotated)
if show:
cv.imshow("YOLOv11 Tracking", annotated)
if cv.waitKey(1) & 0xFF == ord('q'):
break
frame_idx += 1
cap.release()
if video_writer:
video_writer.release()
if show:
try:
cv.destroyAllWindows()
except cv.error:
pass
stats = {}
for cls in unique_ids.values():
stats[cls] = stats.get(cls, 0) + 1
print(f"\n=== Results ===")
print(f"Log CSV : {log_path}")
for cls, count in sorted(stats.items()):
print(f" {cls} : {count} unique objects")
return log_path, stats