Spaces:
Sleeping
Sleeping
Rename app.py to analisador_iridologico.py
Browse files- app.py → analisador_iridologico.py +30 -510
app.py → analisador_iridologico.py
RENAMED
@@ -1,4 +1,3 @@
|
|
1 |
-
import gradio as gr
|
2 |
import cv2
|
3 |
import numpy as np
|
4 |
from PIL import Image
|
@@ -11,13 +10,13 @@ import torch
|
|
11 |
|
12 |
class AnalisadorIridologicoNLP:
|
13 |
def __init__(self):
|
14 |
-
# Usando o modelo multilingual BERT para português
|
15 |
modelo = "neuralmind/bert-base-portuguese-cased"
|
16 |
self.tokenizer = AutoTokenizer.from_pretrained(modelo)
|
17 |
self.model = AutoModelForSequenceClassification.from_pretrained(modelo)
|
18 |
-
|
19 |
-
|
20 |
-
|
|
|
21 |
'pupila': {
|
22 |
'tamanho': {
|
23 |
'grande': 'Indica possível estresse do sistema nervoso ou fadiga adrenal',
|
@@ -54,11 +53,8 @@ class AnalisadorIridologicoNLP:
|
|
54 |
}
|
55 |
}
|
56 |
}
|
57 |
-
|
58 |
def classificar_caracteristica(self, valor, tipo, subtipo):
|
59 |
-
"""
|
60 |
-
Classifica uma característica específica baseada em thresholds
|
61 |
-
"""
|
62 |
if tipo == 'pupila':
|
63 |
if subtipo == 'tamanho':
|
64 |
if valor < 25: return 'pequena'
|
@@ -66,7 +62,6 @@ class AnalisadorIridologicoNLP:
|
|
66 |
else: return 'normal'
|
67 |
elif subtipo == 'forma':
|
68 |
return 'regular' if valor > 0.85 else 'irregular'
|
69 |
-
|
70 |
elif tipo == 'iris':
|
71 |
if subtipo == 'densidade':
|
72 |
if valor < 0.4: return 'baixa'
|
@@ -76,7 +71,6 @@ class AnalisadorIridologicoNLP:
|
|
76 |
if valor < 0.3: return 'irregular'
|
77 |
elif valor > 0.6: return 'homogenea'
|
78 |
else: return 'mista'
|
79 |
-
|
80 |
elif tipo == 'collarette':
|
81 |
if subtipo == 'regularidade':
|
82 |
if valor < 300: return 'alta'
|
@@ -86,16 +80,10 @@ class AnalisadorIridologicoNLP:
|
|
86 |
if valor < 0.7: return 'baixa'
|
87 |
elif valor > 0.9: return 'alta'
|
88 |
else: return 'media'
|
89 |
-
|
90 |
return 'indefinido'
|
91 |
-
|
92 |
def gerar_interpretacao(self, metricas):
|
93 |
-
"""
|
94 |
-
Gera uma interpretação em linguagem natural das métricas
|
95 |
-
"""
|
96 |
interpretacao = []
|
97 |
-
|
98 |
-
# Análise da pupila
|
99 |
if 'pupila' in metricas:
|
100 |
tamanho = self.classificar_caracteristica(
|
101 |
metricas['pupila']['raio'],
|
@@ -107,11 +95,9 @@ class AnalisadorIridologicoNLP:
|
|
107 |
'pupila',
|
108 |
'forma'
|
109 |
)
|
110 |
-
|
111 |
interpretacao.append(f"Pupila: {self.referencias['pupila']['tamanho'][tamanho]}")
|
112 |
interpretacao.append(f"Forma pupilar: {self.referencias['pupila']['forma'][forma]}")
|
113 |
-
|
114 |
-
# Análise da íris
|
115 |
if 'iris' in metricas:
|
116 |
densidade = self.classificar_caracteristica(
|
117 |
metricas['iris']['densidade_media'],
|
@@ -123,11 +109,9 @@ class AnalisadorIridologicoNLP:
|
|
123 |
'iris',
|
124 |
'textura'
|
125 |
)
|
126 |
-
|
127 |
interpretacao.append(f"Íris: {self.referencias['iris']['densidade'][densidade]}")
|
128 |
interpretacao.append(f"Textura: {self.referencias['iris']['textura'][textura]}")
|
129 |
-
|
130 |
-
# Análise do collarette
|
131 |
if 'collarette' in metricas:
|
132 |
regularidade = self.classificar_caracteristica(
|
133 |
metricas['collarette']['regularidade'],
|
@@ -139,14 +123,10 @@ class AnalisadorIridologicoNLP:
|
|
139 |
'collarette',
|
140 |
'circularidade'
|
141 |
)
|
142 |
-
|
143 |
interpretacao.append(f"Collarette: {self.referencias['collarette']['regularidade'][regularidade]}")
|
144 |
interpretacao.append(f"Estrutura: {self.referencias['collarette']['circularidade'][circularidade]}")
|
145 |
-
|
146 |
-
# Gerar texto completo
|
147 |
texto_interpretacao = "\n".join(interpretacao)
|
148 |
-
|
149 |
-
# Usar o modelo BERT para refinar a linguagem
|
150 |
inputs = self.tokenizer(
|
151 |
texto_interpretacao,
|
152 |
return_tensors="pt",
|
@@ -154,94 +134,44 @@ class AnalisadorIridologicoNLP:
|
|
154 |
truncation=True,
|
155 |
max_length=512
|
156 |
)
|
157 |
-
|
158 |
with torch.no_grad():
|
159 |
outputs = self.model(**inputs)
|
160 |
refined_text = self.refinar_texto(texto_interpretacao, outputs.logits)
|
161 |
-
|
162 |
return refined_text
|
163 |
-
|
164 |
def refinar_texto(self, texto, logits):
|
165 |
-
"""
|
166 |
-
Refina o texto usando as logits do modelo
|
167 |
-
"""
|
168 |
sentencas = texto.split("\n")
|
169 |
-
refined_sentencas = []
|
170 |
-
|
171 |
-
for sentenca in sentencas:
|
172 |
-
if len(sentenca.strip()) > 0:
|
173 |
-
refined_sentencas.append(f"• {sentenca}")
|
174 |
-
|
175 |
return "\n".join(refined_sentencas)
|
176 |
|
177 |
-
|
178 |
-
"""
|
179 |
-
Integra a análise NLP ao sistema existente
|
180 |
-
"""
|
181 |
-
if analisador is None:
|
182 |
-
analisador = AnalisadorIridologicoNLP()
|
183 |
-
|
184 |
-
return analisador.gerar_interpretacao(metricas)
|
185 |
-
|
186 |
def pre_processar_imagem(imagem):
|
187 |
-
"""
|
188 |
-
Pré-processamento avançado da imagem
|
189 |
-
"""
|
190 |
-
# Converter para LAB para melhor separação de cores
|
191 |
lab = cv2.cvtColor(imagem, cv2.COLOR_RGB2LAB)
|
192 |
l, a, b = cv2.split(lab)
|
193 |
-
|
194 |
-
# Aplicar CLAHE no canal L
|
195 |
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
|
196 |
l = clahe.apply(l)
|
197 |
-
|
198 |
-
# Recombinar canais
|
199 |
lab = cv2.merge((l,a,b))
|
200 |
-
|
201 |
-
# Converter de volta para RGB
|
202 |
imagem_melhorada = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)
|
203 |
-
|
204 |
-
# Redução de ruído
|
205 |
imagem_melhorada = cv2.GaussianBlur(imagem_melhorada, (5, 5), 0)
|
206 |
-
|
207 |
return imagem_melhorada
|
208 |
|
209 |
def detectar_esclera(imagem):
|
210 |
-
"""
|
211 |
-
Detecta a região da esclera usando segmentação por cor e morfologia
|
212 |
-
"""
|
213 |
-
# Converter para HSV
|
214 |
hsv = cv2.cvtColor(imagem, cv2.COLOR_RGB2HSV)
|
215 |
-
|
216 |
-
# Definir faixa de cor para branco (esclera)
|
217 |
lower_white = np.array([0, 0, 180])
|
218 |
upper_white = np.array([180, 30, 255])
|
219 |
-
|
220 |
-
# Criar máscara
|
221 |
mask_esclera = cv2.inRange(hsv, lower_white, upper_white)
|
222 |
-
|
223 |
-
# Operações morfológicas para limpar
|
224 |
kernel = np.ones((5,5), np.uint8)
|
225 |
mask_esclera = cv2.morphologyEx(mask_esclera, cv2.MORPH_OPEN, kernel)
|
226 |
mask_esclera = cv2.morphologyEx(mask_esclera, cv2.MORPH_CLOSE, kernel)
|
227 |
-
|
228 |
return mask_esclera
|
229 |
|
230 |
def detectar_iris_pupila(imagem, mask_esclera):
|
231 |
-
"""
|
232 |
-
Detecta íris e pupila usando múltiplas técnicas
|
233 |
-
"""
|
234 |
-
# Converter para escala de cinza
|
235 |
gray = cv2.cvtColor(imagem, cv2.COLOR_RGB2GRAY)
|
236 |
-
|
237 |
-
# Aplicar máscara da esclera invertida
|
238 |
mask_olho = cv2.bitwise_not(mask_esclera)
|
239 |
eye_region = cv2.bitwise_and(gray, gray, mask=mask_olho)
|
240 |
-
|
241 |
-
# Detectar bordas
|
242 |
edges = cv2.Canny(eye_region, 30, 60)
|
243 |
-
|
244 |
-
# Detectar círculos para íris
|
245 |
iris_circles = cv2.HoughCircles(
|
246 |
edges,
|
247 |
cv2.HOUGH_GRADIENT,
|
@@ -252,18 +182,13 @@ def detectar_iris_pupila(imagem, mask_esclera):
|
|
252 |
minRadius=80,
|
253 |
maxRadius=150
|
254 |
)
|
255 |
-
|
256 |
-
# Criar máscara da íris
|
257 |
if iris_circles is not None:
|
258 |
iris_circles = np.uint16(np.around(iris_circles))
|
259 |
ix, iy, ir = iris_circles[0][0]
|
260 |
mask_iris = np.zeros_like(gray)
|
261 |
cv2.circle(mask_iris, (ix, iy), ir, 255, -1)
|
262 |
-
|
263 |
-
# Região dentro da íris para detecção da pupila
|
264 |
iris_region = cv2.bitwise_and(gray, gray, mask=mask_iris)
|
265 |
-
|
266 |
-
# Threshold adaptativo para pupila
|
267 |
thresh = cv2.adaptiveThreshold(
|
268 |
iris_region,
|
269 |
255,
|
@@ -272,8 +197,6 @@ def detectar_iris_pupila(imagem, mask_esclera):
|
|
272 |
11,
|
273 |
2
|
274 |
)
|
275 |
-
|
276 |
-
# Detectar pupila
|
277 |
pupil_circles = cv2.HoughCircles(
|
278 |
thresh,
|
279 |
cv2.HOUGH_GRADIENT,
|
@@ -284,49 +207,33 @@ def detectar_iris_pupila(imagem, mask_esclera):
|
|
284 |
minRadius=20,
|
285 |
maxRadius=50
|
286 |
)
|
287 |
-
|
288 |
if pupil_circles is not None:
|
289 |
pupil_circles = np.uint16(np.around(pupil_circles))
|
290 |
px, py, pr = pupil_circles[0][0]
|
291 |
return (ix, iy, ir), (px, py, pr)
|
292 |
-
|
293 |
return None, None
|
294 |
|
295 |
def analisar_textura_setorial(imagem, iris_info, pupil_info):
|
296 |
-
"""
|
297 |
-
Analisa a textura da íris por setores com correção dos níveis de cinza
|
298 |
-
"""
|
299 |
if iris_info is None or pupil_info is None:
|
300 |
return {}
|
301 |
|
302 |
-
# Ensure iris_info and pupil_info are numpy arrays
|
303 |
-
if isinstance(iris_info, tuple):
|
304 |
-
iris_info = np.array(iris_info)
|
305 |
-
if isinstance(pupil_info, tuple):
|
306 |
-
pupil_info = np.array(pupil_info)
|
307 |
-
|
308 |
ix, iy, ir = iris_info
|
309 |
px, py, pr = pupil_info
|
310 |
|
311 |
-
# Converter para escala de cinza
|
312 |
gray = cv2.cvtColor(imagem, cv2.COLOR_RGB2GRAY)
|
313 |
-
|
314 |
-
# Equalização adaptativa do histograma
|
315 |
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(4, 4))
|
316 |
gray = clahe.apply(gray)
|
317 |
|
318 |
-
# Criar máscara anelar da íris
|
319 |
mask_iris = np.zeros_like(gray)
|
320 |
cv2.circle(mask_iris, (ix, iy), int(ir * 0.98), 255, -1)
|
321 |
cv2.circle(mask_iris, (px, py), int(pr * 1.02), 0, -1)
|
322 |
|
323 |
-
# Dividir em 12 setores
|
324 |
setores = {}
|
325 |
for i in range(12):
|
326 |
ang_inicio = i * 30
|
327 |
ang_fim = (i + 1) * 30
|
328 |
-
|
329 |
-
# Criar máscara do setor
|
330 |
mask_setor = np.zeros_like(gray)
|
331 |
cv2.ellipse(mask_setor,
|
332 |
(ix, iy),
|
@@ -337,28 +244,21 @@ def analisar_textura_setorial(imagem, iris_info, pupil_info):
|
|
337 |
255,
|
338 |
-1)
|
339 |
|
340 |
-
# Combinar máscaras
|
341 |
-
kernel = np.ones((2, 2), np.uint8)
|
342 |
mask_final = cv2.bitwise_and(mask_iris, mask_setor)
|
|
|
343 |
mask_final = cv2.morphologyEx(mask_final, cv2.MORPH_OPEN, kernel)
|
344 |
-
|
345 |
-
# Extrair região do setor
|
346 |
setor_roi = cv2.bitwise_and(gray, gray, mask=mask_final)
|
347 |
|
348 |
-
# Análise de textura
|
349 |
non_zero = setor_roi[setor_roi > 0]
|
350 |
if len(non_zero) > 100:
|
351 |
-
# Normalização específica para GLCM
|
352 |
non_zero = ((non_zero - non_zero.min()) /
|
353 |
(non_zero.max() - non_zero.min() + 1e-8) * 15).astype(np.uint8)
|
354 |
|
355 |
-
# Reshape para matriz 2D
|
356 |
tamanho_janela = int(np.sqrt(len(non_zero)))
|
357 |
if tamanho_janela > 1:
|
358 |
matriz_2d = non_zero[:tamanho_janela**2].reshape(tamanho_janela, tamanho_janela)
|
359 |
|
360 |
try:
|
361 |
-
# GLCM com 16 níveis
|
362 |
glcm = graycomatrix(matriz_2d,
|
363 |
distances=[1],
|
364 |
angles=[0, np.pi/4],
|
@@ -366,7 +266,6 @@ def analisar_textura_setorial(imagem, iris_info, pupil_info):
|
|
366 |
symmetric=True,
|
367 |
normed=True)
|
368 |
|
369 |
-
# Calcular propriedades
|
370 |
contraste = np.mean(graycoprops(glcm, 'contrast'))
|
371 |
homogeneidade = np.mean(graycoprops(glcm, 'homogeneity'))
|
372 |
|
@@ -377,7 +276,6 @@ def analisar_textura_setorial(imagem, iris_info, pupil_info):
|
|
377 |
"std": float(np.std(non_zero))
|
378 |
}
|
379 |
except Exception as e:
|
380 |
-
print(f"Erro no GLCM do setor {i+1}: {str(e)}")
|
381 |
setores[f"setor_{i+1}"] = {
|
382 |
"contraste": 0.0,
|
383 |
"homogeneidade": 1.0,
|
@@ -400,428 +298,50 @@ def analisar_textura_setorial(imagem, iris_info, pupil_info):
|
|
400 |
}
|
401 |
|
402 |
return setores
|
403 |
-
|
404 |
-
def avaliar_setores(setores):
|
405 |
-
"""
|
406 |
-
Avalia os setores com limiares recalibrados baseados nos dados observados
|
407 |
-
"""
|
408 |
-
# Calcular estatísticas globais para calibração dinâmica
|
409 |
-
contrastes = [dados['contraste'] for dados in setores.values()]
|
410 |
-
homogeneidades = [dados['homogeneidade'] for dados in setores.values()]
|
411 |
-
|
412 |
-
# Calcular limiares dinâmicos
|
413 |
-
contraste_medio = np.mean(contrastes)
|
414 |
-
contraste_std = np.std(contrastes)
|
415 |
-
homog_media = np.mean(homogeneidades)
|
416 |
-
homog_std = np.std(homogeneidades)
|
417 |
-
|
418 |
-
# Definir limiares baseados nas estatísticas
|
419 |
-
limiar_contraste_alto = contraste_medio + contraste_std
|
420 |
-
limiar_contraste_baixo = contraste_medio - contraste_std
|
421 |
-
limiar_homog_baixo = homog_media - homog_std
|
422 |
-
limiar_homog_alto = homog_media + homog_std
|
423 |
-
|
424 |
-
for setor, dados in setores.items():
|
425 |
-
mensagens = []
|
426 |
-
|
427 |
-
# Análise de contraste recalibrada
|
428 |
-
if dados['contraste'] > limiar_contraste_alto:
|
429 |
-
mensagens.append("Densidade muito alta de sinais")
|
430 |
-
elif dados['contraste'] > contraste_medio:
|
431 |
-
mensagens.append("Densidade moderadamente alta de sinais")
|
432 |
-
elif dados['contraste'] < limiar_contraste_baixo:
|
433 |
-
mensagens.append("Densidade baixa de sinais")
|
434 |
-
|
435 |
-
# Análise de homogeneidade recalibrada
|
436 |
-
if dados['homogeneidade'] < limiar_homog_baixo:
|
437 |
-
mensagens.append("Alterações significativas na textura")
|
438 |
-
elif dados['homogeneidade'] < homog_media:
|
439 |
-
mensagens.append("Possíveis alterações sutis")
|
440 |
-
elif dados['homogeneidade'] > limiar_homog_alto:
|
441 |
-
mensagens.append("Textura muito homogênea")
|
442 |
-
|
443 |
-
# Análise combinada
|
444 |
-
if dados['contraste'] > limiar_contraste_alto and dados['homogeneidade'] < limiar_homog_baixo:
|
445 |
-
mensagens.append("Área que requer atenção especial")
|
446 |
-
|
447 |
-
dados['interpretacao'] = mensagens
|
448 |
-
|
449 |
-
# Adicionar métricas de referência
|
450 |
-
dados['metricas_referencia'] = {
|
451 |
-
'contraste_medio': float(contraste_medio),
|
452 |
-
'contraste_std': float(contraste_std),
|
453 |
-
'homog_media': float(homog_media),
|
454 |
-
'homog_std': float(homog_std)
|
455 |
-
}
|
456 |
-
|
457 |
-
return setores
|
458 |
-
|
459 |
-
def gerar_relatorio_setorial(setores_analisados):
|
460 |
-
"""
|
461 |
-
Gera relatório setorial com informações de referência
|
462 |
-
"""
|
463 |
-
relatorio = "\n2. ANÁLISE SETORIAL\n"
|
464 |
-
|
465 |
-
# Adicionar informações de referência
|
466 |
-
if setores_analisados and 'metricas_referencia' in list(setores_analisados.values())[0]:
|
467 |
-
ref = list(setores_analisados.values())[0]['metricas_referencia']
|
468 |
-
relatorio += "\nValores de Referência:\n"
|
469 |
-
relatorio += f"- Contraste Médio: {ref['contraste_medio']:.2f} (±{ref['contraste_std']:.2f})\n"
|
470 |
-
relatorio += f"- Homogeneidade Média: {ref['homog_media']:.2f} (±{ref['homog_std']:.2f})\n\n"
|
471 |
-
|
472 |
-
for setor, dados in setores_analisados.items():
|
473 |
-
relatorio += f"\n{setor}:\n"
|
474 |
-
relatorio += f"- Contraste: {dados['contraste']:.2f}\n"
|
475 |
-
relatorio += f"- Homogeneidade: {dados['homogeneidade']:.2f}\n"
|
476 |
-
|
477 |
-
if 'interpretacao' in dados:
|
478 |
-
for msg in dados['interpretacao']:
|
479 |
-
relatorio += f" * {msg}\n"
|
480 |
-
|
481 |
-
return relatorio
|
482 |
-
|
483 |
-
def analisar_collarette(imagem, iris_info, pupil_info):
|
484 |
-
"""
|
485 |
-
Analisa o collarette (anel de contração) em detalhes
|
486 |
-
"""
|
487 |
-
if iris_info is None or pupil_info is None:
|
488 |
-
return None
|
489 |
-
|
490 |
-
ix, iy, ir = iris_info
|
491 |
-
px, py, pr = pupil_info
|
492 |
-
|
493 |
-
# Distância entre pupila e íris
|
494 |
-
dist = ir - pr
|
495 |
-
|
496 |
-
# Região do collarette (aproximadamente 35% da distância)
|
497 |
-
collarette_inner = pr + int(dist * 0.25)
|
498 |
-
collarette_outer = pr + int(dist * 0.45)
|
499 |
-
|
500 |
-
# Criar máscara do collarette
|
501 |
-
mask = np.zeros_like(cv2.cvtColor(imagem, cv2.COLOR_RGB2GRAY))
|
502 |
-
cv2.circle(mask, (px, py), collarette_outer, 255, -1)
|
503 |
-
cv2.circle(mask, (px, py), collarette_inner, 0, -1)
|
504 |
-
|
505 |
-
# Extrair região do collarette
|
506 |
-
collarette_region = cv2.bitwise_and(imagem, imagem, mask=mask)
|
507 |
-
|
508 |
-
# Análise detalhada
|
509 |
-
gray_collarette = cv2.cvtColor(collarette_region, cv2.COLOR_RGB2GRAY)
|
510 |
-
non_zero = gray_collarette[gray_collarette != 0]
|
511 |
-
|
512 |
-
if len(non_zero) > 0:
|
513 |
-
# Calcular características
|
514 |
-
distances = [1]
|
515 |
-
angles = [0]
|
516 |
-
glcm = graycomatrix(non_zero.reshape(-1, 1), distances, angles,
|
517 |
-
symmetric=True, normed=True)
|
518 |
-
|
519 |
-
return {
|
520 |
-
"intensidade_media": np.mean(non_zero),
|
521 |
-
"variacao": np.std(non_zero),
|
522 |
-
"contraste": graycoprops(glcm, 'contrast')[0, 0],
|
523 |
-
"homogeneidade": graycoprops(glcm, 'homogeneity')[0, 0],
|
524 |
-
"regularidade": cv2.Laplacian(gray_collarette, cv2.CV_64F).var(),
|
525 |
-
"circularidade": avaliar_circularidade(mask)
|
526 |
-
}
|
527 |
-
|
528 |
-
return None
|
529 |
-
|
530 |
-
def avaliar_circularidade(mask):
|
531 |
-
"""
|
532 |
-
Avalia a circularidade de uma região
|
533 |
-
"""
|
534 |
-
contours, _ = cv2.findContours(mask.astype(np.uint8), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
535 |
-
if contours:
|
536 |
-
cnt = max(contours, key=cv2.contourArea)
|
537 |
-
area = cv2.contourArea(cnt)
|
538 |
-
perimeter = cv2.arcLength(cnt, True)
|
539 |
-
if perimeter > 0:
|
540 |
-
circularity = 4 * np.pi * area / (perimeter * perimeter)
|
541 |
-
return circularity
|
542 |
-
return 0
|
543 |
|
544 |
def validar_metricas(metricas):
|
545 |
-
"""
|
546 |
-
Valida e ajusta as métricas antes da interpretação
|
547 |
-
"""
|
548 |
metricas_validadas = {}
|
549 |
-
|
550 |
-
# Validar pupila
|
551 |
if 'pupila' in metricas:
|
552 |
raio = metricas['pupila'].get('raio', 0)
|
553 |
circularidade = metricas['pupila'].get('circularidade', 0)
|
554 |
|
555 |
-
# Ajustar valores inválidos
|
556 |
if raio <= 0 or raio > 100:
|
557 |
-
raio = 35
|
558 |
if circularidade <= 0 or circularidade > 1:
|
559 |
-
circularidade = 0.85
|
560 |
|
561 |
metricas_validadas['pupila'] = {
|
562 |
'raio': raio,
|
563 |
'circularidade': circularidade
|
564 |
}
|
565 |
-
|
566 |
-
# Validar íris
|
567 |
if 'iris' in metricas:
|
568 |
densidade = metricas['iris'].get('densidade_media', 0)
|
569 |
homogeneidade = metricas['iris'].get('homogeneidade', 0)
|
570 |
-
|
571 |
-
# Ajustar valores inválidos
|
572 |
if densidade < 0:
|
573 |
-
densidade = 0.5
|
574 |
if homogeneidade < 0 or homogeneidade > 1:
|
575 |
-
homogeneidade = 0.5
|
576 |
|
577 |
metricas_validadas['iris'] = {
|
578 |
'densidade_media': densidade,
|
579 |
'homogeneidade': homogeneidade
|
580 |
}
|
581 |
-
|
582 |
-
# Validar collarette
|
583 |
if 'collarette' in metricas and metricas['collarette']:
|
584 |
regularidade = metricas['collarette'].get('regularidade', 0)
|
585 |
circularidade = metricas['collarette'].get('circularidade', 0)
|
586 |
-
|
587 |
-
# Ajustar valores inválidos
|
588 |
if regularidade < 0:
|
589 |
-
regularidade = 300
|
590 |
if circularidade < 0 or circularidade > 1:
|
591 |
-
circularidade = 0.85
|
592 |
|
593 |
metricas_validadas['collarette'] = {
|
594 |
'regularidade': regularidade,
|
595 |
'circularidade': circularidade
|
596 |
}
|
597 |
-
|
598 |
-
return metricas_validadas
|
599 |
-
|
600 |
-
def criar_interface():
|
601 |
-
"""
|
602 |
-
Cria interface moderna do Gradio
|
603 |
-
"""
|
604 |
-
theme = gr.themes.Soft(
|
605 |
-
primary_hue="teal",
|
606 |
-
secondary_hue="green",
|
607 |
-
).set(
|
608 |
-
body_text_color="#2A9D8F",
|
609 |
-
block_title_text_color="#264653",
|
610 |
-
block_label_text_color="#2A9D8F",
|
611 |
-
input_background_fill="#E9F5F3",
|
612 |
-
button_primary_background_fill="#2A9D8F",
|
613 |
-
button_primary_background_fill_dark="#264653",
|
614 |
-
)
|
615 |
|
616 |
-
|
617 |
-
try:
|
618 |
-
# Pré-processamento da imagem
|
619 |
-
imagem_processada = pre_processar_imagem(imagem)
|
620 |
-
|
621 |
-
# Detectar esclera
|
622 |
-
mask_esclera = detectar_esclera(imagem_processada)
|
623 |
-
|
624 |
-
# Detectar íris e pupila
|
625 |
-
iris_info, pupil_info = detectar_iris_pupila(imagem_processada, mask_esclera)
|
626 |
-
|
627 |
-
if iris_info is None or pupil_info is None:
|
628 |
-
return imagem, "Não foi possível detectar íris ou pupila corretamente."
|
629 |
-
|
630 |
-
# Análise de textura setorial
|
631 |
-
analise_setorial = analisar_textura_setorial(imagem_processada, iris_info, pupil_info)
|
632 |
-
|
633 |
-
# Análise do collarette
|
634 |
-
info_collarette = analisar_collarette(imagem_processada, iris_info, pupil_info)
|
635 |
-
|
636 |
-
# Criar visualização da imagem de saída
|
637 |
-
output_img = imagem.copy()
|
638 |
-
ix, iy, ir = iris_info
|
639 |
-
px, py, pr = pupil_info
|
640 |
-
|
641 |
-
# Desenhar esclera
|
642 |
-
contours, _ = cv2.findContours(mask_esclera, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
643 |
-
cv2.drawContours(output_img, contours, -1, (255, 255, 0), 1) # Esclera em amarelo
|
644 |
-
|
645 |
-
# Desenhar íris
|
646 |
-
cv2.circle(output_img, (ix, iy), ir, (0, 255, 0), 2) # Íris em verde
|
647 |
-
|
648 |
-
# Desenhar pupila
|
649 |
-
cv2.circle(output_img, (px, py), pr, (255, 0, 0), 2) # Pupila em vermelho
|
650 |
-
|
651 |
-
# Desenhar setores
|
652 |
-
for i in range(12):
|
653 |
-
ang = i * 30
|
654 |
-
rad = np.radians(ang)
|
655 |
-
end_x = int(ix + ir * np.cos(rad))
|
656 |
-
end_y = int(iy + ir * np.sin(rad))
|
657 |
-
cv2.line(output_img, (ix, iy), (end_x, end_y), (255, 255, 255), 1) # Linhas brancas para setores
|
658 |
-
|
659 |
-
# Adicionando feedback visual se o collarette for detectado
|
660 |
-
if info_collarette:
|
661 |
-
cv2.putText(output_img, "Collarette Detected", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
|
662 |
-
|
663 |
-
# Preparar métricas para análise NLP
|
664 |
-
metricas = {
|
665 |
-
'pupila': {
|
666 |
-
'raio': pr,
|
667 |
-
'circularidade': avaliar_circularidade(pupil_info)
|
668 |
-
},
|
669 |
-
'iris': {
|
670 |
-
'densidade_media': np.mean([dados['contraste'] for dados in analise_setorial.values()]),
|
671 |
-
'homogeneidade': np.mean([dados['homogeneidade'] for dados in analise_setorial.values()])
|
672 |
-
},
|
673 |
-
'collarette': info_collarette
|
674 |
-
}
|
675 |
-
|
676 |
-
# Validar métricas
|
677 |
-
metricas = validar_metricas(metricas)
|
678 |
-
|
679 |
-
# Integrar análise NLP
|
680 |
-
interpretacao_nlp = integrar_analise_nlp(metricas)
|
681 |
-
|
682 |
-
# Gerar relatório
|
683 |
-
relatorio = "ANÁLISE IRIDOLÓGICA DETALHADA\n\n"
|
684 |
-
relatorio += "1. MEDIDAS ESTRUTURAIS\n"
|
685 |
-
relatorio += f"Pupila: Centro ({px}, {py}), Raio {pr}px\n"
|
686 |
-
relatorio += f"Íris: Centro ({ix}, {iy}), Raio {ir}px\n"
|
687 |
-
|
688 |
-
# Adicionar análise setorial
|
689 |
-
relatorio += "2. ANÁLISE SETORIAL\n"
|
690 |
-
for setor, dados in analise_setorial.items():
|
691 |
-
relatorio += f"Setor {setor}: Contraste {dados['contraste']}, Homogeneidade {dados['homogeneidade']}\n"
|
692 |
-
|
693 |
-
relatorio += f"3. COLLARETTE: {info_collarette}\n"
|
694 |
-
relatorio += f"4. INTERPRETAÇÃO NLP: {interpretacao_nlp}\n"
|
695 |
-
|
696 |
-
return output_img, relatorio
|
697 |
-
|
698 |
-
except Exception as e:
|
699 |
-
return imagem, f"Erro durante o processamento: {str(e)}"
|
700 |
-
|
701 |
-
# Interface
|
702 |
-
with gr.Blocks(theme=theme, title="Análise Iridológica Avançada") as interface:
|
703 |
-
gr.Markdown("""
|
704 |
-
# Sistema Avançado de Análise Iridológica
|
705 |
-
### Detecção precisa de esclera, íris e pupila com análise setorial e interpretação em linguagem natural
|
706 |
-
""")
|
707 |
-
|
708 |
-
with gr.Tabs():
|
709 |
-
# Aba de Análise Principal
|
710 |
-
with gr.Tab("Análise de Imagem"):
|
711 |
-
with gr.Row():
|
712 |
-
with gr.Column():
|
713 |
-
input_image = gr.Image(
|
714 |
-
label="Imagem do Olho",
|
715 |
-
type="numpy"
|
716 |
-
)
|
717 |
-
with gr.Column():
|
718 |
-
output_image = gr.Image(
|
719 |
-
label="Análise Visual"
|
720 |
-
)
|
721 |
-
|
722 |
-
analysis_btn = gr.Button("Analisar Olho", variant="primary")
|
723 |
-
output_text = gr.Textbox(
|
724 |
-
label="Relatório de Análise",
|
725 |
-
lines=20
|
726 |
-
)
|
727 |
-
|
728 |
-
analysis_btn.click(
|
729 |
-
fn=processar_imagem,
|
730 |
-
inputs=[input_image],
|
731 |
-
outputs=[output_image, output_text]
|
732 |
-
)
|
733 |
-
|
734 |
-
# Aba de Configurações
|
735 |
-
with gr.Tab("Configurações"):
|
736 |
-
with gr.Row():
|
737 |
-
min_iris_radius = gr.Slider(
|
738 |
-
minimum=60,
|
739 |
-
maximum=200,
|
740 |
-
value=80,
|
741 |
-
label="Raio Mínimo da Íris (px)"
|
742 |
-
)
|
743 |
-
max_iris_radius = gr.Slider(
|
744 |
-
minimum=100,
|
745 |
-
maximum=250,
|
746 |
-
value=150,
|
747 |
-
label="Raio Máximo da Íris (px)"
|
748 |
-
)
|
749 |
-
|
750 |
-
with gr.Row():
|
751 |
-
min_pupil_radius = gr.Slider(
|
752 |
-
minimum=15,
|
753 |
-
maximum=70,
|
754 |
-
value=20,
|
755 |
-
label="Raio Mínimo da Pupila (px)"
|
756 |
-
)
|
757 |
-
max_pupil_radius = gr.Slider(
|
758 |
-
minimum=30,
|
759 |
-
maximum=100,
|
760 |
-
value=50,
|
761 |
-
label="Raio Máximo da Pupila (px)"
|
762 |
-
)
|
763 |
-
|
764 |
-
# Aba de Guia de Captura
|
765 |
-
with gr.Tab("Guia de Captura"):
|
766 |
-
gr.Markdown("""
|
767 |
-
## Guia para Captura de Imagem
|
768 |
-
|
769 |
-
### 1. Iluminação Ideal
|
770 |
-
- Luz natural indireta
|
771 |
-
- Sem reflexos diretos no olho
|
772 |
-
- Iluminação uniforme
|
773 |
-
- Evitar flash
|
774 |
-
|
775 |
-
### 2. Posicionamento
|
776 |
-
- Olho totalmente aberto
|
777 |
-
- Câmera perpendicular ao olho
|
778 |
-
- Distância adequada (15-20cm)
|
779 |
-
- Íris centralizada na imagem
|
780 |
-
|
781 |
-
### 3. Qualidade da Imagem
|
782 |
-
- Resolução mínima: 1280x720
|
783 |
-
- Foco perfeito na íris
|
784 |
-
- Sem movimento/tremor
|
785 |
-
- Imagem nítida e clara
|
786 |
-
|
787 |
-
### 4. Preparação
|
788 |
-
- Limpar a lente da câmera
|
789 |
-
- Olho descansado
|
790 |
-
- Ambiente calmo
|
791 |
-
- Múltiplas capturas
|
792 |
-
""")
|
793 |
-
|
794 |
-
# Aba de Interpretação
|
795 |
-
with gr.Tab("Guia de Interpretação"):
|
796 |
-
gr.Markdown("""
|
797 |
-
## Guia de Interpretação dos Resultados
|
798 |
-
|
799 |
-
### 1. Análise da Pupila
|
800 |
-
- **Tamanho**: Indica atividade do sistema nervoso
|
801 |
-
- **Forma**: Regular ou irregular
|
802 |
-
- **Posição**: Centralizada ou deslocada
|
803 |
-
|
804 |
-
### 2. Análise da Íris
|
805 |
-
- **Densidade**: Integridade do tecido
|
806 |
-
- **Coloração**: Atividade metabólica
|
807 |
-
- **Textura**: Estado geral dos tecidos
|
808 |
-
|
809 |
-
### 3. Sinais Específicos
|
810 |
-
- **Lacunas**: Possíveis deficiências
|
811 |
-
- **Manchas**: Toxicidade ou inflamação
|
812 |
-
- **Anéis**: Tensão ou congestão
|
813 |
-
|
814 |
-
### 4. Collarette
|
815 |
-
- **Regularidade**: Equilíbrio do sistema
|
816 |
-
- **Circularidade**: Integridade estrutural
|
817 |
-
- **Densidade**: Vitalidade geral
|
818 |
-
""")
|
819 |
-
|
820 |
-
return interface
|
821 |
-
|
822 |
-
def main():
|
823 |
-
interface = criar_interface()
|
824 |
-
interface.launch(share=True)
|
825 |
-
|
826 |
-
if __name__ == "__main__":
|
827 |
-
main()
|
|
|
|
|
1 |
import cv2
|
2 |
import numpy as np
|
3 |
from PIL import Image
|
|
|
10 |
|
11 |
class AnalisadorIridologicoNLP:
|
12 |
def __init__(self):
|
|
|
13 |
modelo = "neuralmind/bert-base-portuguese-cased"
|
14 |
self.tokenizer = AutoTokenizer.from_pretrained(modelo)
|
15 |
self.model = AutoModelForSequenceClassification.from_pretrained(modelo)
|
16 |
+
self.referencias = self._definir_referencias()
|
17 |
+
|
18 |
+
def _definir_referencias(self):
|
19 |
+
return {
|
20 |
'pupila': {
|
21 |
'tamanho': {
|
22 |
'grande': 'Indica possível estresse do sistema nervoso ou fadiga adrenal',
|
|
|
53 |
}
|
54 |
}
|
55 |
}
|
56 |
+
|
57 |
def classificar_caracteristica(self, valor, tipo, subtipo):
|
|
|
|
|
|
|
58 |
if tipo == 'pupila':
|
59 |
if subtipo == 'tamanho':
|
60 |
if valor < 25: return 'pequena'
|
|
|
62 |
else: return 'normal'
|
63 |
elif subtipo == 'forma':
|
64 |
return 'regular' if valor > 0.85 else 'irregular'
|
|
|
65 |
elif tipo == 'iris':
|
66 |
if subtipo == 'densidade':
|
67 |
if valor < 0.4: return 'baixa'
|
|
|
71 |
if valor < 0.3: return 'irregular'
|
72 |
elif valor > 0.6: return 'homogenea'
|
73 |
else: return 'mista'
|
|
|
74 |
elif tipo == 'collarette':
|
75 |
if subtipo == 'regularidade':
|
76 |
if valor < 300: return 'alta'
|
|
|
80 |
if valor < 0.7: return 'baixa'
|
81 |
elif valor > 0.9: return 'alta'
|
82 |
else: return 'media'
|
|
|
83 |
return 'indefinido'
|
84 |
+
|
85 |
def gerar_interpretacao(self, metricas):
|
|
|
|
|
|
|
86 |
interpretacao = []
|
|
|
|
|
87 |
if 'pupila' in metricas:
|
88 |
tamanho = self.classificar_caracteristica(
|
89 |
metricas['pupila']['raio'],
|
|
|
95 |
'pupila',
|
96 |
'forma'
|
97 |
)
|
|
|
98 |
interpretacao.append(f"Pupila: {self.referencias['pupila']['tamanho'][tamanho]}")
|
99 |
interpretacao.append(f"Forma pupilar: {self.referencias['pupila']['forma'][forma]}")
|
100 |
+
|
|
|
101 |
if 'iris' in metricas:
|
102 |
densidade = self.classificar_caracteristica(
|
103 |
metricas['iris']['densidade_media'],
|
|
|
109 |
'iris',
|
110 |
'textura'
|
111 |
)
|
|
|
112 |
interpretacao.append(f"Íris: {self.referencias['iris']['densidade'][densidade]}")
|
113 |
interpretacao.append(f"Textura: {self.referencias['iris']['textura'][textura]}")
|
114 |
+
|
|
|
115 |
if 'collarette' in metricas:
|
116 |
regularidade = self.classificar_caracteristica(
|
117 |
metricas['collarette']['regularidade'],
|
|
|
123 |
'collarette',
|
124 |
'circularidade'
|
125 |
)
|
|
|
126 |
interpretacao.append(f"Collarette: {self.referencias['collarette']['regularidade'][regularidade]}")
|
127 |
interpretacao.append(f"Estrutura: {self.referencias['collarette']['circularidade'][circularidade]}")
|
128 |
+
|
|
|
129 |
texto_interpretacao = "\n".join(interpretacao)
|
|
|
|
|
130 |
inputs = self.tokenizer(
|
131 |
texto_interpretacao,
|
132 |
return_tensors="pt",
|
|
|
134 |
truncation=True,
|
135 |
max_length=512
|
136 |
)
|
137 |
+
|
138 |
with torch.no_grad():
|
139 |
outputs = self.model(**inputs)
|
140 |
refined_text = self.refinar_texto(texto_interpretacao, outputs.logits)
|
141 |
+
|
142 |
return refined_text
|
143 |
+
|
144 |
def refinar_texto(self, texto, logits):
|
|
|
|
|
|
|
145 |
sentencas = texto.split("\n")
|
146 |
+
refined_sentencas = [f"• {s}" for s in sentencas if len(s.strip()) > 0]
|
|
|
|
|
|
|
|
|
|
|
147 |
return "\n".join(refined_sentencas)
|
148 |
|
149 |
+
# Funções de pré-processamento, detecção e análise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
def pre_processar_imagem(imagem):
|
|
|
|
|
|
|
|
|
151 |
lab = cv2.cvtColor(imagem, cv2.COLOR_RGB2LAB)
|
152 |
l, a, b = cv2.split(lab)
|
|
|
|
|
153 |
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
|
154 |
l = clahe.apply(l)
|
|
|
|
|
155 |
lab = cv2.merge((l,a,b))
|
|
|
|
|
156 |
imagem_melhorada = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)
|
|
|
|
|
157 |
imagem_melhorada = cv2.GaussianBlur(imagem_melhorada, (5, 5), 0)
|
|
|
158 |
return imagem_melhorada
|
159 |
|
160 |
def detectar_esclera(imagem):
|
|
|
|
|
|
|
|
|
161 |
hsv = cv2.cvtColor(imagem, cv2.COLOR_RGB2HSV)
|
|
|
|
|
162 |
lower_white = np.array([0, 0, 180])
|
163 |
upper_white = np.array([180, 30, 255])
|
|
|
|
|
164 |
mask_esclera = cv2.inRange(hsv, lower_white, upper_white)
|
|
|
|
|
165 |
kernel = np.ones((5,5), np.uint8)
|
166 |
mask_esclera = cv2.morphologyEx(mask_esclera, cv2.MORPH_OPEN, kernel)
|
167 |
mask_esclera = cv2.morphologyEx(mask_esclera, cv2.MORPH_CLOSE, kernel)
|
|
|
168 |
return mask_esclera
|
169 |
|
170 |
def detectar_iris_pupila(imagem, mask_esclera):
|
|
|
|
|
|
|
|
|
171 |
gray = cv2.cvtColor(imagem, cv2.COLOR_RGB2GRAY)
|
|
|
|
|
172 |
mask_olho = cv2.bitwise_not(mask_esclera)
|
173 |
eye_region = cv2.bitwise_and(gray, gray, mask=mask_olho)
|
|
|
|
|
174 |
edges = cv2.Canny(eye_region, 30, 60)
|
|
|
|
|
175 |
iris_circles = cv2.HoughCircles(
|
176 |
edges,
|
177 |
cv2.HOUGH_GRADIENT,
|
|
|
182 |
minRadius=80,
|
183 |
maxRadius=150
|
184 |
)
|
185 |
+
|
|
|
186 |
if iris_circles is not None:
|
187 |
iris_circles = np.uint16(np.around(iris_circles))
|
188 |
ix, iy, ir = iris_circles[0][0]
|
189 |
mask_iris = np.zeros_like(gray)
|
190 |
cv2.circle(mask_iris, (ix, iy), ir, 255, -1)
|
|
|
|
|
191 |
iris_region = cv2.bitwise_and(gray, gray, mask=mask_iris)
|
|
|
|
|
192 |
thresh = cv2.adaptiveThreshold(
|
193 |
iris_region,
|
194 |
255,
|
|
|
197 |
11,
|
198 |
2
|
199 |
)
|
|
|
|
|
200 |
pupil_circles = cv2.HoughCircles(
|
201 |
thresh,
|
202 |
cv2.HOUGH_GRADIENT,
|
|
|
207 |
minRadius=20,
|
208 |
maxRadius=50
|
209 |
)
|
210 |
+
|
211 |
if pupil_circles is not None:
|
212 |
pupil_circles = np.uint16(np.around(pupil_circles))
|
213 |
px, py, pr = pupil_circles[0][0]
|
214 |
return (ix, iy, ir), (px, py, pr)
|
215 |
+
|
216 |
return None, None
|
217 |
|
218 |
def analisar_textura_setorial(imagem, iris_info, pupil_info):
|
|
|
|
|
|
|
219 |
if iris_info is None or pupil_info is None:
|
220 |
return {}
|
221 |
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
ix, iy, ir = iris_info
|
223 |
px, py, pr = pupil_info
|
224 |
|
|
|
225 |
gray = cv2.cvtColor(imagem, cv2.COLOR_RGB2GRAY)
|
|
|
|
|
226 |
clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(4, 4))
|
227 |
gray = clahe.apply(gray)
|
228 |
|
|
|
229 |
mask_iris = np.zeros_like(gray)
|
230 |
cv2.circle(mask_iris, (ix, iy), int(ir * 0.98), 255, -1)
|
231 |
cv2.circle(mask_iris, (px, py), int(pr * 1.02), 0, -1)
|
232 |
|
|
|
233 |
setores = {}
|
234 |
for i in range(12):
|
235 |
ang_inicio = i * 30
|
236 |
ang_fim = (i + 1) * 30
|
|
|
|
|
237 |
mask_setor = np.zeros_like(gray)
|
238 |
cv2.ellipse(mask_setor,
|
239 |
(ix, iy),
|
|
|
244 |
255,
|
245 |
-1)
|
246 |
|
|
|
|
|
247 |
mask_final = cv2.bitwise_and(mask_iris, mask_setor)
|
248 |
+
kernel = np.ones((2, 2), np.uint8)
|
249 |
mask_final = cv2.morphologyEx(mask_final, cv2.MORPH_OPEN, kernel)
|
|
|
|
|
250 |
setor_roi = cv2.bitwise_and(gray, gray, mask=mask_final)
|
251 |
|
|
|
252 |
non_zero = setor_roi[setor_roi > 0]
|
253 |
if len(non_zero) > 100:
|
|
|
254 |
non_zero = ((non_zero - non_zero.min()) /
|
255 |
(non_zero.max() - non_zero.min() + 1e-8) * 15).astype(np.uint8)
|
256 |
|
|
|
257 |
tamanho_janela = int(np.sqrt(len(non_zero)))
|
258 |
if tamanho_janela > 1:
|
259 |
matriz_2d = non_zero[:tamanho_janela**2].reshape(tamanho_janela, tamanho_janela)
|
260 |
|
261 |
try:
|
|
|
262 |
glcm = graycomatrix(matriz_2d,
|
263 |
distances=[1],
|
264 |
angles=[0, np.pi/4],
|
|
|
266 |
symmetric=True,
|
267 |
normed=True)
|
268 |
|
|
|
269 |
contraste = np.mean(graycoprops(glcm, 'contrast'))
|
270 |
homogeneidade = np.mean(graycoprops(glcm, 'homogeneity'))
|
271 |
|
|
|
276 |
"std": float(np.std(non_zero))
|
277 |
}
|
278 |
except Exception as e:
|
|
|
279 |
setores[f"setor_{i+1}"] = {
|
280 |
"contraste": 0.0,
|
281 |
"homogeneidade": 1.0,
|
|
|
298 |
}
|
299 |
|
300 |
return setores
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
301 |
|
302 |
def validar_metricas(metricas):
|
|
|
|
|
|
|
303 |
metricas_validadas = {}
|
304 |
+
|
|
|
305 |
if 'pupila' in metricas:
|
306 |
raio = metricas['pupila'].get('raio', 0)
|
307 |
circularidade = metricas['pupila'].get('circularidade', 0)
|
308 |
|
|
|
309 |
if raio <= 0 or raio > 100:
|
310 |
+
raio = 35
|
311 |
if circularidade <= 0 or circularidade > 1:
|
312 |
+
circularidade = 0.85
|
313 |
|
314 |
metricas_validadas['pupila'] = {
|
315 |
'raio': raio,
|
316 |
'circularidade': circularidade
|
317 |
}
|
318 |
+
|
|
|
319 |
if 'iris' in metricas:
|
320 |
densidade = metricas['iris'].get('densidade_media', 0)
|
321 |
homogeneidade = metricas['iris'].get('homogeneidade', 0)
|
322 |
+
|
|
|
323 |
if densidade < 0:
|
324 |
+
densidade = 0.5
|
325 |
if homogeneidade < 0 or homogeneidade > 1:
|
326 |
+
homogeneidade = 0.5
|
327 |
|
328 |
metricas_validadas['iris'] = {
|
329 |
'densidade_media': densidade,
|
330 |
'homogeneidade': homogeneidade
|
331 |
}
|
332 |
+
|
|
|
333 |
if 'collarette' in metricas and metricas['collarette']:
|
334 |
regularidade = metricas['collarette'].get('regularidade', 0)
|
335 |
circularidade = metricas['collarette'].get('circularidade', 0)
|
336 |
+
|
|
|
337 |
if regularidade < 0:
|
338 |
+
regularidade = 300
|
339 |
if circularidade < 0 or circularidade > 1:
|
340 |
+
circularidade = 0.85
|
341 |
|
342 |
metricas_validadas['collarette'] = {
|
343 |
'regularidade': regularidade,
|
344 |
'circularidade': circularidade
|
345 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
346 |
|
347 |
+
return metricas_validadas
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|