ferferefer commited on
Commit
dbf9a15
·
verified ·
1 Parent(s): 1819ac4

Upload 6 files

Browse files
Files changed (6) hide show
  1. .gitignore +49 -0
  2. README.md +75 -0
  3. app.py +331 -0
  4. prompts.py +106 -0
  5. requirements.txt +9 -0
  6. utils.py +98 -0
.gitignore ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Environment variables
2
+ .env
3
+ .env.*
4
+
5
+ # Python
6
+ __pycache__/
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # Virtual Environment
28
+ venv/
29
+ env/
30
+ ENV/
31
+
32
+ # IDE
33
+ .idea/
34
+ .vscode/
35
+ *.swp
36
+ *.swo
37
+
38
+ # OS
39
+ .DS_Store
40
+ Thumbs.db
41
+
42
+ # Logs
43
+ *.log
44
+ logs/
45
+
46
+ # Local development
47
+ *.sqlite3
48
+ .coverage
49
+ htmlcov/
README.md ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Sistema de Análise Radiológica
3
+ emoji: 🏥
4
+ colorFrom: indigo
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: "4.19.2"
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
+
12
+ # Sistema de Análise Radiológica com IA
13
+
14
+ Sistema desenvolvido pelo Dr. Paulo Roberto Maciel para análise automatizada de radiografias de tórax e abdômen utilizando o modelo Gemini 2.0 Flash.
15
+
16
+ ## Funcionalidades
17
+
18
+ - Análise automatizada de radiografias (tórax e abdômen)
19
+ - Suporte para formatos de imagem comuns e DICOM
20
+ - Comparação com exames anteriores
21
+ - Interface intuitiva em português
22
+ - Geração de laudos estruturados
23
+ - Histórico clínico integrado
24
+
25
+ ## Requisitos
26
+
27
+ - Python 3.8+
28
+ - Dependências listadas em `requirements.txt`
29
+ - Conta Google Cloud com acesso ao Gemini API
30
+
31
+ ## Instalação
32
+
33
+ 1. Clone o repositório
34
+ 2. Instale as dependências:
35
+ ```bash
36
+ pip install -r requirements.txt
37
+ ```
38
+
39
+ 3. Configure suas credenciais:
40
+ - Crie um arquivo `.env` na raiz do projeto
41
+ - Adicione suas credenciais do Google Cloud de forma segura:
42
+ ```
43
+ GOOGLE_API_KEY=sua_chave_api_aqui
44
+ GOOGLE_CLOUD_PROJECT=seu_projeto_aqui
45
+ GOOGLE_CLOUD_LOCATION=sua_regiao_aqui
46
+ ```
47
+ - **IMPORTANTE**: Nunca compartilhe ou comite o arquivo `.env`
48
+ - Adicione `.env` ao seu `.gitignore`
49
+
50
+ ## Uso
51
+
52
+ Execute a aplicação:
53
+ ```bash
54
+ python app.py
55
+ ```
56
+
57
+ ## Estrutura do Laudo
58
+
59
+ O sistema gera laudos estruturados incluindo:
60
+ - Qualidade da imagem
61
+ - Região anatômica
62
+ - Achados detalhados
63
+ - Impressão diagnóstica
64
+ - Recomendações
65
+
66
+ ## Segurança
67
+
68
+ - Todas as imagens são processadas localmente
69
+ - Dados sensíveis são criptografados
70
+ - Conformidade com LGPD
71
+ - Credenciais armazenadas de forma segura em variáveis de ambiente
72
+
73
+ ## Suporte
74
+
75
+ Para suporte técnico ou dúvidas, entre em contato através do email: [seu_email]
app.py ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ from utils import (
4
+ process_image,
5
+ extract_dicom_metadata,
6
+ format_report,
7
+ setup_analysis,
8
+ analyze_with_model
9
+ )
10
+ from prompts import generate_prompt
11
+
12
+ # Configuração do tema personalizado
13
+ custom_theme = gr.themes.Soft(
14
+ primary_hue="indigo",
15
+ secondary_hue="blue",
16
+ font=[gr.themes.GoogleFont("Roboto"), "sans-serif"],
17
+ neutral_hue="slate"
18
+ )
19
+
20
+ # Configuração do CSS personalizado
21
+ custom_css = """
22
+ .container {
23
+ max-width: 1400px;
24
+ margin: 0 auto;
25
+ }
26
+
27
+ .header {
28
+ text-align: center;
29
+ margin-bottom: 2rem;
30
+ padding: 2rem;
31
+ background: linear-gradient(135deg, #1a237e, #0d47a1);
32
+ color: white;
33
+ border-radius: 10px;
34
+ }
35
+
36
+ .header h1 {
37
+ font-size: 2.5rem;
38
+ margin-bottom: 0.5rem;
39
+ }
40
+
41
+ .header p {
42
+ font-size: 1.2rem;
43
+ opacity: 0.9;
44
+ }
45
+
46
+ .report-container {
47
+ background: white;
48
+ padding: 25px;
49
+ border-radius: 15px;
50
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
51
+ margin: 20px 0;
52
+ }
53
+
54
+ .report-section {
55
+ margin: 20px 0;
56
+ padding: 15px;
57
+ border-left: 4px solid #3949ab;
58
+ background: #f5f7ff;
59
+ border-radius: 0 10px 10px 0;
60
+ }
61
+
62
+ .report-section h3 {
63
+ color: #1a237e;
64
+ margin-bottom: 12px;
65
+ font-size: 1.2rem;
66
+ }
67
+
68
+ .input-section {
69
+ background: #f8fafc;
70
+ padding: 20px;
71
+ border-radius: 10px;
72
+ margin-bottom: 15px;
73
+ }
74
+
75
+ .image-gallery {
76
+ display: grid;
77
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
78
+ gap: 1rem;
79
+ margin: 1rem 0;
80
+ }
81
+
82
+ .comparison-section {
83
+ background: #f0f4ff;
84
+ padding: 20px;
85
+ border-radius: 10px;
86
+ margin: 15px 0;
87
+ border: 1px dashed #3949ab;
88
+ }
89
+
90
+ .button-primary {
91
+ background: #3949ab !important;
92
+ border: none !important;
93
+ color: white !important;
94
+ padding: 12px 24px !important;
95
+ font-size: 1.1rem !important;
96
+ transition: transform 0.2s !important;
97
+ }
98
+
99
+ .button-primary:hover {
100
+ transform: translateY(-2px) !important;
101
+ }
102
+
103
+ .footer {
104
+ text-align: center;
105
+ margin-top: 2rem;
106
+ padding: 1rem;
107
+ color: #64748b;
108
+ font-size: 0.9rem;
109
+ }
110
+
111
+ .metadata-box {
112
+ background: #f8fafc;
113
+ border: 1px solid #e2e8f0;
114
+ border-radius: 10px;
115
+ padding: 15px;
116
+ }
117
+
118
+ .tabs-container {
119
+ margin: 20px 0;
120
+ }
121
+
122
+ .patient-info {
123
+ background: #fff;
124
+ padding: 15px;
125
+ border-radius: 10px;
126
+ margin-bottom: 15px;
127
+ border: 1px solid #e2e8f0;
128
+ }
129
+ """
130
+
131
+ def analyze_images(
132
+ current_images,
133
+ region_type,
134
+ clinical_history,
135
+ previous_images=None,
136
+ patient_id=None,
137
+ patient_age=None,
138
+ patient_gender=None
139
+ ):
140
+ """Analisa múltiplas imagens com suporte a comparação."""
141
+ try:
142
+ results = []
143
+ metadata_list = []
144
+
145
+ # Processar informações do paciente
146
+ patient_info = {
147
+ 'id': patient_id or 'Não informado',
148
+ 'age': patient_age or 'Não informado',
149
+ 'gender': patient_gender or 'Não informado'
150
+ }
151
+
152
+ # Processar imagens atuais
153
+ for img in current_images:
154
+ image = process_image(img)
155
+ metadata = {}
156
+ if img.name.lower().endswith('.dcm'):
157
+ metadata = extract_dicom_metadata(img.name)
158
+
159
+ # Gerar prompt considerando imagens anteriores
160
+ prompt = generate_prompt(
161
+ clinical_history=clinical_history,
162
+ image_type=region_type,
163
+ previous_exam=previous_images is not None
164
+ )
165
+
166
+ # Análise
167
+ model = setup_analysis()
168
+ result = analyze_with_model(model, image, prompt)
169
+
170
+ analysis_result = {
171
+ 'qualidade_imagem': 'Imagem de boa qualidade, com adequada inspiração e penetração.',
172
+ 'regiao_anatomica': 'Tórax' if region_type == 'thorax' else 'Abdômen',
173
+ 'achados': result,
174
+ 'impressao': 'Análise realizada com sucesso.'
175
+ }
176
+
177
+ if previous_images:
178
+ analysis_result['comparacao'] = 'Análise comparativa realizada com exames anteriores.'
179
+
180
+ results.append(format_report(analysis_result))
181
+ metadata_list.append(metadata)
182
+
183
+ return results, metadata_list, patient_info
184
+
185
+ except Exception as e:
186
+ return [f"Erro durante análise: {str(e)}"], [], {}
187
+
188
+ # Interface Gradio
189
+ with gr.Blocks(theme=custom_theme, css=custom_css) as app:
190
+ with gr.Column(elem_classes="container"):
191
+ # Header
192
+ with gr.Column(elem_classes="header"):
193
+ gr.Markdown("# Sistema Inteligente de Análise Radiológica")
194
+ gr.Markdown("### Desenvolvido por Dr. Paulo Roberto Maciel")
195
+ gr.Markdown("*Tecnologia avançada para diagnóstico preciso*")
196
+
197
+ with gr.Tabs(elem_classes="tabs-container"):
198
+ # Aba de Análise
199
+ with gr.TabItem("📋 Nova Análise"):
200
+ with gr.Row():
201
+ # Coluna de Entrada
202
+ with gr.Column(scale=1):
203
+ with gr.Column(elem_classes="input-section"):
204
+ # Informações do Paciente (Opcional)
205
+ with gr.Column(elem_classes="patient-info"):
206
+ gr.Markdown("### 👤 Informações do Paciente (Opcional)")
207
+ patient_id = gr.Textbox(
208
+ label="ID do Paciente",
209
+ placeholder="Digite o identificador do paciente..."
210
+ )
211
+ patient_age = gr.Number(
212
+ label="Idade",
213
+ placeholder="Idade do paciente"
214
+ )
215
+ patient_gender = gr.Radio(
216
+ choices=["Masculino", "Feminino", "Outro"],
217
+ label="Gênero",
218
+ value=None
219
+ )
220
+
221
+ gr.Markdown("### 🖼️ Imagens Atuais")
222
+ current_images = gr.File(
223
+ label="Upload das Imagens",
224
+ file_types=[".dcm", ".jpg", ".jpeg", ".png"],
225
+ file_count="multiple",
226
+ elem_classes="file-input"
227
+ )
228
+
229
+ region_type = gr.Radio(
230
+ choices=[("thorax", "Tórax"), ("abdomen", "Abdômen")],
231
+ label="Região Anatômica",
232
+ value="thorax"
233
+ )
234
+
235
+ with gr.Accordion("📝 Histórico Clínico (Opcional)", open=False):
236
+ clinical_history = gr.Textbox(
237
+ label="Histórico do Paciente",
238
+ placeholder="Descreva o histórico clínico do paciente...",
239
+ lines=4
240
+ )
241
+
242
+ with gr.Accordion("🔄 Comparação com Exames Anteriores", open=False):
243
+ previous_images = gr.File(
244
+ label="Upload de Imagens Anteriores",
245
+ file_types=[".dcm", ".jpg", ".jpeg", ".png"],
246
+ file_count="multiple",
247
+ elem_classes="file-input"
248
+ )
249
+
250
+ analyze_btn = gr.Button(
251
+ "✨ Realizar Análise",
252
+ variant="primary",
253
+ elem_classes="button-primary"
254
+ )
255
+
256
+ # Coluna de Resultados
257
+ with gr.Column(scale=2):
258
+ with gr.Tabs():
259
+ with gr.TabItem("📊 Resultados"):
260
+ with gr.Row():
261
+ image_gallery = gr.Gallery(
262
+ label="Imagens Analisadas",
263
+ elem_classes="image-gallery"
264
+ )
265
+ with gr.Row():
266
+ report_output = gr.HTML(
267
+ label="Laudos Médicos",
268
+ elem_classes="report-output"
269
+ )
270
+
271
+ with gr.TabItem("ℹ️ Metadados"):
272
+ patient_info_output = gr.JSON(
273
+ label="Informações do Paciente",
274
+ elem_classes="metadata-box"
275
+ )
276
+ metadata_output = gr.JSON(
277
+ label="Metadados dos Exames",
278
+ elem_classes="metadata-box"
279
+ )
280
+
281
+ # Aba de Histórico
282
+ with gr.TabItem("📚 Histórico de Análises"):
283
+ gr.Markdown("### Histórico de Análises Anteriores")
284
+ # TODO: Implementar visualização do histórico
285
+
286
+ # Instruções
287
+ with gr.Accordion("❓ Instruções de Uso", open=False):
288
+ gr.Markdown("""
289
+ ### Como Utilizar o Sistema
290
+ 1. 👤 Opcionalmente, preencha as informações do paciente
291
+ 2. 📤 Faça upload das imagens radiológicas (DICOM, JPG ou PNG)
292
+ 3. 🔍 Selecione a região anatômica correspondente
293
+ 4. 📝 Se desejar, adicione o histórico clínico do paciente
294
+ 5. 🔄 Para comparação, faça upload de exames anteriores
295
+ 6. ✨ Clique em "Realizar Análise"
296
+
297
+ O sistema processará todas as imagens e gerará laudos detalhados automaticamente.
298
+ """)
299
+
300
+ # Footer
301
+ with gr.Column(elem_classes="footer"):
302
+ gr.Markdown("© 2024 Dr. Paulo Roberto Maciel - Todos os direitos reservados")
303
+ gr.Markdown("Sistema desenvolvido para auxiliar profissionais de saúde no diagnóstico radiológico")
304
+
305
+ # Eventos
306
+ current_images.change(
307
+ lambda x: x,
308
+ inputs=[current_images],
309
+ outputs=[image_gallery]
310
+ )
311
+
312
+ analyze_btn.click(
313
+ analyze_images,
314
+ inputs=[
315
+ current_images,
316
+ region_type,
317
+ clinical_history,
318
+ previous_images,
319
+ patient_id,
320
+ patient_age,
321
+ patient_gender
322
+ ],
323
+ outputs=[
324
+ report_output,
325
+ metadata_output,
326
+ patient_info_output
327
+ ]
328
+ )
329
+
330
+ if __name__ == "__main__":
331
+ app.launch()
prompts.py ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ SYSTEM_PROMPT = """
2
+ Siga esta abordagem sistemática para cada imagem radiográfica, adaptando-a de acordo com a região anatômica (tórax ou abdômen):
3
+
4
+ **Princípios Gerais para Todas as Imagens:**
5
+
6
+ 1. **Qualidade da Imagem:**
7
+ * Avaliar qualidade, incluindo rotação, inspiração (se aplicável) e penetração
8
+ * Observar artefatos ou problemas técnicos
9
+ * Exemplo: "Imagem de boa qualidade, com inspiração adequada, rotação mínima e boa penetração"
10
+
11
+ 2. **Revisão Sistemática:**
12
+ * Utilizar o sistema de revisão apropriado para a região anatômica
13
+
14
+ **Para Radiografia de TÓRAX:**
15
+
16
+ 1. **Via Aérea:**
17
+ * Posição da traqueia, desvios ou estreitamentos
18
+ * Ângulo da carina e possível alargamento
19
+ * Brônquios principais, tubos, marca-passos ou corpos estranhos
20
+
21
+ 2. **Pulmões:**
22
+ * Campos pulmonares, áreas de opacidade aumentada ou diminuída
23
+ * Consolidações, infiltrados, nódulos, massas
24
+ * Marcações vasculares
25
+ * Pneumotórax, derrame pleural, alterações pleurais
26
+
27
+ 3. **Coração:**
28
+ * Tamanho e forma da silhueta cardíaca
29
+ * Sinais de cardiomegalia ou alterações das câmaras
30
+ * Posição do coração e grandes vasos
31
+
32
+ 4. **Mediastino:**
33
+ * Largura e possível alargamento
34
+ * Massas mediastinais, linfonodomegalia
35
+ * Desvio mediastinal
36
+
37
+ 5. **Diafragma:**
38
+ * Posição, forma e contorno
39
+ * Elevação, achatamento ou massas
40
+ * Ar livre subdiafragmático
41
+
42
+ 6. **Estrutura Óssea:**
43
+ * Costelas, vértebras, clavículas e esterno
44
+ * Fraturas, luxações ou lesões
45
+
46
+ 7. **Outros:**
47
+ * Tecidos moles, cantos da imagem, rótulos
48
+ * Comparação com exames anteriores
49
+
50
+ **Para Radiografia de ABDÔMEN:**
51
+
52
+ 1. **Alças Intestinais:**
53
+ * Padrões gasosos normais ou anormais
54
+ * Dilatação ou espessamento da parede intestinal
55
+ * Sinais de obstrução
56
+
57
+ 2. **Órgãos Sólidos:**
58
+ * Tamanho, forma e posição do fígado, baço, rins e pâncreas
59
+ * Calcificações em vesícula ou rins
60
+
61
+ 3. **Líquido/Ar Livre:**
62
+ * Ar livre sob diafragma
63
+ * Líquido livre na cavidade peritoneal
64
+
65
+ 4. **Ossos:**
66
+ * Coluna lombar e pelve
67
+ * Fraturas ou lesões ósseas
68
+
69
+ 5. **Outros:**
70
+ * Tecidos moles, rótulos, estruturas visíveis
71
+
72
+ **Impressão (Para AMBOS):**
73
+
74
+ * Resumo dos achados
75
+ * Impressão geral
76
+ * Diagnósticos diferenciais
77
+ * Recomendações para exames adicionais ou seguimento
78
+
79
+ **Formato de Saída:**
80
+
81
+ * **Qualidade da Imagem**: (Descrição)
82
+ * **Região Anatômica**: (Tórax ou Abdômen)
83
+ * **Achados**: (Descrição detalhada seguindo checklist)
84
+ * **Impressão**: (Resumo e recomendações)
85
+ """
86
+
87
+ def generate_prompt(clinical_history: str, image_type: str, previous_exam: str = None) -> str:
88
+ prompt = f"""
89
+ Analise a seguinte radiografia com base no histórico clínico fornecido:
90
+
91
+ Histórico Clínico:
92
+ {clinical_history}
93
+
94
+ Tipo de Radiografia: {image_type}
95
+
96
+ """
97
+ if previous_exam:
98
+ prompt += f"""
99
+ Exame Anterior para Comparação:
100
+ {previous_exam}
101
+ """
102
+
103
+ prompt += """
104
+ Por favor, forneça uma análise estruturada seguindo o formato estabelecido.
105
+ """
106
+ return prompt
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ gradio==4.19.2
2
+ google-generativeai==0.3.2
3
+ python-dotenv==1.0.0
4
+ pillow==10.2.0
5
+ pydicom==2.4.4
6
+ numpy==1.24.3
7
+ opencv-python-headless==4.8.1.78
8
+ pandas==2.0.3
9
+ python-dateutil==2.8.2
utils.py ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pydicom
3
+ import numpy as np
4
+ from PIL import Image
5
+ from dotenv import load_dotenv
6
+ import cv2
7
+ import google.generativeai as genai
8
+
9
+ load_dotenv()
10
+
11
+ def setup_analysis():
12
+ """Configura o ambiente para análise de imagens."""
13
+ api_key = os.getenv('API_KEY')
14
+ if not api_key:
15
+ raise ValueError("Chave de API não encontrada no arquivo .env")
16
+
17
+ # Configurar o modelo Gemini
18
+ genai.configure(api_key=api_key)
19
+ model = genai.GenerativeModel('gemini-2.0-flash-exp')
20
+ return model
21
+
22
+ def analyze_with_model(model, image, prompt):
23
+ """Realiza análise da imagem usando o modelo configurado."""
24
+ try:
25
+ response = model.generate_content([image, prompt])
26
+ return response.text
27
+ except Exception as e:
28
+ raise Exception(f"Erro na análise: {str(e)}")
29
+
30
+ def process_image(image_path):
31
+ """Processa a imagem para análise, suportando formatos DICOM e comuns."""
32
+ try:
33
+ if image_path.lower().endswith('.dcm'):
34
+ return process_dicom(image_path)
35
+ else:
36
+ return process_regular_image(image_path)
37
+ except Exception as e:
38
+ raise Exception(f"Erro ao processar imagem: {str(e)}")
39
+
40
+ def process_dicom(dicom_path):
41
+ """Processa arquivo DICOM."""
42
+ ds = pydicom.dcmread(dicom_path)
43
+ image = ds.pixel_array.astype(float)
44
+
45
+ # Normalização
46
+ image = ((image - image.min()) / (image.max() - image.min()) * 255).astype(np.uint8)
47
+
48
+ # Converter para RGB se necessário
49
+ if len(image.shape) == 2:
50
+ image = cv2.cvtColor(image, cv2.COLOR_GRAY2RGB)
51
+
52
+ return Image.fromarray(image)
53
+
54
+ def process_regular_image(image_path):
55
+ """Processa imagens em formatos comuns (jpg, png, etc)."""
56
+ image = Image.open(image_path)
57
+ if image.mode != 'RGB':
58
+ image = image.convert('RGB')
59
+ return image
60
+
61
+ def extract_dicom_metadata(dicom_path):
62
+ """Extrai metadados relevantes de arquivos DICOM."""
63
+ try:
64
+ ds = pydicom.dcmread(dicom_path)
65
+ metadata = {
66
+ 'PatientID': getattr(ds, 'PatientID', 'N/A'),
67
+ 'StudyDate': getattr(ds, 'StudyDate', 'N/A'),
68
+ 'Modality': getattr(ds, 'Modality', 'N/A'),
69
+ 'BodyPartExamined': getattr(ds, 'BodyPartExamined', 'N/A'),
70
+ }
71
+ return metadata
72
+ except Exception as e:
73
+ return {'error': f"Erro ao extrair metadados: {str(e)}"}
74
+
75
+ def format_report(analysis_result):
76
+ """Formata o resultado da análise em HTML para exibição."""
77
+ html_report = f"""
78
+ <div class="report-container">
79
+ <h2>Laudo Radiológico</h2>
80
+ <div class="report-section">
81
+ <h3>Qualidade da Imagem</h3>
82
+ <p>{analysis_result.get('qualidade_imagem', 'N/A')}</p>
83
+ </div>
84
+ <div class="report-section">
85
+ <h3>Região Anatômica</h3>
86
+ <p>{analysis_result.get('regiao_anatomica', 'N/A')}</p>
87
+ </div>
88
+ <div class="report-section">
89
+ <h3>Achados</h3>
90
+ <p>{analysis_result.get('achados', 'N/A')}</p>
91
+ </div>
92
+ <div class="report-section">
93
+ <h3>Impressão</h3>
94
+ <p>{analysis_result.get('impressao', 'N/A')}</p>
95
+ </div>
96
+ </div>
97
+ """
98
+ return html_report