Spaces:
Sleeping
Sleeping
Update yolo_detection.py
Browse files- yolo_detection.py +111 -0
yolo_detection.py
CHANGED
|
@@ -209,6 +209,117 @@ def annotate_video_with_bboxes(video_path):
|
|
| 209 |
writer.release()
|
| 210 |
return annotated_video_path
|
| 211 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
# File type validation
|
| 213 |
IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
|
| 214 |
VIDEO_EXTENSIONS = {'.mp4', '.mkv', '.mov', '.avi', '.flv', '.wmv', '.webm', '.m4v'}
|
|
|
|
| 209 |
writer.release()
|
| 210 |
return annotated_video_path
|
| 211 |
|
| 212 |
+
|
| 213 |
+
def process_video_unified(media_path):
|
| 214 |
+
"""
|
| 215 |
+
Single pass YOLO processing for video.
|
| 216 |
+
Detects people/machinery, calculates max counts, and generates an annotated video.
|
| 217 |
+
Returns: max_people_count, total_machinery_count, max_machine_types, annotated_video_path
|
| 218 |
+
"""
|
| 219 |
+
max_people_count = 0
|
| 220 |
+
max_machine_types = {
|
| 221 |
+
"Tower Crane": 0, "Mobile Crane": 0, "Compactor/Roller": 0, "Bulldozer": 0,
|
| 222 |
+
"Excavator": 0, "Dump Truck": 0, "Concrete Mixer": 0, "Loader": 0,
|
| 223 |
+
"Pump Truck": 0, "Pile Driver": 0, "Grader": 0, "Other Vehicle": 0
|
| 224 |
+
}
|
| 225 |
+
annotated_video_path = None
|
| 226 |
+
|
| 227 |
+
try:
|
| 228 |
+
cap = cv2.VideoCapture(media_path)
|
| 229 |
+
if not cap.isOpened():
|
| 230 |
+
print(f"Error: Could not open video file {media_path}")
|
| 231 |
+
return 0, 0, {}, None
|
| 232 |
+
|
| 233 |
+
fps = cap.get(cv2.CAP_PROP_FPS)
|
| 234 |
+
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
| 235 |
+
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
| 236 |
+
sample_rate = max(1, int(fps)) # Sample 1 frame per second
|
| 237 |
+
frame_count = 0
|
| 238 |
+
|
| 239 |
+
# Create a temp file for output annotated video
|
| 240 |
+
out_file = tempfile.NamedTemporaryFile(suffix=".mp4", delete=False)
|
| 241 |
+
annotated_video_path = out_file.name
|
| 242 |
+
out_file.close()
|
| 243 |
+
|
| 244 |
+
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
|
| 245 |
+
writer = cv2.VideoWriter(annotated_video_path, fourcc, fps, (w, h))
|
| 246 |
+
|
| 247 |
+
while True:
|
| 248 |
+
ret, frame = cap.read()
|
| 249 |
+
if not ret:
|
| 250 |
+
break
|
| 251 |
+
|
| 252 |
+
# Process every nth frame based on sample rate for stats, but annotate every frame
|
| 253 |
+
if frame_count % sample_rate == 0:
|
| 254 |
+
results = YOLO_MODEL(frame) # Run detection
|
| 255 |
+
|
| 256 |
+
# --- Calculate Max Counts ---
|
| 257 |
+
people, _, machine_types = process_yolo_results(results)
|
| 258 |
+
max_people_count = max(max_people_count, people)
|
| 259 |
+
for k, v in machine_types.items():
|
| 260 |
+
if k in max_machine_types: # Ensure key exists
|
| 261 |
+
max_machine_types[k] = max(max_machine_types.get(k, 0), v)
|
| 262 |
+
|
| 263 |
+
# --- Annotate Frame (using the same results) ---
|
| 264 |
+
frame_counts = {} # For summary text on this frame
|
| 265 |
+
annotated_frame = frame.copy() # Work on a copy for annotation
|
| 266 |
+
|
| 267 |
+
for r in results:
|
| 268 |
+
boxes = r.boxes
|
| 269 |
+
for box in boxes:
|
| 270 |
+
cls_id = int(box.cls[0])
|
| 271 |
+
conf = float(box.conf[0])
|
| 272 |
+
if conf < 0.5: continue
|
| 273 |
+
|
| 274 |
+
x1, y1, x2, y2 = map(int, box.xyxy[0])
|
| 275 |
+
class_name = YOLO_MODEL.names[cls_id]
|
| 276 |
+
|
| 277 |
+
# Draw bounding box
|
| 278 |
+
color = (0, 255, 0)
|
| 279 |
+
cv2.rectangle(annotated_frame, (x1, y1), (x2, y2), color, 2)
|
| 280 |
+
label_text = f"{class_name} {conf:.2f}"
|
| 281 |
+
cv2.putText(annotated_frame, label_text, (x1, y1 - 6),
|
| 282 |
+
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
|
| 283 |
+
|
| 284 |
+
# Increment per-frame class count for summary
|
| 285 |
+
frame_counts[class_name] = frame_counts.get(class_name, 0) + 1
|
| 286 |
+
|
| 287 |
+
# Build and draw summary string for the frame
|
| 288 |
+
summary_str = ", ".join(f"{cls}: {cnt}" for cls, cnt in frame_counts.items())
|
| 289 |
+
cv2.putText(annotated_frame, summary_str, (15, 30),
|
| 290 |
+
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (255, 255, 0), 2)
|
| 291 |
+
|
| 292 |
+
writer.write(annotated_frame) # Write annotated frame
|
| 293 |
+
else:
|
| 294 |
+
# If not sampling this frame for stats, still write original frame to keep video length correct
|
| 295 |
+
# Or optionally, run detection+annotation anyway if performance allows and annotation is desired for all frames
|
| 296 |
+
# For now, let's just write the original frame to maintain sync
|
| 297 |
+
writer.write(frame)
|
| 298 |
+
|
| 299 |
+
|
| 300 |
+
frame_count += 1
|
| 301 |
+
|
| 302 |
+
cap.release()
|
| 303 |
+
writer.release()
|
| 304 |
+
|
| 305 |
+
# Filter out zero counts from max_machine_types
|
| 306 |
+
max_machine_types = {k: v for k, v in max_machine_types.items() if v > 0}
|
| 307 |
+
total_machinery_count = sum(max_machine_types.values())
|
| 308 |
+
|
| 309 |
+
print(f"Unified processing complete. People: {max_people_count}, Machinery: {total_machinery_count}, Types: {max_machine_types}")
|
| 310 |
+
return max_people_count, total_machinery_count, max_machine_types, annotated_video_path
|
| 311 |
+
|
| 312 |
+
except Exception as e:
|
| 313 |
+
print(f"Error in unified YOLO video processing: {str(e)}")
|
| 314 |
+
# Clean up potentially created temp file on error
|
| 315 |
+
if annotated_video_path and os.path.exists(annotated_video_path):
|
| 316 |
+
try:
|
| 317 |
+
os.remove(annotated_video_path)
|
| 318 |
+
except OSError:
|
| 319 |
+
pass # Ignore error during cleanup
|
| 320 |
+
return 0, 0, {}, None
|
| 321 |
+
|
| 322 |
+
|
| 323 |
# File type validation
|
| 324 |
IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
|
| 325 |
VIDEO_EXTENSIONS = {'.mp4', '.mkv', '.mov', '.avi', '.flv', '.wmv', '.webm', '.m4v'}
|