Spaces:
Running
Running
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() | |
] | |
) | |
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""" | |
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 | |
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 |