DHEIVER's picture
Update app.py
a75511c verified
raw
history blame
13.9 kB
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