diff --git a/take-away/docker-compose.yaml b/take-away/docker-compose.yaml index d53a6c3..70b0416 100755 --- a/take-away/docker-compose.yaml +++ b/take-away/docker-compose.yaml @@ -125,11 +125,11 @@ services: # GStreamer capture settings - CAPTURE_FPS: ${CAPTURE_FPS:-1} # Frames per second for pipeline (1fps for multi-station) + CAPTURE_FPS: ${CAPTURE_FPS:-10} # Frames per second for pipeline (10fps) # Frame pipeline tuning (order 384 fix: OCR models take ~30s to load) # Pre-buffer must hold frames until OCR is ready to detect orders - PRE_BUFFER_FRAMES: ${PRE_BUFFER_FRAMES:-100} # Frames to hold in buffer (100 @ 1fps = 100s) + PRE_BUFFER_FRAMES: ${PRE_BUFFER_FRAMES:-1000} # Frames to hold in buffer (1000 @ 10fps = 100s) OCR_WARMUP_FRAMES: ${OCR_WARMUP_FRAMES:-2} # Frames to wait before OCR ready signal (reduced for faster startup) # Performance tuning @@ -140,6 +140,11 @@ services: # Metrics logging CONTAINER_RESULTS_PATH: ${CONTAINER_RESULTS_PATH:-/results} USECASE_1: ${USECASE_1:-take-away-order-accuracy} + + # Order Recall - path where frame-selector writes debug frames + FRAME_SELECTOR_DEBUG_DIR: ${FRAME_SELECTOR_DEBUG_DIR:-/results/frame-selector-in} + # Playback FPS for stitched recall replay video + RECALL_REPLAY_FPS: ${RECALL_REPLAY_FPS:-10} # RabbitMQ disabled for simplicity (direct HTTP calls used) USE_RABBITMQ: ${USE_RABBITMQ:-false} diff --git a/take-away/gradio-ui/gradio_app.py b/take-away/gradio-ui/gradio_app.py index ff7f3fa..c7dc584 100755 --- a/take-away/gradio-ui/gradio_app.py +++ b/take-away/gradio-ui/gradio_app.py @@ -4,6 +4,8 @@ import threading import time import math +import tempfile +import os import numpy as np from typing import Optional, List, Dict from PIL import Image @@ -26,7 +28,7 @@ # Global processing state is_processing = False -APP_TITLE = "๐ฆ Take-Away Order Accuracy" +APP_TITLE = "Take-Away Order Accuracy" APP_DESCRIPTION = "AI-powered order validation for take-away operations" # Intel Brand CSS (matching dine-in styling) @@ -35,7 +37,7 @@ :root { --primary-500: #0071C5 !important; --primary-600: #005A9E !important; - --primary-700: #00285A !important; + --primary-700: #0258b5 !important; --neutral-50: #f8fafc; --neutral-100: #f1f5f9; --neutral-200: #e2e8f0; @@ -75,7 +77,7 @@ /* Header Banner - Intel Blue */ .header-banner { - background: linear-gradient(135deg, #0071C5 0%, #00285A 100%); + background: linear-gradient(135deg, #0071C5 0%, #0258b5 100%); color: white; padding: 28px 40px; border-radius: 12px; @@ -123,7 +125,7 @@ /* Primary Button - Intel Blue */ .primary-btn { - background: linear-gradient(135deg, #0071C5 0%, #00285A 100%) !important; + background: linear-gradient(135deg, #0071C5 0%, #0258b5 100%) !important; border: none !important; border-radius: 10px !important; font-weight: 600 !important; @@ -156,7 +158,7 @@ } .video-history-header { - background: linear-gradient(135deg, #0071C5 0%, #00285A 100%); + background: linear-gradient(135deg, #0071C5 0%, #0258b5 100%); color: white; padding: 14px 20px; cursor: pointer; @@ -181,7 +183,7 @@ padding: 12px 15px; text-align: left; font-weight: 600; - color: #00285A; + color: #0258b5; font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; @@ -231,7 +233,7 @@ /* Section Headers */ .section-header { font-weight: 600; - color: #00285A; + color: #0258b5; font-size: 14px; margin-bottom: 10px; } @@ -239,7 +241,7 @@ /* Footer */ .footer-info { text-align: center; - color: #00285A; + color: #0258b5; font-size: 13px; padding: 20px; border-top: 2px solid #E6F3FB; @@ -391,11 +393,11 @@ def start_smooth_stream(rtsp_url): current_time = time.time() if current_time - last_update >= 1.0: fps = fps_counter / (current_time - last_update) - status = f"โ Frame {frame_count} - Smooth stream active ({fps:.1f} FPS)" + status = f"Frame {frame_count} - Smooth stream active ({fps:.1f} FPS)" fps_counter = 0 last_update = current_time else: - status = f"โ Frame {frame_count} - Smooth stream active" + status = f"Frame {frame_count} - Smooth stream active" yield frame, status @@ -461,7 +463,7 @@ def upload_video_with_progress(file, progress=gr.Progress()): initial_stats = fetch_statistics() initial_processed = initial_stats.get("total_processed", 0) if initial_stats else 0 - progress(0.1, desc="๐ค Uploading video...") + progress(0.1, desc="Uploading video...") with open(file.name, "rb") as f: resp = _api.post( @@ -472,7 +474,7 @@ def upload_video_with_progress(file, progress=gr.Progress()): if resp.status_code != 200: is_processing = False - return f"โ Upload failed: {resp.text}", gr.update(interactive=True, value="๐ Upload & Start Processing") + return f"โ Upload failed: {resp.text}", gr.update(interactive=True, value="Upload & Start Processing") data = resp.json() video_id = data.get('video_id', 'unknown') @@ -503,11 +505,11 @@ def upload_video_with_progress(file, progress=gr.Progress()): if orders_completed > 0: # Bump progress when orders complete order_progress = min(0.9, time_progress + 0.1 * orders_completed) - progress(order_progress, desc=f"๐ {orders_completed} order(s) detected") + progress(order_progress, desc=f"{orders_completed} order(s) detected") else: - progress(time_progress, desc=f"๐ Analyzing video...") + progress(time_progress, desc=f"Analyzing video...") else: - progress(time_progress, desc=f"๐ Processing...") + progress(time_progress, desc=f"Processing...") # Check if results are available results = fetch_results() @@ -520,7 +522,7 @@ def upload_video_with_progress(file, progress=gr.Progress()): results = fetch_results() break - progress(1.0, desc="โ Processing complete!") + progress(1.0, desc="Processing complete!") # Mark video as completed via API try: @@ -541,12 +543,12 @@ def upload_video_with_progress(file, progress=gr.Progress()): mismatch = stats.get("total_mismatch", 0) if stats else 0 return ( - f"โ Video processed successfully\n" + f"Video processed successfully\n" f"Video: {video_name}\n" f"Time: {upload_time}\n" f"Orders detected: {len(session_results)}\n" f"Validated: {validated} | Mismatch: {mismatch}", - gr.update(interactive=True, value="๐ Upload & Start Processing") + gr.update(interactive=True, value="Upload & Start Processing") ) except Exception as e: @@ -557,7 +559,7 @@ def upload_video_with_progress(file, progress=gr.Progress()): _api.post(f"{API_BASE}/videos/{video_id}/fail?error={str(e)}", timeout=5) except: pass - return f"โ Upload error: {e}", gr.update(interactive=True, value="๐ Upload & Start Processing") + return f"Upload error: {e}", gr.update(interactive=True, value="Upload & Start Processing") def generate_validation_summary(results): """Generate validation summary from results""" @@ -621,10 +623,10 @@ def start_rtsp_processing(rtsp_url): data = resp.json() video_id = data.get("video_id", "") return ( - f"โ RTSP pipeline started\n" + f"RTSP pipeline started\n" f"Source: {rtsp_url}\n" f"Video ID: {video_id}\n" - f"Results will appear in the '๐ Detected Orders' tab." + f"Results will appear in the 'Detected Orders' tab." ) except Exception as e: @@ -661,24 +663,24 @@ def format_validation_summary_table(results):
| Metric | -Count | -Percentage | +Metric | +Count | +Percentage |
|---|---|---|---|---|---|
| Total Orders | -{total} | -100% | +Total Orders | +{total} | +100% |
| โ Validated | +Validated | {validated} | {validated/total*100:.1f}% | ||
| โ Mismatch | +Mismatch | {mismatch} | {mismatch/total*100:.1f}% | ||
| {name} | -{qty} | +{name} | +{qty} | {item_status} | |
| {item.get("name","?")} | +{item.get("quantity",0)} | +||||
| {name} | +{qty} | +{s} | +