import gradio as gr import tensorflow as tf import numpy as np import os import random from PIL import Image import keras # --- Config --- # Get the absolute path of the directory containing this script BASE_DIR = os.path.dirname(os.path.abspath(__file__)) MODEL_PATH = os.path.join(BASE_DIR, "best_model.keras") IMAGE_FOLDER = os.path.join(BASE_DIR, "test_images") IMG_SIZE = (128, 128) # --- Global State Helpers --- loading_error = None # MONKEY PATCH: Handle 'quantization_config' in Dense layer # This patch ensures that if 'quantization_config' is passed to Dense layer (from saved model), # it is safely ignored instead of raising an error. try: _original_dense_init = keras.layers.Dense.__init__ def _patched_dense_init(self, *args, quantization_config=None, **kwargs): _original_dense_init(self, *args, **kwargs) keras.layers.Dense.__init__ = _patched_dense_init print("✅ Applied monkey patch to keras.layers.Dense for quantization_config compatibility") except Exception as e: print(f"⚠️ Failed to patch Dense layer: {e}") def load_game_resources(): global loading_error """Loads model and image list.""" print(f"DEBUG: Keras Version: {keras.__version__}") print(f"DEBUG: TensorFlow Version: {tf.__version__}") # Load Model try: # compile=False avoids errors related to optimizers/losses that aren't needed for inference model = keras.models.load_model(MODEL_PATH, compile=False) print("✅ Model loaded successfully.") except Exception as e: print(f"❌ Error loading model: {e}") loading_error = str(e) model = None # Load Images images = [] if os.path.exists(IMAGE_FOLDER): for fname in os.listdir(IMAGE_FOLDER): if fname.lower().endswith(('.png', '.jpg', '.jpeg')): path = os.path.join(IMAGE_FOLDER, fname) # Determine label from filename prefix created in prepare_assets.py label = "Real" if "real_" in fname.lower() else "AI" images.append((path, label)) random.shuffle(images) print(f"✅ Loaded {len(images)} images.") return model, images # Load resources once on startup global_model, global_images = load_game_resources() # --- Game Logic --- def start_new_game(): """Reset scores and reshuffle images.""" random.shuffle(global_images) # State: [human_score, model_score, round_count, current_image_index, image_list] return 0, 0, 0, 0, global_images, gr.update(visible=True), gr.update(visible=False) def get_current_image(index, image_list): if not image_list or index >= len(image_list): return None, "Game Over! Restart to play again.", None path, label = image_list[index] return path, f"Round {index + 1}", label def predict_and_score(user_guess, index, image_list, human_score, model_score, rounds): if not image_list or index >= len(image_list): return human_score, model_score, rounds, index, "Game Over", None, gr.update(visible=False), gr.update(visible=True) path, true_label = image_list[index] # 1. Model Prediction model_guess_label = "Unsure" confidence = 0.0 if global_model: try: img = Image.open(path).convert('RGB') img = img.resize(IMG_SIZE) img_array = np.array(img) # Model has internal Rescaling layer, so keep [0, 255] img_array = np.expand_dims(img_array, axis=0) # Prediction (Sigmoid: <0.5 = Class 0, >0.5 = Class 1) # We need to know which class is which. # Usually: 0=AI, 1=Real OR 0=Real, 1=AI. # In the notebook: train_generator.class_indices would tell us. # Assumption based on directory names: sorting usually puts AI first. # So 0=AI, 1=Real. Let's verify this output if possible, but assume 0=AI for now. prediction = global_model.predict(img_array, verbose=0)[0][0] # Notebook typically uses 'training_AI' and 'training_real'. # Alphabetical: AI=0, real=1. is_real = prediction > 0.5 model_guess_label = "Real" if is_real else "AI" confidence = prediction if is_real else 1 - prediction except Exception as e: print(f"Prediction Error: {e}") model_guess_label = "Error" else: model_guess_label = f"Load Error: {loading_error}" # 2. Update Scores human_correct = (user_guess == true_label) model_correct = (model_guess_label == true_label) if human_correct: human_score += 1 if model_correct: model_score += 1 rounds += 1 next_idx = index + 1 # ... (Previous code) # 3. Message & Visuals if human_correct: # Green success text result_text = f"## Correct! It was {true_label}. 🎉\n\n" # Trigger confetti JS js_cmd = """ """ else: # Red failure text result_text = f"## Wrong! It was {true_label}. 😢\n\n" js_cmd = "" result_text += f"**You guessed:** {user_guess}\n" model_result_color = "green" if model_correct else "red" result_text += f"**Model guessed:** {model_guess_label} ({confidence:.2f})" # Prepare next round view # Hide guess buttons, Show Next button return human_score, model_score, rounds, next_idx, result_text, gr.update(visible=False), gr.update(visible=True), js_cmd # Helper for Game Over HTML def get_game_over_html(human_score, model_score): winner = "It's a Tie!" color = "#6b7280" # Gray if human_score > model_score: winner = "🎉 YOU WON! 🎉" color = "#10b981" # Green elif model_score > human_score: winner = "🤖 AI WON! 🤖" color = "#ef4444" # Red return f"""
Click 'Restart Game' to play again