Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import tensorflow as tf | |
| import numpy as np | |
| from PIL import Image | |
| import json | |
| import requests | |
| import io | |
| import base64 | |
| import os | |
| os.environ["CUDA_VISIBLE_DEVICES"] = "-1" # Disable GPU | |
| # ===== CONFIGURACIÓN ===== | |
| PLANTNET_CONFIG = { | |
| "base_url": "https://my-api.plantnet.org/v2/identify/all", | |
| "api_key": "2b10GtUDt6p1whX94wlEiR3CG", | |
| "timeout": 10, | |
| "lang": "es" # Español | |
| } | |
| # ===== VARIABLES GLOBALES ===== | |
| MODEL_LOADED = False | |
| model = None | |
| labels = [] | |
| # ===== FUNCIONES DE UTILIDAD ===== | |
| def load_model(): | |
| global model, labels, MODEL_LOADED | |
| try: | |
| model = tf.keras.models.load_model("flores_modelo (2).h5") | |
| with open("clases_orden_oxford.json", "r") as f: | |
| class_indices = json.load(f) | |
| labels = [None] * len(class_indices) | |
| for class_name, idx in class_indices.items(): | |
| labels[idx] = class_name.replace("_", " ").title() | |
| MODEL_LOADED = True | |
| except Exception as e: | |
| print(f"Error al cargar el modelo: {e}") | |
| MODEL_LOADED = False | |
| def image_to_base64(image_path): | |
| try: | |
| with open(image_path, "rb") as img_file: | |
| return base64.b64encode(img_file.read()).decode('utf-8') | |
| except: | |
| return None | |
| def preprocess_image(image): | |
| image = image.resize((224, 224)) | |
| img_array = np.array(image) / 255.0 | |
| if img_array.shape[-1] == 4: | |
| img_array = img_array[..., :3] | |
| return np.expand_dims(img_array, axis=0) | |
| # ===== INTEGRACIÓN CON PLANTNET ===== | |
| def get_flower_info_from_plantnet(flower_name, image_array): | |
| try: | |
| image_pil = Image.fromarray((image_array[0] * 255).astype(np.uint8)) | |
| img_byte_arr = io.BytesIO() | |
| image_pil.save(img_byte_arr, format='JPEG') | |
| img_byte_arr.seek(0) | |
| url = f"{PLANTNET_CONFIG['base_url']}?api-key={PLANTNET_CONFIG['api_key']}&lang={PLANTNET_CONFIG['lang']}" | |
| files = {'images': ('image.jpg', img_byte_arr, 'image/jpeg')} | |
| data = {'organs': 'flower'} | |
| response = requests.post(url, files=files, data=data, timeout=PLANTNET_CONFIG['timeout']) | |
| if response.status_code == 200: | |
| return parse_plantnet_response(response.json(), flower_name) | |
| else: | |
| print(f"Error en PlantNet: {response.status_code}") | |
| return get_fallback_info(flower_name) | |
| except requests.exceptions.Timeout: | |
| print("Tiempo de espera agotado en PlantNet") | |
| return get_fallback_info(flower_name) | |
| except Exception as e: | |
| print(f"PlantNet error: {e}") | |
| return get_fallback_info(flower_name) | |
| def parse_plantnet_response(data, flower_name): | |
| if 'results' in data and len(data['results']) > 0: | |
| results = data['results'][:3] | |
| info = f""" | |
| <div class="bg-[rgba(255,255,255,0.1)] border-2 border-[rgba(255,165,0,0.3)] rounded-2xl p-5 m-[15px_0] shadow-[0_8px_32px_rgba(0,0,0,0.2)]"> | |
| <div class="flex items-center justify-center"> | |
| <h2 class="text-yellow-400 font-bold text-2xl">🌸 {flower_name}</h2> | |
| </div> | |
| <div> | |
| <h3 class="text-yellow-400 font-bold text-xl mt-4">📊 Identificación Científica</h3> | |
| """ | |
| for i, result in enumerate(results, 1): | |
| species = result.get('species', {}) | |
| scientific_name = species.get('scientificNameWithoutAuthor', 'N/A') | |
| authorship = species.get('scientificNameAuthorship', '') | |
| common_names = species.get('commonNames', []) | |
| family = species.get('family', {}).get('scientificNameWithoutAuthor', 'N/A') | |
| genus = species.get('genus', {}).get('scientificNameWithoutAuthor', 'N/A') | |
| score = result.get('score', 0) | |
| common_names_str = ', '.join(common_names[:3]) if common_names else 'No disponible' | |
| info += f""" | |
| <div class="flex items-start mt-4"> | |
| <div class="text-black mr-4">#{i}</div> | |
| <div> | |
| <h4 class="text-black font-semibold">{scientific_name} {authorship}</h4> | |
| <div class="relative h-2 bg-gray-200 rounded mt-2"> | |
| <div class="absolute h-full bg-yellow-400 rounded" style="width: {score*100}%"></div> | |
| <span class="text-black text-sm mt-2 block">{score:.1%} confianza</span> | |
| </div> | |
| <div class="grid grid-cols-1 gap-2 mt-2"> | |
| <div class="text-black"><strong>Nombres comunes:</strong> {common_names_str}</div> | |
| <div class="text-black"><strong>Familia:</strong> {family}</div> | |
| <div class="text-black"><strong>Género:</strong> {genus}</div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| info += """ | |
| </div> | |
| <div class="mt-6"> | |
| <h3 class="text-yellow-400 font-bold text-xl">🌿 Cuidados Generales</h3> | |
| <div class="grid grid-cols-2 gap-4 mt-2"> | |
| <div class="flex items-start"><div class="text-2xl mr-2">☀️</div><div class="text-black"><strong>Luz:</strong> Luz solar directa o indirecta</div></div> | |
| <div class="flex items-start"><div class="text-2xl mr-2">💧</div><div class="text-black"><strong>Riego:</strong> Mantener húmedo, evitar exceso</div></div> | |
| <div class="flex items-start"><div class="text-2xl mr-2">🌡️</div><div class="text-black"><strong>Temperatura:</strong> Evitar cambios bruscos</div></div> | |
| <div class="flex items-start"><div class="text-2xl mr-2">🌱</div><div class="text-black"><strong>Suelo:</strong> Bien drenado y rico en nutrientes</div></div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| return info | |
| return get_fallback_info(flower_name) | |
| def get_fallback_info(flower_name): | |
| return f""" | |
| <div class="bg-[rgba(255,255,255,0.1)] border-2 border-[rgba(255,165,0,0.3)] rounded-2xl p-5 m-[15px_0] shadow-[0_8px_32px_rgba(0,0,0,0.2)]"> | |
| <div class="flex items-center justify-between"> | |
| <h2 class="text-yellow-400 font-bold text-2xl">🌸 {flower_name}</h2> | |
| <div class="bg-red-600 text-black px-2 py-1 rounded">PlantNet no disponible</div> | |
| </div> | |
| <div class="mt-4"> | |
| <h3 class="text-yellow-400 font-bold text-xl">📖 Información General</h3> | |
| <p class="text-black">Identificado por nuestro modelo de IA entrenado en el conjunto de datos Oxford 102 Flowers.</p> | |
| <div class="grid grid-cols-2 gap-4 mt-4"> | |
| <div class="text-black"> | |
| <h4 class="font-semibold">🌺 Características</h4> | |
| <ul class="list-disc ml-5"> | |
| <li>Estructuras reproductivas de la planta</li> | |
| <li>Varios colores y formas</li> | |
| <li>Evolucionadas para atraer polinizadores</li> | |
| </ul> | |
| </div> | |
| <div class="text-black"> | |
| <h4 class="font-semibold">🎯 Cuidados Básicos</h4> | |
| <ul class="list-disc ml-5"> | |
| <li>Buena iluminación según la especie</li> | |
| <li>Riego regular sin exceso</li> | |
| <li>Temperatura estable</li> | |
| <li>Fertilización adecuada</li> | |
| </ul> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| # ===== LÓGICA DE PREDICCIÓN ===== | |
| def predict(image): | |
| if not image: | |
| return "No se cargó ninguna imagen", "0%", """ | |
| <div class="bg-red-600 text-black rounded-2xl p-5 m-[15px_0]"> | |
| <h3 class="text-xl font-bold">⚠️ Imagen requerida</h3> | |
| <p>Por favor, carga una imagen de una flor para iniciar la identificación.</p> | |
| </div> | |
| """ | |
| if not MODEL_LOADED: | |
| return "Error en el modelo", "0%", """ | |
| <div class="bg-red-600 text-black rounded-2xl p-5 m-[15px_0]"> | |
| <h3 class="text-xl font-bold">🚫 Modelo no disponible</h3> | |
| <p>No se pudo cargar el modelo de clasificación. Verifica los archivos del modelo.</p> | |
| </div> | |
| """ | |
| try: | |
| img_array = preprocess_image(image) | |
| preds = model.predict(img_array) | |
| class_idx = np.argmax(preds[0]) | |
| confidence = preds[0][class_idx] | |
| label_name = labels[class_idx] | |
| flower_details = get_flower_info_from_plantnet(label_name, img_array) | |
| return label_name, f"{confidence:.2%}", flower_details | |
| except Exception as e: | |
| return "Error", "0%", f""" | |
| <div class="bg-red-600 text-black rounded-2xl p-5 m-[15px_0]"> | |
| <h3 class="text-xl font-bold">❌ Error de predicción</h3> | |
| <p>Error durante el procesamiento: {str(e)}</p> | |
| </div> | |
| """ | |
| # ===== CSS PARA EL FONDO DE LA APLICACIÓN ===== | |
| custom_css = """ | |
| .gradio-container { | |
| background: #1E2A44; | |
| min-height: 100vh; | |
| } | |
| """ | |
| # ===== JAVASCRIPT PARA OPTIMIZACIÓN DE CÁMARA ===== | |
| camera_js_improved = """ | |
| <script> | |
| function initCameraOptimization() { | |
| function ensureButtonsVisible() { | |
| document.querySelectorAll('[data-testid="image"]').forEach(container => { | |
| container.querySelectorAll('button').forEach(button => { | |
| button.style.display = 'flex'; | |
| button.style.visibility = 'visible'; | |
| button.style.opacity = '1'; | |
| button.style.zIndex = '999'; | |
| }); | |
| }); | |
| } | |
| ensureButtonsVisible(); | |
| setInterval(ensureButtonsVisible, 5000); | |
| } | |
| if (document.readyState === 'loading') { | |
| document.addEventListener('DOMContentLoaded', initCameraOptimization); | |
| } else { | |
| initCameraOptimization(); | |
| } | |
| </script> | |
| """ | |
| # ===== APLICACIÓN PRINCIPAL ===== | |
| # Cargar el modelo al inicio | |
| load_model() | |
| img3_b64 = image_to_base64("img3.png") | |
| img2_b64 = image_to_base64("img2.png") | |
| with gr.Blocks(theme=gr.themes.Soft(), css=custom_css, title="🌸 Flower ") as demo: | |
| header_html = f''' | |
| <div style="background: rgba(37, 58, 105); border-radius: 16px; padding: 16px; margin-bottom: 24px; border: 1px solid #e5e7eb; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); max-width: 1024px; margin-left: auto; margin-right: auto;"> | |
| <div style="display: flex; align-items: center; justify-content: space-between; gap: 16px;"> | |
| {'<div style="flex-shrink: 0; width: 150px; height: 150px;"><img src="data:image/png;base64,' + img3_b64 + '" alt="Logo" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"></div>' if img3_b64 else '<div style="width: 80px; height: 80px;"></div>'} | |
| <div style="text-align: center;"> | |
| <h1 style="color: #FFCC00; font-size: 24px; font-weight: bold; margin-bottom: 4px;">IDENTIFICADOR DE FLORES</h1> | |
| <p style="color: #FFFFFF; font-weight: 500; font-size: 14px; margin-bottom: 4px;">Identifica cualquier flor en cuestión de segundos</p> | |
| <p style="color: #22c55e; font-size: 12px;">Oxford 102 Flowers + PlantNet</p> | |
| </div> | |
| {'<div style="flex-shrink: 0; width: 150px; height: 150px;"><img src="data:image/png;base64,' + img2_b64 + '" alt="Logo" style="width: 100%; height: 100%; object-fit: contain; border-radius: 8px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);"></div>' if img2_b64 else '<div style="width: 48px; height: 48px;"></div>'} | |
| </div> | |
| </div> | |
| ''' | |
| gr.HTML(header_html) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| image_input = gr.Image(type="pil", label="📷 Cargar Imagen de Flor", height=350, sources=["upload", "webcam", "clipboard"]) | |
| predict_btn = gr.Button("🔍 Identificar Flor", variant="primary") | |
| gr.HTML(f'<div class="bg-[rgba(255,255,255,0.1)] border-2 border-[rgba(255,165,0,0.3)] rounded-2xl p-5 m-[15px_0] shadow-[0_8px_32px_rgba(0,0,0,0.2)]"><h3 class="text-yellow-400 font-bold text-xl">📊 Estado del Sistema</h3><p class="text-black">Modelo: {"✅ Activo" if MODEL_LOADED else "❌ Error"}</p><p class="text-black">Clases: {len(labels)}</p></div>') | |
| with gr.Column(scale=1): | |
| result_label = gr.Textbox(label="🌼 Flor Identificada", interactive=False, placeholder="El nombre de la flor aparecerá aquí...") | |
| result_conf = gr.Textbox(label="📊 Confianza", interactive=False, placeholder="El nivel de confianza aparecerá aquí...") | |
| flower_info_output = gr.HTML(value='<div class="bg-[rgba(255,255,255,0.1)] border-2 border-[rgba(255,165,0,0.3)] rounded-2xl p-5 m-[15px_0] shadow-[0_8px_32px_rgba(0,0,0,0.2)]"><h3 class="text-yellow-400 font-bold text-xl">🌸 ¡Bienvenido!</h3><p class="text-black">Carga una imagen de una flor para iniciar la identificación.</p></div>') | |
| predict_btn.click(fn=predict, inputs=image_input, outputs=[result_label, result_conf, flower_info_output]) | |
| gr.HTML(camera_js_improved) | |
| if __name__ == "__main__": | |
| demo.launch(share=False) |