GABRIELSZK commited on
Commit
e70ba90
·
verified ·
1 Parent(s): b98e0d2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +87 -88
app.py CHANGED
@@ -1,4 +1,5 @@
1
- # Código completo atualizado para omitir exames ausentes no PDF
 
2
 
3
  import fitz
4
  import re
@@ -9,58 +10,53 @@ import pytesseract
9
  from PIL import Image, ImageEnhance, ImageFilter
10
  import io
11
 
12
- # Faixas de referência originais; exames sem faixa definida retornam valor bruto
13
  faixas = {
14
- "HB": (12, 17), "HT": (36, 50), "LEUCO": (4, 11), "PLT": (150, 450),
15
  "K+": (3.5, 5.5), "NA+": (135, 145), "UREIA": (10, 50), "CR": (0.6, 1.3),
16
  "TGO": (0, 40), "TGP": (0, 40), "ALB": (3.5, 5.0), "INR": (0.8, 1.2),
17
  "TAP": (10, 14), "TTP": (25, 35)
18
  }
19
 
20
  def classificar(nome, valor):
21
- """
22
- Retorna valor com setas se fora da faixa.
23
- """
24
  try:
25
- raw = valor.replace("K", "").replace(">", "").replace("<", "").strip()
26
  val = float(raw)
27
  if nome in faixas:
28
- min_v, max_v = faixas[nome]
29
- if val < min_v:
30
- return f"{valor} "
31
- if val > max_v:
32
- return f"{valor} ↑"
33
  return valor
34
  except:
35
  return valor
36
 
37
- # Pré-processamento de imagem para OCR
38
- def melhorar_imagem(img):
39
  img = img.convert('L')
40
  img = ImageEnhance.Contrast(img).enhance(2)
41
- img = img.filter(ImageFilter.SHARPEN)
42
- return img
43
 
44
- # Extrai texto nativo + OCR das páginas do PDF
45
  def extrair_texto_pdf(pdf_file):
46
- texto_fitz = []
 
47
  ocr_imgs = []
48
  with fitz.open(pdf_file.name) as doc:
49
  for page in doc:
50
- texto_fitz.append(page.get_text())
51
  pix = page.get_pixmap(dpi=300)
52
  img = Image.open(io.BytesIO(pix.tobytes("png")))
53
  ocr_imgs.append(melhorar_imagem(img))
54
- texto_fitz = " ".join(texto_fitz)
55
- texto_fitz = re.sub(r'\s+', ' ', texto_fitz)
56
- texto_ocr = " ".join(pytesseract.image_to_string(im) for im in ocr_imgs)
57
- texto_ocr = re.sub(r'(\b[A-Z])\s+(?=[A-Z]\b)', r'\1', texto_ocr)
58
- texto_ocr = re.sub(r'\s+', ' ', texto_ocr)
59
- return texto_fitz, texto_ocr
60
-
61
- # Padrões regex para extração de cada exame, incluindo EAS
62
  exames = {
63
- "LEUCO": r"leuc[óo]citos.*?([\d.,]+)\s?(?:10\^3)?/u?l",
 
64
  "B": r"bas[óo]filos.*?([\d.,]+)\s?%",
65
  "SS": r"segmentados.*?([\d.,]+)\s?%",
66
  "EOS": r"eosin[óo]filos.*?([\d.,]+)\s?%",
@@ -68,105 +64,108 @@ exames = {
68
  "MONO": r"mon[óo]citos.*?([\d.,]+)\s?%",
69
  "HB": r"hemoglobina.*?([\d.,]+)\s?g/dl",
70
  "HT": r"hemat[óo]crito.*?([\d.,]+)\s?%",
71
- "PLT": r"plaquetas.*?([\d.,]+)\s?(?:10\^3)?/u?l",
 
72
  "AMIL": r"amilase.*?([\d.,]+)\s?u/l",
 
 
 
73
  "ÁC UR": r"[áa]cido ur[íi]co.*?([\d.,]+)\s?mg/dl",
74
  "BT": r"bilirrubina total.*?([\d.,]+)\s?mg/dl",
75
  "BD": r"bilirrubina direta.*?([\d.,]+)\s?mg/dl",
76
  "BI": r"bilirrubina indireta.*?([\d.,]+)\s?mg/dl",
77
- "CAI": r"c[áa]lcio ioniza(?:do)?[a-z]*.*?([\d.,]+)\s?mmol/l",
78
  "CA TOTAL":r"c[áa]lcio total.*?([\d.,]+)\s?mg/dl",
79
  "CL-": r"cloro.*?([\d.,]+)\s?mmol/l",
80
- "CR": r"creatinina.*?([\d.,]+)\s?mg/dl",
81
- "UREIA": r"ureia.*?([\d.,]+)\s?mg/dl",
82
- "FAL": r"fosfatase alcalina.*?([\d.,]+)\s?u/l",
83
  "FÓS": r"f[oó]sforo.*?([\d.,]+)\s?mg/dl",
 
 
 
 
 
84
  "GGT": r"ggt.*?([\d.,]+)\s?u/l",
85
- "GLI": r"glicose.*?([\d.,]+)\s?mg/dl",
86
- "LIP": r"lipase.*?([\d.,]+)\s?u/l",
87
- "MG++": r"magn[eé]sio.*?([\d.,]+)\s?mg/dl",
88
- "PCR": r"pcr.*?\bresultado\b\s*([\d]+,[\d]+)",
89
- "K+": r"pot[áa]ssio.*?([\d.,]+)\s?mmol/l",
90
- "NA+": r"s[óo]dio.*?([\d.,]+)\s?mmol/l", "PTN": r"prote[íi]na total.*?([\d.,]+)\s?g/dl",
91
  "ALB": r"albumina.*?([\d.,]+)\s?g/dl",
 
92
  "GLOB": r"globulina.*?([\d.,]+)\s?g/dl",
93
  "RELAÇÃO": r"rela[cç][ãa]o\s+a\/g.*?([\d.,]+)",
94
- "TGO": r"tgo.*?([\d.,]+)\s?u/l",
95
- "TGP": r"tgp.*?([\d.,]+)\s?u/l",
96
- "TAP": r"tempo de protrombina.*?\bresultado\b\s*([\d]+,[\d]+)",
97
- "INR": r"I\s*N\s*R\s+([\d]+,[\d]+)",
98
  "TTP": r"ttpa.*?([\d.,]+)\s?seg",
99
- "DIMERO D":r"d[ií]mero d.*?resultado\s*([\d.,]+)\s?ng/ml",
100
- "LAC": r"lactato.*?([\d.,]+)\s?mmol/l",
 
101
  "CKMB": r"ck[- ]?mb.*?([\d.,]+)\s?u/l",
102
- "CPK": r"cpk.*?\bresultado\b\s*([\d.,]+)",
103
- "TROPO": r"troponina.*?([<>]?[\d.,]+)\s?ng/ml",
104
- # Padrões para EAS
105
- "LEUC ESTERASE": r"Leuc[óo]cito esterase\s*[:\-]?\s*([A-Za-z0-9\+\-]+)",
106
- "LEUCO EAS": r"Leuc[óo]citos?\s*[:\-]?\s*([\d]+\s*[-\/]\s*\d+)",
107
- "HEMA EAS": r"Hem[áa]cias?\s*[:\-]?\s*([\d]+\s*[-\/]\s*\d+)",
108
- "BACTERIAS": r"Bact[ée]rias?\s*[:\-]?\s*([A-Za-z]+)"
 
109
  }
110
 
111
- # Ordem de saída das chaves (sem EAS)
112
  ordem = [
113
  "LEUCO","B","SS","EOS","LINF","MONO",
114
- "HB","HT","PLT","AMIL","ÁC UR","BT","BD","BI",
115
- "CAI","CA TOTAL","CL-","CR","UREIA",
116
- "FAL","FÓS","GGT","GLI","LIP","MG++",
117
- "PCR","K+","NA+","PTN","ALB","GLOB","RELAÇÃO",
118
- "TGO","TGP","TAP","INR","TTP","DIMERO D","LAC",
119
- "CKMB","CPK","TROPO"
120
  ]
121
 
122
  def extrair_exames_formatado(pdf_file):
123
  if not pdf_file:
124
  return "Nenhum arquivo enviado.", None
125
- texto_fitz, texto_ocr = extrair_texto_pdf(pdf_file)
126
- textos = [texto_fitz, texto_ocr]
 
127
  resultados = {}
128
- for rotulo, padrao in exames.items():
129
- val = None
130
- for txt in textos:
131
- m = re.search(padrao, txt, re.IGNORECASE)
132
- if m:
133
- val = m.group(1).replace(',', '.')
134
- break
135
- if val:
136
- resultados[rotulo] = classificar(rotulo, val)
137
-
138
- # EAS (se presente)
 
139
  eas_chaves = ["LEUC ESTERASE","LEUCO EAS","HEMA EAS","BACTERIAS"]
140
  partes_eas = [f"{k}: {resultados[k]}" for k in eas_chaves if k in resultados]
141
  texto_eas = ""
142
  if partes_eas:
143
- texto_eas = "EAS: " + " / ".join(partes_eas)
144
 
145
- # Parte principal (omite ausentes)
146
  partes_main = [f"{r}: {resultados[r]}" for r in ordem if r in resultados]
147
  texto_main = " / ".join(partes_main)
148
 
149
- # Concatena
150
- texto_final = " / ".join([t for t in (texto_eas, texto_main) if t])
151
 
152
- # Gera CSV apenas com presentes
153
- df = pd.DataFrame(
154
- [(k, resultados[k]) for k in resultados],
155
- columns=["Exame","Valor"]
156
- )
157
  temp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
158
  df.to_csv(temp.name, index=False)
159
 
160
  return texto_final, temp.name
161
 
162
- # Interface Gradio
163
  with gr.Blocks() as demo:
164
- gr.Markdown("## 🧪 Extrator Avançado com OCR e EAS")
165
- pdf_file = gr.File(label="📄 PDF de exames", file_types=[".pdf"])
166
  btn = gr.Button("🔍 Extrair Exames")
167
- out_txt = gr.Textbox(label="📋 Exames Classificados", lines=12)
168
- dl = gr.File(label="📥 CSV")
169
- btn.click(extrair_exames_formatado, inputs=pdf_file, outputs=[out_txt, dl])
170
 
171
- if __name__ == '__main__':
172
  demo.launch()
 
 
1
+ # Instalações necessárias
2
+ !pip install pdfplumber gradio pandas pytesseract --quiet
3
 
4
  import fitz
5
  import re
 
10
  from PIL import Image, ImageEnhance, ImageFilter
11
  import io
12
 
13
+ # Faixas de referência para classificação
14
  faixas = {
15
+ "HB": (11.5, 16.5), "HT": (36, 50), "LEUCO": (4000, 11000), "PLT": (150000, 450000),
16
  "K+": (3.5, 5.5), "NA+": (135, 145), "UREIA": (10, 50), "CR": (0.6, 1.3),
17
  "TGO": (0, 40), "TGP": (0, 40), "ALB": (3.5, 5.0), "INR": (0.8, 1.2),
18
  "TAP": (10, 14), "TTP": (25, 35)
19
  }
20
 
21
  def classificar(nome, valor):
22
+ """Adiciona setas se valor numérico estiver fora da faixa de referência."""
 
 
23
  try:
24
+ raw = valor.replace(">", "").replace("<", "").strip()
25
  val = float(raw)
26
  if nome in faixas:
27
+ mn, mx = faixas[nome]
28
+ if val < mn: return f"{valor} ↓"
29
+ if val > mx: return f"{valor} "
 
 
30
  return valor
31
  except:
32
  return valor
33
 
34
+ def melhorar_imagem(img: Image.Image) -> Image.Image:
35
+ """Aumenta contraste e nitidez para OCR."""
36
  img = img.convert('L')
37
  img = ImageEnhance.Contrast(img).enhance(2)
38
+ return img.filter(ImageFilter.SHARPEN)
 
39
 
 
40
  def extrair_texto_pdf(pdf_file):
41
+ """Extrai texto nativo e via OCR de cada página."""
42
+ texto_nativo = []
43
  ocr_imgs = []
44
  with fitz.open(pdf_file.name) as doc:
45
  for page in doc:
46
+ texto_nativo.append(page.get_text())
47
  pix = page.get_pixmap(dpi=300)
48
  img = Image.open(io.BytesIO(pix.tobytes("png")))
49
  ocr_imgs.append(melhorar_imagem(img))
50
+ tn = " ".join(texto_nativo)
51
+ tn = re.sub(r'\s+', ' ', tn)
52
+ tocr = " ".join(pytesseract.image_to_string(im) for im in ocr_imgs)
53
+ tocr = re.sub(r'\s+', ' ', tocr)
54
+ return tn, tocr
55
+
56
+ # Padrões regex (case-insensitive) para todos os exames, incluindo Troponina Qualitativa
 
57
  exames = {
58
+ # Hemograma e diferenciais
59
+ "LEUCO": r"leuc[óo]citos.*?([\d.,]+)\s?/u?l",
60
  "B": r"bas[óo]filos.*?([\d.,]+)\s?%",
61
  "SS": r"segmentados.*?([\d.,]+)\s?%",
62
  "EOS": r"eosin[óo]filos.*?([\d.,]+)\s?%",
 
64
  "MONO": r"mon[óo]citos.*?([\d.,]+)\s?%",
65
  "HB": r"hemoglobina.*?([\d.,]+)\s?g/dl",
66
  "HT": r"hemat[óo]crito.*?([\d.,]+)\s?%",
67
+ "PLT": r"plaquetas.*?([\d.,]+).?/u?l",
68
+ # Bioquímica
69
  "AMIL": r"amilase.*?([\d.,]+)\s?u/l",
70
+ "LIP": r"lipase.*?([\d.,]+)\s?u/l",
71
+ "GLI": r"glicose.*?([\d.,]+)\s?mg/dl",
72
+ "LACTATO": r"lactato.*?([\d.,]+)\s?mmol/l",
73
  "ÁC UR": r"[áa]cido ur[íi]co.*?([\d.,]+)\s?mg/dl",
74
  "BT": r"bilirrubina total.*?([\d.,]+)\s?mg/dl",
75
  "BD": r"bilirrubina direta.*?([\d.,]+)\s?mg/dl",
76
  "BI": r"bilirrubina indireta.*?([\d.,]+)\s?mg/dl",
77
+ "CAI": r"c[áa]lcio ioniza(?:do)?.*?([\d.,]+)\s?mmol/l",
78
  "CA TOTAL":r"c[áa]lcio total.*?([\d.,]+)\s?mg/dl",
79
  "CL-": r"cloro.*?([\d.,]+)\s?mmol/l",
80
+ "MG++": r"magn[ée]sio.*?([\d.,]+)\s?mg/dl",
 
 
81
  "FÓS": r"f[oó]sforo.*?([\d.,]+)\s?mg/dl",
82
+ "UREIA": r"ureia.*?([\d.,]+)\s?mg/dl",
83
+ "CR": r"creatinina.*?([\d.,]+)\s?mg/dl",
84
+ # Hepática e proteínas
85
+ "TGO": r"tgo.*?([\d.,]+)\s?u/l",
86
+ "TGP": r"tgp.*?([\d.,]+)\s?u/l",
87
  "GGT": r"ggt.*?([\d.,]+)\s?u/l",
88
+ "FAL": r"fosfatase alcalina.*?([\d.,]+)\s?u/l",
 
 
 
 
 
89
  "ALB": r"albumina.*?([\d.,]+)\s?g/dl",
90
+ "PTN TOTAL":r"prote[ií]na total.*?([\d.,]+)\s?g/dl",
91
  "GLOB": r"globulina.*?([\d.,]+)\s?g/dl",
92
  "RELAÇÃO": r"rela[cç][ãa]o\s+a\/g.*?([\d.,]+)",
93
+ # Coagulação
94
+ "TAP": r"tempo de protrombina.*?resultado\s*([\d.,]+)",
95
+ "INR": r"inr\s*([\d.,]+)",
 
96
  "TTP": r"ttpa.*?([\d.,]+)\s?seg",
97
+ # Inflamatório
98
+ "PCR": r"pcr.*?resultado\s*([\d.,]+)",
99
+ # Cardíacos
100
  "CKMB": r"ck[- ]?mb.*?([\d.,]+)\s?u/l",
101
+ "CPK": r"cpk.*?resultado\s*([\d.,]+)",
102
+ "TROPO": r"troponina\s*(?!qual).*?([<>]?[\d.,]+)\s?ng/ml",
103
+ "TROPONINA QUAL": r"troponina qualitativa.*?resultado\s*([A-Za-z]+)",
104
+ # EAS (urina)
105
+ "LEUC ESTERASE": r"leuc[óo]cito esterase.*?([A-Za-z\+\-]+)",
106
+ "LEUCO EAS": r"leuc[óo]citos?.*?([\d]+\s*[-\/]\s*\d+)",
107
+ "HEMA EAS": r"hem[áa]cias?.*?([\d]+\s*[-\/]\s*\d+)",
108
+ "BACTERIAS": r"bact[ée]rias?.*?([A-Za-z]+)"
109
  }
110
 
111
+ # Ordem preferencial de exibição (numéricos e qualitativos)
112
  ordem = [
113
  "LEUCO","B","SS","EOS","LINF","MONO",
114
+ "HB","HT","PLT","AMIL","LIP","GLI","LACTATO",
115
+ "ÁC UR","BT","BD","BI","CAI","CA TOTAL","CL-","MG++","FÓS","UREIA","CR",
116
+ "TGO","TGP","GGT","FAL","ALB","PTN TOTAL","GLOB","RELAÇÃO",
117
+ "TAP","INR","TTP","PCR","DIMERO D",
118
+ "CKMB","CPK","TROPO","TROPONINA QUAL"
 
119
  ]
120
 
121
  def extrair_exames_formatado(pdf_file):
122
  if not pdf_file:
123
  return "Nenhum arquivo enviado.", None
124
+ # extrai texto
125
+ tn, tocr = extrair_texto_pdf(pdf_file)
126
+ textos = tn + " " + tocr
127
  resultados = {}
128
+
129
+ # varre todos os padrões
130
+ for nome, pat in exames.items():
131
+ m = re.search(pat, textos, re.IGNORECASE)
132
+ if m:
133
+ val = m.group(1).strip().replace(",", ".")
134
+ # normaliza QUAL como uppercase
135
+ if nome == "TROPONINA QUAL":
136
+ val = val.upper()
137
+ resultados[nome] = classificar(nome, val)
138
+
139
+ # monta string de EAS
140
  eas_chaves = ["LEUC ESTERASE","LEUCO EAS","HEMA EAS","BACTERIAS"]
141
  partes_eas = [f"{k}: {resultados[k]}" for k in eas_chaves if k in resultados]
142
  texto_eas = ""
143
  if partes_eas:
144
+ texto_eas = "🟤 EAS (Urinálise) → " + " / ".join(partes_eas)
145
 
146
+ # monta string principal
147
  partes_main = [f"{r}: {resultados[r]}" for r in ordem if r in resultados]
148
  texto_main = " / ".join(partes_main)
149
 
150
+ # concatena só as partes não vazias
151
+ texto_final = "\n".join([t for t in (texto_eas, texto_main) if t])
152
 
153
+ # gera CSV
154
+ df = pd.DataFrame([[k, resultados[k]] for k in resultados], columns=["Exame","Valor"])
 
 
 
155
  temp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
156
  df.to_csv(temp.name, index=False)
157
 
158
  return texto_final, temp.name
159
 
160
+ # interface Gradio
161
  with gr.Blocks() as demo:
162
+ gr.Markdown("## 🧪 Extrator Avançado com OCR + EAS + Troponina Qualitativa")
163
+ pdf_input = gr.File(label="📄 PDF de exames", file_types=[".pdf"])
164
  btn = gr.Button("🔍 Extrair Exames")
165
+ out_txt = gr.Textbox(label="📋 Resultados", lines=8)
166
+ dl = gr.File(label="📥 Baixar CSV")
167
+ btn.click(extrair_exames_formatado, inputs=pdf_input, outputs=[out_txt, dl])
168
 
169
+ if __name__ == "__main__":
170
  demo.launch()
171
+