|
|
import gradio as gr |
|
|
import mlflow |
|
|
import os |
|
|
import time |
|
|
import tempfile |
|
|
from ultralytics import YOLO |
|
|
from PIL import Image |
|
|
import cv2 |
|
|
import numpy as np |
|
|
|
|
|
|
|
|
tracking_uri = os.getenv("MLFLOW_TRACKING_URI") |
|
|
username = os.getenv("MLFLOW_TRACKING_USERNAME") |
|
|
password = os.getenv("MLFLOW_TRACKING_PASSWORD") |
|
|
|
|
|
if not all([tracking_uri, username, password]): |
|
|
raise ValueError("MLflow Secrets are not configured! Go to Space Settings and verify the names") |
|
|
|
|
|
os.environ["MLFLOW_TRACKING_URI"] = tracking_uri |
|
|
os.environ["MLFLOW_TRACKING_USERNAME"] = username |
|
|
os.environ["MLFLOW_TRACKING_PASSWORD"] = password |
|
|
|
|
|
mlflow.set_experiment("YOLOv12s_Inference_Logs") |
|
|
print("MLflow configured successfully using secrets!") |
|
|
|
|
|
|
|
|
model = YOLO("Yolo12s.pt") |
|
|
|
|
|
|
|
|
def run_inference(media_file, media_type): |
|
|
if media_file is None: |
|
|
return None, None, None, None, "Please upload a file first" |
|
|
|
|
|
media_path = media_file.name |
|
|
|
|
|
with mlflow.start_run(run_name=f"Inference_{int(time.time())}") as run: |
|
|
mlflow.log_param("media_type", media_type) |
|
|
mlflow.log_param("model", "YOLOv12s") |
|
|
mlflow.log_param("timestamp", time.strftime("%Y-%m-%d %H:%M:%S")) |
|
|
|
|
|
if media_type == "Image": |
|
|
img = Image.open(media_path).convert("RGB") |
|
|
img_array = np.array(img) |
|
|
results = model(img_array)[0] |
|
|
annotated = results.plot() |
|
|
output_img = Image.fromarray(annotated[..., ::-1]) |
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmpdir: |
|
|
in_path = os.path.join(tmpdir, "input.jpg") |
|
|
out_path = os.path.join(tmpdir, "output.jpg") |
|
|
img.save(in_path) |
|
|
output_img.save(out_path) |
|
|
mlflow.log_artifact(in_path, artifact_path="input") |
|
|
mlflow.log_artifact(out_path, artifact_path="output") |
|
|
|
|
|
detections = len(results.boxes) if results.boxes is not None else 0 |
|
|
mlflow.log_metric("detections_count", detections) |
|
|
|
|
|
return img, output_img, None, None, f"Detection Complete! Objects Detected: {detections}" |
|
|
|
|
|
else: |
|
|
cap = cv2.VideoCapture(media_path) |
|
|
fps = cap.get(cv2.CAP_PROP_FPS) or 30 |
|
|
w = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) |
|
|
h = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) |
|
|
|
|
|
output_video = "output_video.mp4" |
|
|
writer = cv2.VideoWriter(output_video, cv2.VideoWriter_fourcc(*'mp4v'), fps, (w, h)) |
|
|
|
|
|
frame_count = 0 |
|
|
total_detections = 0 |
|
|
|
|
|
while cap.isOpened(): |
|
|
ret, frame = cap.read() |
|
|
if not ret: |
|
|
break |
|
|
|
|
|
results = model(frame)[0] |
|
|
annotated = results.plot() |
|
|
writer.write(annotated) |
|
|
|
|
|
frame_count += 1 |
|
|
total_detections += len(results.boxes) if results.boxes is not None else 0 |
|
|
|
|
|
cap.release() |
|
|
writer.release() |
|
|
|
|
|
mlflow.log_artifact(media_path, artifact_path="input_video") |
|
|
mlflow.log_artifact(output_video, artifact_path="output_video") |
|
|
mlflow.log_metric("frames_processed", frame_count) |
|
|
mlflow.log_metric("total_detections", total_detections) |
|
|
mlflow.log_metric("avg_detections_per_frame", total_detections / frame_count if frame_count > 0 else 0) |
|
|
|
|
|
avg_det = total_detections / frame_count if frame_count > 0 else 0 |
|
|
result_message = f"Video Processing Complete! Frames: {frame_count}, Total Detections: {total_detections}, Average per Frame: {avg_det:.2f}" |
|
|
|
|
|
result_video = output_video |
|
|
|
|
|
return None, None, media_path, result_video, result_message |
|
|
|
|
|
|
|
|
css = """ |
|
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap'); |
|
|
|
|
|
* { |
|
|
font-family: 'Inter', sans-serif; |
|
|
} |
|
|
|
|
|
body { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
} |
|
|
|
|
|
.gradio-container { |
|
|
max-width: 1400px !important; |
|
|
margin: 40px auto !important; |
|
|
background: rgba(255, 255, 255, 0.95) !important; |
|
|
border-radius: 24px !important; |
|
|
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3) !important; |
|
|
padding: 0 !important; |
|
|
overflow: hidden !important; |
|
|
} |
|
|
|
|
|
.header-container { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
padding: 48px 40px; |
|
|
text-align: center; |
|
|
border-radius: 24px 24px 0 0; |
|
|
} |
|
|
|
|
|
.header-container h1 { |
|
|
color: white !important; |
|
|
font-size: 2.8em !important; |
|
|
font-weight: 700 !important; |
|
|
margin: 0 0 12px 0 !important; |
|
|
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); |
|
|
} |
|
|
|
|
|
.header-container p { |
|
|
color: rgba(255, 255, 255, 0.95) !important; |
|
|
font-size: 1.1em !important; |
|
|
margin: 0 !important; |
|
|
} |
|
|
|
|
|
.main-content { |
|
|
padding: 48px 40px; |
|
|
} |
|
|
|
|
|
.file-upload-area { |
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); |
|
|
border-radius: 16px; |
|
|
padding: 32px; |
|
|
border: 2px dashed #667eea; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.file-upload-area:hover { |
|
|
border-color: #764ba2; |
|
|
transform: translateY(-2px); |
|
|
box-shadow: 0 8px 16px rgba(102, 126, 234, 0.2); |
|
|
} |
|
|
|
|
|
button.primary { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important; |
|
|
border: none !important; |
|
|
color: white !important; |
|
|
font-weight: 600 !important; |
|
|
font-size: 1.1em !important; |
|
|
padding: 16px 48px !important; |
|
|
border-radius: 12px !important; |
|
|
cursor: pointer !important; |
|
|
transition: all 0.3s ease !important; |
|
|
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4) !important; |
|
|
} |
|
|
|
|
|
button.primary:hover { |
|
|
transform: translateY(-2px) !important; |
|
|
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.6) !important; |
|
|
} |
|
|
|
|
|
.radio-group label { |
|
|
background: white; |
|
|
padding: 12px 24px; |
|
|
border-radius: 8px; |
|
|
border: 2px solid #e5e7eb; |
|
|
cursor: pointer; |
|
|
transition: all 0.3s ease; |
|
|
} |
|
|
|
|
|
.radio-group label:hover { |
|
|
border-color: #667eea; |
|
|
background: #f5f7fa; |
|
|
} |
|
|
|
|
|
.output-image, .output-video { |
|
|
border-radius: 16px; |
|
|
overflow: hidden; |
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1); |
|
|
background: white; |
|
|
} |
|
|
|
|
|
.info-box { |
|
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); |
|
|
border-radius: 12px; |
|
|
padding: 24px; |
|
|
border-left: 4px solid #667eea; |
|
|
} |
|
|
|
|
|
::-webkit-scrollbar { |
|
|
width: 8px; |
|
|
} |
|
|
|
|
|
::-webkit-scrollbar-track { |
|
|
background: #f1f1f1; |
|
|
} |
|
|
|
|
|
::-webkit-scrollbar-thumb { |
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); |
|
|
border-radius: 4px; |
|
|
} |
|
|
|
|
|
::-webkit-scrollbar-thumb:hover { |
|
|
background: #764ba2; |
|
|
} |
|
|
|
|
|
@keyframes fadeIn { |
|
|
from { |
|
|
opacity: 0; |
|
|
transform: translateY(20px); |
|
|
} |
|
|
to { |
|
|
opacity: 1; |
|
|
transform: translateY(0); |
|
|
} |
|
|
} |
|
|
|
|
|
.animate-in { |
|
|
animation: fadeIn 0.6s ease-out; |
|
|
} |
|
|
""" |
|
|
|
|
|
with gr.Blocks() as demo: |
|
|
gr.HTML(""" |
|
|
<div class="header-container animate-in"> |
|
|
<h1>YOLOv12s Vehicle Detection</h1> |
|
|
<p>Detect vehicles in Egyptian streets with state-of-the-art AI</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
with gr.Row(elem_classes="main-content"): |
|
|
with gr.Column(scale=1): |
|
|
gr.Markdown("### Upload Media") |
|
|
media = gr.File( |
|
|
label="Drop your image or video here", |
|
|
file_types=[".jpg", ".jpeg", ".png", ".mp4", ".avi"], |
|
|
elem_classes="file-upload-area" |
|
|
) |
|
|
|
|
|
gr.Markdown("### Media Type") |
|
|
media_type = gr.Radio( |
|
|
["Image", "Video"], |
|
|
label="Select type", |
|
|
value="Image", |
|
|
elem_classes="radio-group" |
|
|
) |
|
|
|
|
|
btn = gr.Button("Run Detection", variant="primary", size="lg", elem_classes="primary") |
|
|
|
|
|
gr.Markdown(""" |
|
|
--- |
|
|
### Features |
|
|
- Real-time vehicle detection |
|
|
- Support for images and videos |
|
|
- Auto-logging to DagsHub |
|
|
- Detailed metrics tracking |
|
|
""") |
|
|
|
|
|
with gr.Column(scale=2): |
|
|
gr.Markdown("### Detection Results") |
|
|
|
|
|
with gr.Tabs(): |
|
|
with gr.Tab("Image Results"): |
|
|
with gr.Row(): |
|
|
img_original = gr.Image( |
|
|
label="Original Image", |
|
|
height=400, |
|
|
elem_classes="output-image" |
|
|
) |
|
|
img_detected = gr.Image( |
|
|
label="Detected Objects", |
|
|
height=400, |
|
|
elem_classes="output-image" |
|
|
) |
|
|
|
|
|
with gr.Tab("Video Results"): |
|
|
with gr.Row(): |
|
|
vid_original = gr.Video( |
|
|
label="Original Video", |
|
|
height=400, |
|
|
elem_classes="output-video" |
|
|
) |
|
|
vid_detected = gr.Video( |
|
|
label="Detected Objects", |
|
|
height=400, |
|
|
elem_classes="output-video" |
|
|
) |
|
|
|
|
|
gr.Markdown("### Run Information") |
|
|
info = gr.Markdown( |
|
|
"Ready to detect... Upload a file and click Run Detection to start!", |
|
|
elem_classes="info-box" |
|
|
) |
|
|
|
|
|
btn.click( |
|
|
fn=run_inference, |
|
|
inputs=[media, media_type], |
|
|
outputs=[img_original, img_detected, vid_original, vid_detected, info] |
|
|
) |
|
|
|
|
|
gr.HTML(""" |
|
|
<div style="text-align: center; padding: 32px; color: #6b7280;"> |
|
|
<p>Powered by YOLOv12s - MLflow Tracking - DagsHub Integration</p> |
|
|
</div> |
|
|
""") |
|
|
|
|
|
demo.launch(css=css, theme=gr.themes.Soft()) |