import gradio as gr import cv2 import numpy as np from PIL import Image from dataclasses import dataclass from typing import Tuple, Dict, List import torch import torch.nn.functional as F import torchvision.transforms as transforms import logging import os from datetime import datetime # Configuração de logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.FileHandler('iridology_analyzer.log'), logging.StreamHandler() ] ) @dataclass class IrisZone: """Classe para definir as características de uma zona da íris""" name: str inner_ratio: float outer_ratio: float color: Tuple[int, int, int] angle_start: float = 0 angle_end: float = 360 def __post_init__(self): """Validação dos parâmetros da zona""" if not 0 <= self.inner_ratio <= 1: raise ValueError("inner_ratio deve estar entre 0 e 1") if not 0 <= self.outer_ratio <= 1: raise ValueError("outer_ratio deve estar entre 0 e 1") if self.inner_ratio >= self.outer_ratio: raise ValueError("inner_ratio deve ser menor que outer_ratio") class ImageProcessor: """Classe para processamento básico de imagens""" @staticmethod def enhance_image(img: np.ndarray) -> np.ndarray: """Melhora a qualidade da imagem""" # Converter para LAB lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB) l, a, b = cv2.split(lab) # Aplicar CLAHE no canal L clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8)) l = clahe.apply(l) # Merge canais lab = cv2.merge((l,a,b)) # Converter de volta para RGB enhanced = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB) return enhanced @staticmethod def reduce_noise(img: np.ndarray) -> np.ndarray: """Reduz ruído na imagem""" return cv2.fastNlMeansDenoisingColored(img, None, 10, 10, 7, 21) class IrisAnalyzer: """Classe principal para análise da íris""" def __init__(self): self.zones = [ IrisZone("Zona Cerebral/Neural", 0.85, 1.0, (255, 0, 0)), IrisZone("Zona Digestiva", 0.7, 0.85, (0, 255, 0)), IrisZone("Zona Respiratória", 0.55, 0.7, (0, 0, 255)), IrisZone("Zona Circulatória", 0.4, 0.55, (255, 255, 0)), IrisZone("Zona Linfática", 0.25, 0.4, (255, 0, 255)), IrisZone("Zona Endócrina", 0.15, 0.25, (0, 255, 255)), IrisZone("Zona Pupilar", 0, 0.15, (128, 128, 128)) ] self.image_processor = ImageProcessor() logging.info("IrisAnalyzer inicializado com %d zonas", len(self.zones)) def preprocess_image(self, img: np.ndarray) -> np.ndarray: """Pré-processa a imagem para análise""" try: # Melhorar qualidade enhanced = self.image_processor.enhance_image(img) # Reduzir ruído denoised = self.image_processor.reduce_noise(enhanced) # Converter para escala de cinza gray = cv2.cvtColor(denoised, cv2.COLOR_RGB2GRAY) # Equalização adaptativa clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)) preprocessed = clahe.apply(gray) logging.info("Pré-processamento de imagem concluído com sucesso") return preprocessed except Exception as e: logging.error("Erro no pré-processamento: %s", str(e)) raise def detect_pupil(self, img: np.ndarray) -> Tuple[int, int, int]: """Detecta a pupila na imagem""" try: preprocessed = self.preprocess_image(img) # Threshold adaptativo _, thresh = cv2.threshold(preprocessed, 30, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) # Operações morfológicas kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5)) morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel) # Encontrar contornos contours, _ = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) if not contours: logging.warning("Nenhum contorno encontrado na imagem") return None # Encontrar o contorno mais circular max_circularity = 0 best_contour = None for contour in contours: area = cv2.contourArea(contour) perimeter = cv2.arcLength(contour, True) if perimeter == 0: continue circularity = 4 * np.pi * area / (perimeter * perimeter) if circularity > max_circularity: max_circularity = circularity best_contour = contour if best_contour is None: logging.warning("Nenhum contorno circular encontrado") return None # Ajustar círculo (x, y), radius = cv2.minEnclosingCircle(best_contour) logging.info("Pupila detectada em (x=%d, y=%d) com raio=%d", int(x), int(y), int(radius)) return (int(x), int(y), int(radius)) except Exception as e: logging.error("Erro na detecção da pupila: %s", str(e)) raise def analyze_zone(self, img: np.ndarray, mask: np.ndarray) -> Dict: """Analisa características de uma zona específica""" try: # Extrair características da região mean_color = cv2.mean(img, mask=mask) # Calcular textura gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) zone_pixels = cv2.bitwise_and(gray, gray, mask=mask) if np.sum(mask) > 0: std_dev = np.std(zone_pixels[mask > 0]) mean_intensity = np.mean(zone_pixels[mask > 0]) # Calcular características adicionais non_zero = zone_pixels[mask > 0] percentiles = np.percentile(non_zero, [25, 50, 75]) analysis = { 'mean_intensity': mean_intensity, 'std_dev': std_dev, 'color_variation': mean_color[:3], 'quartiles': percentiles.tolist(), 'pixel_count': len(non_zero) } else: analysis = { 'mean_intensity': 0, 'std_dev': 0, 'color_variation': (0,0,0), 'quartiles': [0,0,0], 'pixel_count': 0 } return analysis except Exception as e: logging.error("Erro na análise da zona: %s", str(e)) raise def interpret_zone(self, analysis: Dict) -> str: """Interpreta os resultados da análise de uma zona""" try: intensity = analysis['mean_intensity'] variation = analysis['std_dev'] # Classificação básica if intensity < 85: base_condition = "Possível área de atenção" confidence = "baixa" if variation > 30 else "média" elif intensity < 170: base_condition = "Área moderada" confidence = "média" if variation > 20 else "alta" else: base_condition = "Área normal" confidence = "alta" if variation < 15 else "média" # Adicionar detalhes estatísticos detail = (f"(Intensidade: {intensity:.1f}, " f"Variação: {variation:.1f}, " f"Confiança: {confidence})") return f"{base_condition} {detail}" except Exception as e: logging.error("Erro na interpretação da zona: %s", str(e)) raise def analyze_iris(self, img: np.ndarray) -> Tuple[np.ndarray, Dict]: """Análise principal da íris""" try: # Criar cópia para desenho output_img = img.copy() results = {} # Detectar pupila pupil = self.detect_pupil(img) if pupil is None: return img, {"Erro": "Não foi possível detectar a pupila"} x, y, pupil_radius = pupil # Estimar raio da íris iris_radius = pupil_radius * 4 # Desenhar círculo da pupila cv2.circle(output_img, (x, y), pupil_radius, (0, 0, 0), 2) # Analisar cada zona for zone in self.zones: inner_r = int(iris_radius * zone.inner_ratio) outer_r = int(iris_radius * zone.outer_ratio) # Criar máscara para a zona mask = np.zeros(img.shape[:2], dtype=np.uint8) cv2.circle(mask, (x, y), outer_r, 255, -1) cv2.circle(mask, (x, y), inner_r, 0, -1) # Desenhar círculos da zona cv2.circle(output_img, (x, y), outer_r, zone.color, 2) # Analisar zona analysis = self.analyze_zone(img, mask) interpretation = self.interpret_zone(analysis) results[zone.name] = interpretation # Adicionar texto text_x = x - iris_radius text_y = y + outer_r cv2.putText(output_img, zone.name, (text_x, text_y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, zone.color, 1) logging.info("Análise completa realizada com sucesso") return output_img, results except Exception as e: logging.error("Erro na análise da íris: %s", str(e)) return img, {"Erro": f"Erro na análise: {str(e)}"} def process_image(img): """Função principal para processar imagem via Gradio""" try: analyzer = IrisAnalyzer() return analyzer.analyze_iris(np.array(img)) except Exception as e: logging.error("Erro no processamento da imagem: %s", str(e)) return img, {"Erro": str(e)} # Interface Gradio atualizada with gr.Blocks(theme=gr.themes.Soft()) as iface: gr.Markdown(""" # 🔍 Analisador Avançado de Íris ### Baseado na teoria de Jensen para análise de zonas da íris ⚠️ **AVISO**: Esta é uma demonstração educacional. Não use para diagnósticos médicos. """) with gr.Row(): with gr.Column(scale=1): # Corrigido o componente Image removendo o parâmetro 'tool' input_image = gr.Image( label="Carregue a imagem do olho", type="numpy", height=300, sources=["upload", "clipboard"] # Especifica as fontes de entrada permitidas ) analyze_btn = gr.Button( "📸 Analisar Imagem", variant="primary", scale=1 ) gr.Markdown(""" ### Como usar: 1. Faça upload de uma imagem clara do olho 2. Certifique-se que a íris está bem visível 3. Clique em "Analisar Imagem" 4. Veja os resultados à direita """) with gr.Column(scale=1): output_image = gr.Image( label="Análise Visual", height=300 ) with gr.Accordion("📊 Resultados Detalhados", open=True): results = gr.JSON(label="") with gr.Row(): gr.Markdown(""" ### Legenda das Zonas: - 🔴 Zona Cerebral/Neural - 🟢 Zona Digestiva - 🔵 Zona Respiratória - 🟡 Zona Circulatória - 🟣 Zona Linfática - 🔰 Zona Endócrina - ⚪ Zona Pupilar """) # Configurar o fluxo de análise analyze_btn.click( fn=process_image, inputs=input_image, outputs=[output_image, results], ) gr.Markdown(""" --- ### ℹ️ Informações Importantes Este aplicativo utiliza técnicas avançadas de processamento de imagem para: - Detectar automaticamente a pupila - Segmentar as zonas da íris - Analisar padrões de cor e textura - Gerar relatório detalhado por zona **Recursos Técnicos:** - Processamento de imagem adaptativo - Detecção automática de pupila - Análise de textura e padrões - Sistema de logging para rastreamento - Tratamento de erros robusto **Lembre-se**: A iridologia é considerada uma prática alternativa e não é reconhecida pela medicina convencional como método válido de diagnóstico. """) # Configuração de logging logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[ logging.StreamHandler() ] ) if __name__ == "__main__": try: iface.launch( share=True, server_name="0.0.0.0", server_port=7860, enable_queue=True ) logging.info("Aplicação iniciada com sucesso") except Exception as e: logging.error(f"Erro ao iniciar a aplicação: {str(e)}") raise