DHEIVER commited on
Commit
82a1a13
·
verified ·
1 Parent(s): ad42f1d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +350 -40
app.py CHANGED
@@ -9,6 +9,7 @@ import torch.nn.functional as F
9
  import torchvision.transforms as transforms
10
  import logging
11
  from datetime import datetime
 
12
 
13
  # Configuração básica de logging
14
  logging.basicConfig(
@@ -23,42 +24,184 @@ class IrisZone:
23
  inner_ratio: float
24
  outer_ratio: float
25
  color: Tuple[int, int, int]
26
- angle_start: float = 0
27
- angle_end: float = 360
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  class IrisAnalyzer:
30
  """Classe principal para análise da íris"""
31
  def __init__(self):
32
  self.zones = [
33
- IrisZone("Zona Cerebral/Neural", 0.85, 1.0, (255, 0, 0)),
34
- IrisZone("Zona Digestiva", 0.7, 0.85, (0, 255, 0)),
35
- IrisZone("Zona Respiratória", 0.55, 0.7, (0, 0, 255)),
36
- IrisZone("Zona Circulatória", 0.4, 0.55, (255, 255, 0)),
37
- IrisZone("Zona Linfática", 0.25, 0.4, (255, 0, 255)),
38
- IrisZone("Zona Endócrina", 0.15, 0.25, (0, 255, 255)),
39
- IrisZone("Zona Pupilar", 0, 0.15, (128, 128, 128))
 
 
40
  ]
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  def detect_pupil(self, img: np.ndarray) -> Tuple[int, int, int]:
43
- """Detecta a pupila na imagem"""
44
  try:
45
- gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
 
 
 
 
46
  blur = cv2.GaussianBlur(gray, (5,5), 0)
47
  _, thresh = cv2.threshold(blur, 30, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
48
 
49
- contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
 
 
 
 
 
50
 
51
  if not contours:
52
  return None
53
 
54
- # Encontrar o maior contorno circular
55
- max_area = 0
56
  best_contour = None
57
 
58
  for contour in contours:
59
  area = cv2.contourArea(contour)
60
- if area > max_area:
61
- max_area = area
 
 
 
 
 
 
62
  best_contour = contour
63
 
64
  if best_contour is None:
@@ -71,79 +214,246 @@ class IrisAnalyzer:
71
  logging.error(f"Erro na detecção da pupila: {str(e)}")
72
  return None
73
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  def analyze_iris(self, img: np.ndarray) -> Tuple[np.ndarray, Dict]:
75
- """Análise principal da íris"""
76
  try:
77
  output_img = img.copy()
78
- results = {}
 
 
 
 
 
 
 
 
79
 
 
80
  pupil = self.detect_pupil(img)
81
  if pupil is None:
82
- return img, {"Erro": "Não foi possível detectar a pupila"}
83
 
84
  x, y, pupil_radius = pupil
85
  iris_radius = pupil_radius * 4
86
 
 
87
  cv2.circle(output_img, (x, y), pupil_radius, (0, 0, 0), 2)
88
 
 
 
 
 
89
  for zone in self.zones:
90
  inner_r = int(iris_radius * zone.inner_ratio)
91
  outer_r = int(iris_radius * zone.outer_ratio)
92
 
93
- cv2.circle(output_img, (x, y), outer_r, zone.color, 2)
94
-
95
- # Análise simplificada da zona
96
  mask = np.zeros(img.shape[:2], dtype=np.uint8)
97
  cv2.circle(mask, (x, y), outer_r, 255, -1)
98
  cv2.circle(mask, (x, y), inner_r, 0, -1)
99
 
100
- mean_color = cv2.mean(img, mask=mask)
101
- results[zone.name] = f"Intensidade média: {mean_color[0]:.1f}"
 
 
 
 
102
 
103
- # Adicionar rótulo
104
- cv2.putText(output_img, zone.name,
105
- (x - iris_radius, y + outer_r),
 
 
 
 
 
 
 
 
106
  cv2.FONT_HERSHEY_SIMPLEX, 0.5, zone.color, 1)
107
 
108
- return output_img, results
 
 
 
 
 
 
 
 
 
109
 
110
  except Exception as e:
111
- logging.error(f"Erro na análise: {str(e)}")
112
- return img, {"Erro": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
 
114
  def process_image(img):
115
  """Função principal para processar imagem"""
116
  if img is None:
117
- return None, {"Erro": "Nenhuma imagem fornecida"}
 
118
  analyzer = IrisAnalyzer()
119
- return analyzer.analyze_iris(np.array(img))
 
 
 
120
 
121
  # Interface Gradio
122
  with gr.Blocks(theme=gr.themes.Soft()) as iface:
123
  gr.Markdown("""
124
- # 🔍 Analisador de Íris
125
- ### Análise de zonas da íris baseada na teoria de Jensen
 
 
126
  """)
127
 
128
  with gr.Row():
129
- with gr.Column():
130
  input_image = gr.Image(
131
  label="Upload da imagem do olho",
132
  type="numpy",
133
  sources=["upload", "clipboard"]
134
  )
135
 
136
- analyze_btn = gr.Button("📸 Analisar", variant="primary")
 
 
 
137
 
138
- with gr.Column():
 
 
 
 
 
 
 
 
139
  output_image = gr.Image(label="Análise Visual")
140
- results = gr.JSON(label="Resultados")
 
 
 
 
 
 
 
 
 
 
 
 
141
 
142
  analyze_btn.click(
143
  fn=process_image,
144
  inputs=input_image,
145
- outputs=[output_image, results]
146
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
 
 
148
  if __name__ == "__main__":
149
- iface.launch()
 
 
 
 
 
 
9
  import torchvision.transforms as transforms
10
  import logging
11
  from datetime import datetime
12
+ import json
13
 
14
  # Configuração básica de logging
15
  logging.basicConfig(
 
24
  inner_ratio: float
25
  outer_ratio: float
26
  color: Tuple[int, int, int]
27
+ description: str
28
+ indicators: Dict[str, str]
29
+
30
+ def __post_init__(self):
31
+ if not 0 <= self.inner_ratio <= 1:
32
+ raise ValueError("inner_ratio deve estar entre 0 e 1")
33
+ if not 0 <= self.outer_ratio <= 1:
34
+ raise ValueError("outer_ratio deve estar entre 0 e 1")
35
+ if self.inner_ratio >= self.outer_ratio:
36
+ raise ValueError("inner_ratio deve ser menor que outer_ratio")
37
+
38
+ class IrisAnalysis:
39
+ """Classe para análise e interpretação dos resultados"""
40
+
41
+ @staticmethod
42
+ def get_zone_interpretation(zone_name: str, intensity: float, variation: float) -> Dict:
43
+ """Interpretação detalhada para cada zona"""
44
+ base_interpretations = {
45
+ "Zona Cerebral/Neural": {
46
+ "aspectos": "Sistema nervoso central e periférico",
47
+ "sistemas": ["Cérebro", "Medula espinhal", "Nervos"],
48
+ "baixa": {
49
+ "indicacao": "Possível fadiga neural",
50
+ "sugestoes": [
51
+ "Considerar avaliação do sono",
52
+ "Verificar níveis de estresse",
53
+ "Avaliar demanda cognitiva"
54
+ ]
55
+ },
56
+ "média": {
57
+ "indicacao": "Condição neural moderada",
58
+ "sugestoes": [
59
+ "Manter boa higiene do sono",
60
+ "Praticar atividades mentais"
61
+ ]
62
+ },
63
+ "alta": {
64
+ "indicacao": "Boa vitalidade neural",
65
+ "sugestoes": [
66
+ "Manter práticas saudáveis",
67
+ "Continuar estimulação cognitiva"
68
+ ]
69
+ }
70
+ },
71
+ "Zona Digestiva": {
72
+ "aspectos": "Sistema digestivo completo",
73
+ "sistemas": ["Estômago", "Intestinos", "Fígado", "Pâncreas"],
74
+ "baixa": {
75
+ "indicacao": "Possível sensibilidade digestiva",
76
+ "sugestoes": [
77
+ "Avaliar hábitos alimentares",
78
+ "Considerar diário alimentar",
79
+ "Observar reações a alimentos"
80
+ ]
81
+ },
82
+ "média": {
83
+ "indicacao": "Sistema digestivo em equilíbrio moderado",
84
+ "sugestoes": [
85
+ "Manter alimentação balanceada",
86
+ "Observar horários das refeições"
87
+ ]
88
+ },
89
+ "alta": {
90
+ "indicacao": "Boa condição digestiva",
91
+ "sugestoes": [
92
+ "Manter dieta equilibrada",
93
+ "Continuar bons hábitos"
94
+ ]
95
+ }
96
+ },
97
+ # [Definições similares para outras zonas...]
98
+ }
99
+
100
+ # Determinar nível baseado na intensidade
101
+ if intensity < 85:
102
+ nivel = "baixa"
103
+ confianca = "reduzida" if variation > 30 else "moderada"
104
+ elif intensity < 170:
105
+ nivel = "média"
106
+ confianca = "moderada" if variation > 20 else "alta"
107
+ else:
108
+ nivel = "alta"
109
+ confianca = "alta" if variation < 15 else "moderada"
110
+
111
+ zone_info = base_interpretations.get(zone_name, {})
112
+
113
+ return {
114
+ "nome": zone_name,
115
+ "aspectos_analisados": zone_info.get("aspectos", ""),
116
+ "sistemas_relacionados": zone_info.get("sistemas", []),
117
+ "interpretacao": zone_info.get(nivel, {}).get("indicacao", "Sem interpretação disponível"),
118
+ "sugestoes": zone_info.get(nivel, {}).get("sugestoes", []),
119
+ "metricas": {
120
+ "intensidade": intensity,
121
+ "variacao": variation,
122
+ "nivel_geral": nivel,
123
+ "confianca_analise": confianca,
124
+ },
125
+ "indicadores": {
126
+ "intensidade_valor": f"{intensity:.1f}/255",
127
+ "variacao_valor": f"{variation:.1f}%",
128
+ "homogeneidade": "Baixa" if variation > 30 else "Média" if variation > 15 else "Alta"
129
+ }
130
+ }
131
 
132
  class IrisAnalyzer:
133
  """Classe principal para análise da íris"""
134
  def __init__(self):
135
  self.zones = [
136
+ IrisZone(
137
+ name="Zona Cerebral/Neural",
138
+ inner_ratio=0.85,
139
+ outer_ratio=1.0,
140
+ color=(255, 0, 0),
141
+ description="Sistema nervoso central e periférico",
142
+ indicators={"estresse": "alto", "energia": "moderada"}
143
+ ),
144
+ # [Outras zonas definidas similarmente...]
145
  ]
146
 
147
+ def preprocess_image(self, img: np.ndarray) -> np.ndarray:
148
+ """Pré-processamento da imagem"""
149
+ try:
150
+ # Converter para LAB para melhor contraste
151
+ lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB)
152
+ l, a, b = cv2.split(lab)
153
+
154
+ # Aplicar CLAHE no canal L
155
+ clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8,8))
156
+ l = clahe.apply(l)
157
+
158
+ # Mesclar canais
159
+ lab = cv2.merge((l,a,b))
160
+ enhanced = cv2.cvtColor(lab, cv2.COLOR_LAB2RGB)
161
+
162
+ # Redução de ruído
163
+ denoised = cv2.fastNlMeansDenoisingColored(enhanced, None, 10, 10, 7, 21)
164
+
165
+ return denoised
166
+ except Exception as e:
167
+ logging.error(f"Erro no pré-processamento: {str(e)}")
168
+ return img
169
+
170
  def detect_pupil(self, img: np.ndarray) -> Tuple[int, int, int]:
171
+ """Detecção avançada da pupila"""
172
  try:
173
+ # Pré-processar imagem
174
+ processed = self.preprocess_image(img)
175
+ gray = cv2.cvtColor(processed, cv2.COLOR_RGB2GRAY)
176
+
177
+ # Aplicar threshold adaptativo
178
  blur = cv2.GaussianBlur(gray, (5,5), 0)
179
  _, thresh = cv2.threshold(blur, 30, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
180
 
181
+ # Operações morfológicas
182
+ kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
183
+ morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
184
+
185
+ # Encontrar contornos
186
+ contours, _ = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
187
 
188
  if not contours:
189
  return None
190
 
191
+ # Encontrar o contorno mais circular
192
+ best_circularity = 0
193
  best_contour = None
194
 
195
  for contour in contours:
196
  area = cv2.contourArea(contour)
197
+ perimeter = cv2.arcLength(contour, True)
198
+ if perimeter == 0:
199
+ continue
200
+
201
+ circularity = 4 * np.pi * area / (perimeter * perimeter)
202
+
203
+ if circularity > best_circularity:
204
+ best_circularity = circularity
205
  best_contour = contour
206
 
207
  if best_contour is None:
 
214
  logging.error(f"Erro na detecção da pupila: {str(e)}")
215
  return None
216
 
217
+ def analyze_zone(self, img: np.ndarray, mask: np.ndarray, zone_name: str) -> Dict:
218
+ """Análise detalhada de uma zona específica"""
219
+ try:
220
+ # Extrair características
221
+ mean_color = cv2.mean(img, mask=mask)
222
+ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
223
+ zone_pixels = cv2.bitwise_and(gray, gray, mask=mask)
224
+
225
+ if np.sum(mask) > 0:
226
+ pixels = zone_pixels[mask > 0]
227
+ mean_intensity = np.mean(pixels)
228
+ std_dev = np.std(pixels)
229
+
230
+ # Calcular características adicionais
231
+ percentiles = np.percentile(pixels, [25, 50, 75])
232
+
233
+ # Gerar interpretação detalhada
234
+ analysis = IrisAnalysis.get_zone_interpretation(
235
+ zone_name, mean_intensity, std_dev
236
+ )
237
+
238
+ # Adicionar métricas estatísticas
239
+ analysis["metricas_detalhadas"] = {
240
+ "mediana": float(percentiles[1]),
241
+ "quartil_inferior": float(percentiles[0]),
242
+ "quartil_superior": float(percentiles[2]),
243
+ "pixels_analisados": len(pixels),
244
+ "variacao_cor": {
245
+ "r": mean_color[0],
246
+ "g": mean_color[1],
247
+ "b": mean_color[2]
248
+ }
249
+ }
250
+
251
+ return analysis
252
+
253
+ return {"erro": "Zona sem pixels válidos para análise"}
254
+
255
+ except Exception as e:
256
+ logging.error(f"Erro na análise da zona {zone_name}: {str(e)}")
257
+ return {"erro": str(e)}
258
+
259
  def analyze_iris(self, img: np.ndarray) -> Tuple[np.ndarray, Dict]:
260
+ """Análise principal com relatório detalhado"""
261
  try:
262
  output_img = img.copy()
263
+ detailed_results = {
264
+ "meta": {
265
+ "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
266
+ "versao_analise": "1.0.0"
267
+ },
268
+ "resumo_geral": {},
269
+ "zonas": {},
270
+ "observacoes": []
271
+ }
272
 
273
+ # Detectar pupila
274
  pupil = self.detect_pupil(img)
275
  if pupil is None:
276
+ return img, {"erro": "Não foi possível detectar a pupila"}
277
 
278
  x, y, pupil_radius = pupil
279
  iris_radius = pupil_radius * 4
280
 
281
+ # Desenhar círculo da pupila
282
  cv2.circle(output_img, (x, y), pupil_radius, (0, 0, 0), 2)
283
 
284
+ # Analisar cada zona
285
+ overall_health_score = 0
286
+ num_zones = len(self.zones)
287
+
288
  for zone in self.zones:
289
  inner_r = int(iris_radius * zone.inner_ratio)
290
  outer_r = int(iris_radius * zone.outer_ratio)
291
 
292
+ # Criar máscara para a zona
 
 
293
  mask = np.zeros(img.shape[:2], dtype=np.uint8)
294
  cv2.circle(mask, (x, y), outer_r, 255, -1)
295
  cv2.circle(mask, (x, y), inner_r, 0, -1)
296
 
297
+ # Desenhar círculos da zona
298
+ cv2.circle(output_img, (x, y), outer_r, zone.color, 2)
299
+
300
+ # Analisar zona
301
+ analysis = self.analyze_zone(img, mask, zone.name)
302
+ detailed_results["zonas"][zone.name] = analysis
303
 
304
+ # Calcular score geral
305
+ if "metricas" in analysis:
306
+ if analysis["metricas"]["nivel_geral"] == "alta":
307
+ overall_health_score += 1
308
+ elif analysis["metricas"]["nivel_geral"] == "média":
309
+ overall_health_score += 0.5
310
+
311
+ # Adicionar texto
312
+ text_x = x - iris_radius
313
+ text_y = y + outer_r
314
+ cv2.putText(output_img, zone.name, (text_x, text_y),
315
  cv2.FONT_HERSHEY_SIMPLEX, 0.5, zone.color, 1)
316
 
317
+ # Calcular resumo geral
318
+ health_percentage = (overall_health_score / num_zones) * 100
319
+ detailed_results["resumo_geral"] = {
320
+ "indice_geral": f"{health_percentage:.1f}%",
321
+ "interpretacao": "Bom" if health_percentage > 75 else
322
+ "Moderado" if health_percentage > 50 else
323
+ "Requer atenção"
324
+ }
325
+
326
+ return output_img, detailed_results
327
 
328
  except Exception as e:
329
+ logging.error(f"Erro na análise da íris: {str(e)}")
330
+ return img, {"erro": str(e)}
331
+
332
+ def format_results(results: Dict) -> str:
333
+ """Formata os resultados para exibição"""
334
+ if "erro" in results:
335
+ return f"Erro na análise: {results['erro']}"
336
+
337
+ formatted = "# Relatório de Análise da Íris\n\n"
338
+
339
+ # Adicionar resumo geral
340
+ if "resumo_geral" in results:
341
+ formatted += "## Resumo Geral\n"
342
+ formatted += f"- Índice Geral: {results['resumo_geral']['indice_geral']}\n"
343
+ formatted += f"- Interpretação: {results['resumo_geral']['interpretacao']}\n\n"
344
+
345
+ # Adicionar análise por zona
346
+ if "zonas" in results:
347
+ formatted += "## Análise por Zona\n\n"
348
+ for zone_name, analysis in results["zonas"].items():
349
+ formatted += f"### {zone_name}\n"
350
+ formatted += f"- Aspectos Analisados: {analysis.get('aspectos_analisados', 'N/A')}\n"
351
+ formatted += f"- Interpretação: {analysis.get('interpretacao', 'N/A')}\n"
352
+
353
+ if "sugestoes" in analysis:
354
+ formatted += "- Sugestões:\n"
355
+ for sugestao in analysis["sugestoes"]:
356
+ formatted += f" * {sugestao}\n"
357
+
358
+ if "metricas" in analysis:
359
+ formatted += f"- Nível Geral: {analysis['metricas']['nivel_geral']}\n"
360
+ formatted += f"- Confiança da Análise: {analysis['metricas']['confianca_analise']}\n"
361
+
362
+ formatted += "\n"
363
+
364
+ # Adicionar timestamp
365
+ if "meta" in results:
366
+ formatted += f"\n---\nAnálise realizada em: {results['meta']['timestamp']}\n"
367
+ formatted += f"Versão do sistema: {results['meta']['versao_analise']}\n"
368
+
369
+ return formatted
370
 
371
  def process_image(img):
372
  """Função principal para processar imagem"""
373
  if img is None:
374
+ return None, "Erro: Nenhuma imagem fornecida"
375
+
376
  analyzer = IrisAnalyzer()
377
+ output_img, results = analyzer.analyze_iris(np.array(img))
378
+ formatted_results = format_results(results)
379
+
380
+ return output_img, formatted_results
381
 
382
  # Interface Gradio
383
  with gr.Blocks(theme=gr.themes.Soft()) as iface:
384
  gr.Markdown("""
385
+ # 🔍 Analisador Avançado de Íris
386
+ ### Sistema de análise baseado na teoria de Jensen
387
+
388
+ ⚠️ **AVISO**: Esta é uma demonstração educacional. Não deve ser utilizada para diagnósticos médicos.
389
  """)
390
 
391
  with gr.Row():
392
+ with gr.Column(scale=1):
393
  input_image = gr.Image(
394
  label="Upload da imagem do olho",
395
  type="numpy",
396
  sources=["upload", "clipboard"]
397
  )
398
 
399
+ analyze_btn = gr.Button(
400
+ "📸 Analisar Imagem",
401
+ variant="primary"
402
+ )
403
 
404
+ gr.Markdown("""
405
+ ### Instruções:
406
+ 1. Faça upload de uma imagem clara do olho
407
+ 2. Certifique-se que a íris está bem visível
408
+ 3. Clique em "Analisar Imagem"
409
+ 4. Revise o relatório detalhado
410
+ """)
411
+
412
+ with gr.Column(scale=1):
413
  output_image = gr.Image(label="Análise Visual")
414
+ results_text = gr.Markdown(label="Relatório Detalhado")
415
+
416
+ with gr.Row():
417
+ gr.Markdown("""
418
+ ### Legenda das Zonas:
419
+ - 🔴 Zona Cerebral/Neural: Sistema nervoso
420
+ - 🟢 Zona Digestiva: Sistema digestivo
421
+ - 🔵 Zona Respiratória: Sistema respiratório
422
+ - 🟡 Zona Circulatória: Sistema circulatório
423
+ - 🟣 Zona Linfática: Sistema linfático
424
+ - 🔰 Zona Endócrina: Sistema hormonal
425
+ - ⚪ Zona Pupilar: Sistema nervoso autônomo
426
+ """)
427
 
428
  analyze_btn.click(
429
  fn=process_image,
430
  inputs=input_image,
431
+ outputs=[output_image, results_text]
432
  )
433
+
434
+ gr.Markdown("""
435
+ ---
436
+ ### 📋 Sobre a Análise
437
+
438
+ Este sistema realiza:
439
+ - Detecção automática da pupila
440
+ - Análise de 7 zonas principais da íris
441
+ - Avaliação de padrões e texturas
442
+ - Geração de relatório detalhado
443
+
444
+ **Observações Importantes:**
445
+ - A iridologia é uma prática alternativa
446
+ - Os resultados são interpretativos
447
+ - Consulte profissionais de saúde para diagnósticos
448
+
449
+ ---
450
+ """)
451
 
452
+ # Configurações para execução
453
  if __name__ == "__main__":
454
+ try:
455
+ iface.launch()
456
+ logging.info("Aplicação iniciada com sucesso")
457
+ except Exception as e:
458
+ logging.error(f"Erro ao iniciar a aplicação: {str(e)}")
459
+ raise