Spaces:
Sleeping
Sleeping
| """ | |
| SpectralDetector V3.6 - Web Interface | |
| Gradio app per il rilevamento di immagini AI-generated | |
| Cascaded 4-HEAD architecture (A, B, C, D) | |
| """ | |
| import gradio as gr | |
| import cv2 | |
| import numpy as np | |
| from pathlib import Path | |
| import sys | |
| import json | |
| from datetime import datetime | |
| import tempfile | |
| import os | |
| # Import del detector | |
| project_root = Path(__file__).parent | |
| sys.path.insert(0, str(project_root)) | |
| sys.path.insert(0, str(project_root / 'archive' / 'legacy_src')) | |
| from detect_image import SpectralDetectorV36Cascaded | |
| # Configurazione | |
| MODELS_DIR = "outputs/heads_v36_67features" | |
| # Inizializza detector (caricato una volta all'avvio) | |
| print("π Caricamento modelli V3.6...") | |
| detector = SpectralDetectorV36Cascaded( | |
| models_dir=MODELS_DIR, | |
| verbose=True | |
| ) | |
| print("β Modelli V3.6 caricati!") | |
| def analyze_image(image): | |
| """ | |
| Analizza un'immagine caricata dall'utente con V3.6. | |
| Args: | |
| image: numpy array (H, W, 3) RGB | |
| Returns: | |
| decision, confidence, details, zone_color | |
| """ | |
| try: | |
| # Converti RGB -> BGR per OpenCV | |
| if image is None: | |
| return "β Errore", "Nessuna immagine caricata", "", "#ff0000" | |
| image_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR) | |
| # Salva temporaneamente l'immagine | |
| temp_dir = tempfile.gettempdir() | |
| temp_path = os.path.join(temp_dir, f"gradio_upload_{datetime.now().strftime('%Y%m%d_%H%M%S')}.jpg") | |
| cv2.imwrite(temp_path, image_bgr) | |
| try: | |
| # Classifica usando V3.6 | |
| start_time = datetime.now() | |
| result = detector.classify_image(temp_path) | |
| proc_time = (datetime.now() - start_time).total_seconds() | |
| finally: | |
| # Rimuovi il file temporaneo | |
| if os.path.exists(temp_path): | |
| os.remove(temp_path) | |
| # Estrai risultati V3.6 | |
| decision = result['final_label'] | |
| confidence = result['confidence'] | |
| route = result['route_taken'] | |
| exit_head = result['exit_head'] | |
| probs = result['probabilities'] | |
| # Decision display | |
| if decision == 'REAL': | |
| decision_display = "πΈ IMMAGINE REALE" | |
| decision_emoji = "β " | |
| zone_color = '#22c55e' # Verde | |
| elif 'Unknown' in decision: | |
| decision_display = f"π€ {decision}" | |
| decision_emoji = "β" | |
| zone_color = '#f97316' # Arancione | |
| else: | |
| decision_display = f"π€ AI: {decision}" | |
| decision_emoji = "β οΈ" | |
| zone_color = '#ef4444' # Rosso | |
| confidence_label = f"Confidenza: {confidence*100:.1f}%" | |
| # Dettagli tecnici V3.6 | |
| details = f""" | |
| ### π Analisi Dettagliata V3.6 | |
| **Decisione Finale:** {decision_emoji} **{decision}** | |
| **Route:** {route} | |
| **Exit HEAD:** {exit_head} | |
| **Tempo di Elaborazione:** {proc_time:.2f}s | |
| --- | |
| #### ProbabilitΓ (POST-CALIBRATION) | |
| - **P(REAL):** {probs['p_real']:.4f} ({probs['p_real']*100:.2f}%) | |
| - **P(AI):** {probs['p_ai']:.4f} ({probs['p_ai']*100:.2f}%) | |
| #### Identificazione Generatore (4-HEAD System) | |
| - **P(GPT-IMAGE-1):** {probs['p_gpt_image_1']:.4f} (HEAD D) | |
| - **P(SDXL):** {probs['p_sdxl']:.4f} (HEAD B) | |
| - **P(ChatGPT):** {probs['p_chatgpt']:.4f} (HEAD C) | |
| - **P(Gemini):** {probs['p_gemini']:.4f} (HEAD C) | |
| #### Architettura Cascaded | |
| - **Threshold Hit:** {result['threshold_hit']} | |
| - **Meta-Router Scores:** {result.get('scores', 'N/A')} | |
| --- | |
| *SpectralDetector V3.6-CASCADED - Build 2025-11-07* | |
| """ | |
| return decision_display, confidence_label, details, zone_color | |
| except Exception as e: | |
| import traceback | |
| error_trace = traceback.format_exc() | |
| error_details = f""" | |
| ### β Errore nell'Analisi | |
| **Messaggio:** {str(e)} | |
| **Traceback:** | |
| ``` | |
| {error_trace} | |
| ``` | |
| Si prega di: | |
| 1. Verificare che l'immagine sia valida (JPG, PNG) | |
| 2. Assicurarsi che l'immagine non sia corrotta | |
| 3. Provare con un'altra immagine | |
| --- | |
| *Se l'errore persiste, contattare il supporto.* | |
| """ | |
| return "β Errore", "Analisi fallita", error_details, "#ff0000" | |
| def create_interface(): | |
| """ | |
| Crea l'interfaccia Gradio. | |
| """ | |
| # CSS personalizzato | |
| custom_css = """ | |
| .gradio-container { | |
| font-family: 'Inter', sans-serif; | |
| } | |
| .decision-box { | |
| padding: 20px; | |
| border-radius: 10px; | |
| text-align: center; | |
| font-size: 24px; | |
| font-weight: bold; | |
| margin: 10px 0; | |
| } | |
| .confidence-box { | |
| padding: 15px; | |
| border-radius: 8px; | |
| text-align: center; | |
| font-size: 18px; | |
| margin: 10px 0; | |
| } | |
| .footer { | |
| text-align: center; | |
| padding: 20px; | |
| color: #6b7280; | |
| font-size: 14px; | |
| } | |
| """ | |
| with gr.Blocks(css=custom_css, theme=gr.themes.Soft(), title="SpectralDetector V3.6") as demo: | |
| # Header | |
| gr.Markdown(""" | |
| # π¬ SpectralDetector V3.6 | |
| ### Rilevatore di Immagini AI-Generated - Architettura Cascaded 4-HEAD | |
| Carica un'immagine per verificare se Γ¨ **reale** o **generata da AI** (ChatGPT, GPT-IMAGE-1, Gemini, SDXL, etc.) | |
| **NovitΓ V3.6:** 4 HEAD specializzati (A: REAL/AI, B: SDXL, C: ChatGPT/Gemini, D: GPT-IMAGE-1) con router cascaded + calibrazione temperature | |
| """) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| # Input | |
| gr.Markdown("### π€ Carica Immagine") | |
| image_input = gr.Image( | |
| type="numpy", | |
| label="Seleziona o trascina un'immagine", | |
| height=400 | |
| ) | |
| analyze_btn = gr.Button( | |
| "π Analizza Immagine", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| gr.Markdown(""" | |
| #### βΉοΈ Formati Supportati | |
| - JPG, JPEG, PNG | |
| - Qualsiasi risoluzione | |
| - Massimo 10 MB | |
| #### π‘οΈ Privacy | |
| Le immagini non vengono salvate nΓ© condivise. | |
| """) | |
| with gr.Column(scale=1): | |
| # Output | |
| gr.Markdown("### π Risultato Analisi") | |
| decision_output = gr.Textbox( | |
| label="Decisione", | |
| interactive=False, | |
| elem_classes=["decision-box"] | |
| ) | |
| confidence_output = gr.Textbox( | |
| label="Confidenza", | |
| interactive=False, | |
| elem_classes=["confidence-box"] | |
| ) | |
| zone_indicator = gr.Textbox( | |
| label="Indicatore Zona", | |
| visible=False, | |
| interactive=False | |
| ) | |
| details_output = gr.Markdown(label="Dettagli Tecnici") | |
| # Esempi | |
| gr.Markdown("### π― Prova con Esempi") | |
| # Cerca immagini di esempio | |
| example_images = [] | |
| example_dir = Path("examples") | |
| if example_dir.exists(): | |
| for img_path in example_dir.glob("*.jpg"): | |
| example_images.append([str(img_path)]) | |
| for img_path in example_dir.glob("*.png"): | |
| example_images.append([str(img_path)]) | |
| if example_images: | |
| gr.Examples( | |
| examples=example_images[:6], # Max 6 esempi | |
| inputs=image_input, | |
| label="Clicca su un esempio" | |
| ) | |
| # Info tecniche | |
| with gr.Accordion("π¬ Dettagli Tecnici del Sistema V3.6", open=False): | |
| gr.Markdown(""" | |
| ### Architettura SpectralDetector V3.6 - Cascaded 4-HEAD System | |
| #### π§ Modelli (4-HEAD Architecture) | |
| - **HEAD A**: Random Forest (200 trees) - REAL vs AI Gate | |
| - **HEAD B**: Random Forest (200 trees) - SDXL Detection | |
| - **HEAD C**: Random Forest (200 trees) - ChatGPT vs Gemini | |
| - **HEAD D**: Random Forest (200 trees) - GPT-IMAGE-1 Detection | |
| - **Meta-Router V3.6**: Cascaded decision with fallback (Opzione 3) | |
| - **Calibration**: Temperature scaling per HEAD A/B/C | |
| #### π Features | |
| - **HEAD A/B/C**: 67 features (FFT, DCT, compression, saturation) | |
| - **HEAD D**: 90 features (67 base + 23 global/patch extra) | |
| #### π― Performance (Dataset Completo - 1,546 immagini) | |
| - **Binary Accuracy**: 94.1% (β₯92% target) β | |
| - **Generator ID**: 84.8% (β₯80% target) β | |
| - **Unknown Rate**: 5.4% (β€25% target) β | |
| - **AI Recall**: 99.9% | |
| - **Per-Generator**: ChatGPT 93.9%, Gemini 94.3%, SDXL 73.7%, GPT-IMAGE-1 74.3% | |
| - **Processing Time**: ~93ms per immagine | |
| #### π Cascaded Router Architecture | |
| 1. **HEAD A Gate**: p_real β₯ 0.65 β REAL | p_ai β₯ 0.65 β Cascade | Gray zone β Meta-Router | |
| 2. **AI Cascade (DβBβC)**: Early-exit con thresholds ad alta precisione | |
| - HEAD D: p_gpt_image_1 β₯ 0.78 β GPT-IMAGE-1 | |
| - HEAD B: p_sdxl β₯ 0.72 β SDXL | |
| - HEAD C: max(p_cg, p_ge) β₯ 0.68 β ChatGPT/Gemini | |
| 3. **Meta-Router Fallback**: Weighted scores + margin guardrail quando incerto | |
| #### π Generatori Rilevabili | |
| - ChatGPT (DALL-E 3) | |
| - GPT-IMAGE-1 (OpenAI) | |
| - Google Gemini (Imagen) | |
| - Stable Diffusion XL (SDXL) | |
| #### π Release | |
| - **Versione**: 3.6-CASCADED | |
| - **Build**: 2025-11-07 | |
| - **Training**: Dataset Completo (1,546 samples, 5 sources) | |
| - **Validation**: 70/20/10 split stratified | |
| """) | |
| # Footer | |
| gr.Markdown(""" | |
| --- | |
| <div class="footer"> | |
| <p><strong>SpectralDetector V3.6-CASCADED</strong> - Build 2025-11-07</p> | |
| <p>Developed by Denis Billi | 4-HEAD Cascaded Architecture with Meta-Router Fallback</p> | |
| <p>β οΈ Questo strumento Γ¨ da considerarsi di supporto. Per decisioni critiche, consultare un esperto.</p> | |
| </div> | |
| """) | |
| # Event handlers | |
| analyze_btn.click( | |
| fn=analyze_image, | |
| inputs=image_input, | |
| outputs=[decision_output, confidence_output, details_output, zone_indicator] | |
| ) | |
| return demo | |
| # Create demo instance at module level (required for HuggingFace Spaces) | |
| demo = create_interface() | |
| if __name__ == "__main__": | |
| demo.launch( | |
| server_name="0.0.0.0", # Accessibile da rete | |
| server_port=7860, # Porta standard Hugging Face | |
| share=False, # Non creare link pubblico (solo per test locale) | |
| show_error=True | |
| ) | |