GABRIELSZK commited on
Commit
e444dfe
·
verified ·
1 Parent(s): 07c89f0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -86
app.py CHANGED
@@ -6,52 +6,43 @@ import tempfile
6
  import pytesseract
7
  from PIL import Image, ImageEnhance, ImageFilter
8
  import io
 
9
 
10
- # 🎯 Faixas de referência (valores de referência mínimos e máximos)
11
  faixas = {
12
- "LEUCO": (4000, 11000),
13
- "B": (0, 1), "SS": (45, 59), "EOS": (1, 6), "LINF": (30, 50), "MONO": (1, 8),
14
- "HB": (12, 17), "HT": (36, 50), "PLT": (150, 450),
15
- "AMIL": (25, 125), "ÁC UR": (3.5, 7.2),
16
- "BT": (0.3, 1.2), "BD": (0.0, 0.3), "BI": (0.1, 0.8),
17
- "CAI": (1.1, 1.3), "CA TOTAL": (8.5, 10.2),
18
- "CL-": (98, 107), "CR": (0.6, 1.3), "UREIA": (17, 49),
19
- "FAL": (44, 147), "FÓS": (2.5, 4.5), "GGT": (8, 61),
20
- "GLI": (70, 99), "LIP": (10, 140), "MG++": (1.7, 2.2),
21
- "PCR": (0, 5), "K+": (3.5, 5.1), "NA+": (135, 145),
22
- "PTN": (6.0, 8.3), "ALB": (3.5, 5.0), "GLOB": (2.3, 3.5),
23
- "RELAÇÃO": (1.0, 2.2),
24
- "TGO": (0, 40), "TGP": (0, 40),
25
- "TAP": (10, 14), "INR": (0.8, 1.2), "TTP": (25, 35),
26
- "DIMERO D": (0, 500), "LAC": (0.5, 2.2),
27
- "CKMB": (0, 25), "CPK": (20, 200),
28
- "TROPONINA": (0, 0.5)
29
  }
30
 
31
  def classificar(nome, valor):
32
  try:
33
- v = float(valor.replace(">", "").replace("<", "").strip())
34
  if nome in faixas:
35
  lo, hi = faixas[nome]
36
- if v < lo:
37
- return f"{valor} "
38
- if v > hi:
39
- return f"{valor} ↑"
40
  return valor
41
  except:
42
  return valor
43
 
44
- # Ajustes para melhorar OCR
45
  def melhorar_imagem(img: Image.Image) -> Image.Image:
46
  img = img.convert("L")
47
  img = ImageEnhance.Contrast(img).enhance(2)
48
  return img.filter(ImageFilter.SHARPEN)
49
 
50
- # Extrai texto nativo + OCR do PDF
 
 
51
  def extrair_texto_pdf(pdf_input):
52
  if isinstance(pdf_input, dict):
53
  pdf_path = pdf_input.get("name") or pdf_input.get("file_path")
54
- elif hasattr(pdf_input, "name") and isinstance(pdf_input.name, str):
55
  pdf_path = pdf_input.name
56
  else:
57
  pdf_path = str(pdf_input)
@@ -59,67 +50,57 @@ def extrair_texto_pdf(pdf_input):
59
  texto_nativo, ocr_imgs = [], []
60
  with fitz.open(pdf_path) as doc:
61
  for page in doc:
62
- texto_nativo.append(page.get_text())
63
- pix = page.get_pixmap(dpi=300)
64
- img = Image.open(io.BytesIO(pix.tobytes("png")))
65
- ocr_imgs.append(melhorar_imagem(img))
 
 
 
66
  tn = re.sub(r"\s+", " ", "".join(texto_nativo))
67
- tocr = re.sub(r"\s+", " ", " ".join(pytesseract.image_to_string(im) for im in ocr_imgs))
 
 
 
 
68
  return tn, tocr
69
 
70
- # Padrões de extração com word boundaries e unidades obrigatórias
71
  exames = {
72
  "LEUCO": r"\bleuc[óo]citos\b.*?([\d.,]+)\s*/u?l",
73
- "B": r"\bbastonetes\b.*?([\d.,]+)\s?%",
74
- "SS": r"\bsegmentados\b.*?([\d.,]+)\s?%",
75
- "EOS": r"\beosin[óo]filos\b.*?([\d.,]+)\s?%",
76
  "LINF": r"\blinf[oó]citos\b.*?([\d.,]+)\s?%",
77
  "MONO": r"\bmon[óo]citos\b.*?([\d.,]+)\s?%",
78
- "HB": r"\bhemoglobina\b.*?([\d.,]+)\s?g/dl",
79
- "HT": r"\bhemat[óo]crito\b.*?([\d.,]+)\s?%",
80
- "PLT": r"\bplaquetas\b.*?([\d.,]+)\s*/u?l",
81
  "AMIL": r"\bamilase\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
82
- "BT": r"\bbilirrubina total\b.*?([\d.,]+)\s?mg/dl",
83
- "BD": r"\bbilirrubina direta\b.*?([\d.,]+)\s?mg/dl",
84
- "BI": r"\bbilirrubina indireta\b.*?([\d.,]+)\s?mg/dl",
85
- "CR": r"\bcreatinina\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
86
- "UREIA":r"\bureia\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
87
- "FAL": r"\bfosfatase alcalina\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
88
- "GGT": r"\bggt\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
89
- "TGO": r"\btgo\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
90
- "TGP": r"\btgp\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
91
- "GLI": r"\bglicose\b(?! qualitativa).*?resultado[:\s]*([\d.,]+)\s?mg/dl",
92
- "LIP": r"\blipase\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
93
  "MG++": r"\bmagn[eé]sio\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
94
- "TAP": r"\btempo de protrombina\b.*?resultado[:\s]*([\d.,]+)",
95
- "INR": r"\binr\b.*?([\d.,]+)",
96
- "TTP": r"\bttpa\b.*?resultado[:\s]*([\d.,]+)",
97
  "DIMERO D": r"\bd[ií]mero d\b.*?resultado[:\s]*([\d.,]+)",
98
- "PCR": r"\bpcr\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
99
- "CKMB": r"\bck[- ]?mb\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
100
- "CPK": r"\bcpk\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
101
- "TROPONINA": r"troponina(?! qualitativa).*?resultado[:\s]*([><\d.,]+)(?=\s*ng\/m[lL])",
102
- "TROPONINA QUAL": r"troponina qualitativa.*?resultado[:\s]*(positivo|negativo)",
103
- "PROTEINA UR": r"\bprote[ií]na\b.*?\b(ausente|positivo|negativo)",
104
- "GLI UR": r"\bglicose\b.*?\b(ausente|positivo|negativo)",
105
- "CETONAS UR": r"\bcorpos cet[oô]nicos\b.*?\b(ausente|positivo|negativo)",
106
- "SANGUE UR": r"\bsangue\b.*?\b(ausente|positivo|negativo)",
107
- "LEUC ESTERASE": r"\bleuc[óo]citos? esterase\b.*?\b(ausente|positivo|negativo)",
108
- "NITRITO UR": r"\bnitrito\b.*?\b(ausente|positivo|negativo)",
109
- "LEUCO EAS": r"\bleuc[óo]citos?\b\s*([\d]+[-\/–][\d]+)",
110
- "HEMA EAS": r"\bhem[áa]cias?\b\s*([\d]+[-\/–][\d]+)",
111
- "BACTERIAS UR": r"\bbact[ée]rias?\b.*?\b(raras|ausentes|positivas|negativas)"
112
  }
 
113
 
114
- # Ordem de exibição
115
- ordem = [
116
- "LEUCO","B","SS","EOS","LINF","MONO",
117
- "HB","HT","PLT","AMIL","BT","BD","BI",
118
- "CR","UREIA","FAL","GGT","TGO","TGP","GLI","LIP","MG++",
119
- "PCR","CKMB","CPK","TROPONINA","TROPONINA QUAL",
120
- "TAP","INR","TTP","DIMERO D",
121
- "PROTEINA UR","GLI UR","CETONAS UR","SANGUE UR","LEUC ESTERASE","NITRITO UR","LEUCO EAS","HEMA EAS","BACTERIAS UR"
122
- ]
123
 
124
  def extrair_exames_formatado(pdf_file):
125
  if not pdf_file:
@@ -127,8 +108,8 @@ def extrair_exames_formatado(pdf_file):
127
  tn, tocr = extrair_texto_pdf(pdf_file)
128
  texto = (tn + " " + tocr).lower()
129
  resultados = {}
130
- for nome, pat in exames.items():
131
- m = re.search(pat, texto, re.IGNORECASE)
132
  if not m:
133
  continue
134
  raw = m.group(1).strip().upper()
@@ -137,22 +118,16 @@ def extrair_exames_formatado(pdf_file):
137
  else:
138
  resultados[nome] = classificar(nome, raw.replace(",", "."))
139
 
140
- eas_fields = [f"{k}: {resultados[k]}" for k in ordem if k in resultados and (k.endswith("UR") or k.endswith("EAS"))]
141
- main_fields = [f"{r}: {resultados[r]}" for r in ordem if r in resultados and not (r.endswith("UR") or r.endswith("EAS"))]
142
- line_eas = f"🟤 EAS → {' / '.join(eas_fields)}" if eas_fields else ""
143
- line_main = ' / '.join(main_fields)
144
- final = '\n'.join([l for l in (line_eas, line_main) if l])
145
-
146
- # Gera CSV
147
  df = pd.DataFrame([[k, resultados[k]] for k in resultados], columns=["Exame", "Valor"])
148
  tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
149
  df.to_csv(tmp.name, index=False)
150
  return final, tmp.name
151
 
152
- # Interface Gradio
153
  demo = gr.Blocks()
154
  with demo:
155
- gr.Markdown("## 🧪 Extrator Avançado com OCR + EAS + Troponina (Quant. e Qual.)")
156
  pdf_input = gr.File(file_types=[".pdf"], label="📄 PDF de exames")
157
  btn = gr.Button("🔍 Extrair")
158
  out_txt = gr.Textbox(lines=15, label="📋 Resultados")
 
6
  import pytesseract
7
  from PIL import Image, ImageEnhance, ImageFilter
8
  import io
9
+ from concurrent.futures import ThreadPoolExecutor
10
 
 
11
  faixas = {
12
+ "LEUCO": (4000, 11000), "B": (0, 1), "SS": (45, 59), "EOS": (1, 6),
13
+ "LINF": (30, 50), "MONO": (1, 8), "HB": (12, 17), "HT": (36, 50), "PLT": (150, 450),
14
+ "AMIL": (25, 125), "ÁC UR": (3.5, 7.2), "BT": (0.3, 1.2), "BD": (0.0, 0.3), "BI": (0.1, 0.8),
15
+ "CAI": (1.1, 1.3), "CA TOTAL": (8.5, 10.2), "CL-": (98, 107), "CR": (0.6, 1.3), "UREIA": (17, 49),
16
+ "FAL": (44, 147), "FÓS": (2.5, 4.5), "GGT": (8, 61), "GLI": (70, 99), "LIP": (10, 140),
17
+ "MG++": (1.7, 2.2), "PCR": (0, 5), "K+": (3.5, 5.1), "NA+": (135, 145), "PTN": (6.0, 8.3),
18
+ "ALB": (3.5, 5.0), "GLOB": (2.3, 3.5), "RELAÇÃO": (1.0, 2.2), "TGO": (0, 40), "TGP": (0, 40),
19
+ "TAP": (10, 14), "INR": (0.8, 1.2), "TTP": (25, 35), "DIMERO D": (0, 500), "LAC": (0.5, 2.2),
20
+ "CKMB": (0, 25), "CPK": (20, 200), "TROPONINA": (0, 0.5)
 
 
 
 
 
 
 
 
21
  }
22
 
23
  def classificar(nome, valor):
24
  try:
25
+ v = float(valor.replace("<", "").replace(">", "").strip())
26
  if nome in faixas:
27
  lo, hi = faixas[nome]
28
+ if v < lo: return f"{valor} ↓"
29
+ if v > hi: return f"{valor} "
 
 
30
  return valor
31
  except:
32
  return valor
33
 
 
34
  def melhorar_imagem(img: Image.Image) -> Image.Image:
35
  img = img.convert("L")
36
  img = ImageEnhance.Contrast(img).enhance(2)
37
  return img.filter(ImageFilter.SHARPEN)
38
 
39
+ def ocr_image(img):
40
+ return pytesseract.image_to_string(img)
41
+
42
  def extrair_texto_pdf(pdf_input):
43
  if isinstance(pdf_input, dict):
44
  pdf_path = pdf_input.get("name") or pdf_input.get("file_path")
45
+ elif hasattr(pdf_input, "name"):
46
  pdf_path = pdf_input.name
47
  else:
48
  pdf_path = str(pdf_input)
 
50
  texto_nativo, ocr_imgs = [], []
51
  with fitz.open(pdf_path) as doc:
52
  for page in doc:
53
+ t = page.get_text()
54
+ texto_nativo.append(t)
55
+ if not t.strip():
56
+ pix = page.get_pixmap(dpi=200)
57
+ img = Image.open(io.BytesIO(pix.tobytes("png")))
58
+ ocr_imgs.append(melhorar_imagem(img))
59
+
60
  tn = re.sub(r"\s+", " ", "".join(texto_nativo))
61
+ tocr = ""
62
+ if ocr_imgs:
63
+ with ThreadPoolExecutor() as executor:
64
+ tocr = " ".join(executor.map(ocr_image, ocr_imgs))
65
+ tocr = re.sub(r"\s+", " ", tocr)
66
  return tn, tocr
67
 
 
68
  exames = {
69
  "LEUCO": r"\bleuc[óo]citos\b.*?([\d.,]+)\s*/u?l",
70
+ "B": r"\bbastonetes\b.*?([\d.,]+)\s?%",
71
+ "SS": r"\bsegmentados\b.*?([\d.,]+)\s?%",
72
+ "EOS": r"\beosin[óo]filos\b.*?([\d.,]+)\s?%",
73
  "LINF": r"\blinf[oó]citos\b.*?([\d.,]+)\s?%",
74
  "MONO": r"\bmon[óo]citos\b.*?([\d.,]+)\s?%",
75
+ "HB": r"\bhemoglobina\b.*?([\d.,]+)\s?g/dl",
76
+ "HT": r"\bhemat[óo]crito\b.*?([\d.,]+)\s?%",
77
+ "PLT": r"\bplaquetas\b.*?([\d.,]+)\s*/u?l",
78
  "AMIL": r"\bamilase\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
79
+ "BT": r"\bbilirrubina total\b.*?([\d.,]+)\s?mg/dl",
80
+ "BD": r"\bbilirrubina direta\b.*?([\d.,]+)\s?mg/dl",
81
+ "BI": r"\bbilirrubina indireta\b.*?([\d.,]+)\s?mg/dl",
82
+ "CR": r"\bcreatinina\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
83
+ "UREIA": r"\bureia\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
84
+ "FAL": r"\bfosfatase alcalina\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
85
+ "GGT": r"\bggt\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
86
+ "TGO": r"\btgo\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
87
+ "TGP": r"\btgp\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
88
+ "GLI": r"\bglicose\b(?! qualitativa).*?resultado[:\s]*([\d.,]+)\s?mg/dl",
89
+ "LIP": r"\blipase\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
90
  "MG++": r"\bmagn[eé]sio\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
91
+ "TAP": r"\btempo de protrombina\b.*?resultado[:\s]*([\d.,]+)",
92
+ "INR": r"\binr\b.*?([\d.,]+)",
93
+ "TTP": r"\bttpa\b.*?resultado[:\s]*([\d.,]+)",
94
  "DIMERO D": r"\bd[ií]mero d\b.*?resultado[:\s]*([\d.,]+)",
95
+ "PCR": r"\bpcr\b.*?resultado[:\s]*([\d.,]+)\s?mg/dl",
96
+ "CKMB": r"\bck[- ]?mb\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
97
+ "CPK": r"\bcpk\b.*?resultado[:\s]*([\d.,]+)\s?u/l",
98
+ "TROPONINA": r"troponina(?! qualitativa).*?resultado[:\s]*([><\d.,]+)(?=\s*ng/m[lL])",
99
+ "TROPONINA QUAL": r"troponina qualitativa.*?resultado[:\s]*(positivo|negativo)"
 
 
 
 
 
 
 
 
 
100
  }
101
+ regex_compilado = {k: re.compile(v, re.IGNORECASE) for k, v in exames.items()}
102
 
103
+ ordem = list(exames.keys())
 
 
 
 
 
 
 
 
104
 
105
  def extrair_exames_formatado(pdf_file):
106
  if not pdf_file:
 
108
  tn, tocr = extrair_texto_pdf(pdf_file)
109
  texto = (tn + " " + tocr).lower()
110
  resultados = {}
111
+ for nome, pat in regex_compilado.items():
112
+ m = pat.search(texto)
113
  if not m:
114
  continue
115
  raw = m.group(1).strip().upper()
 
118
  else:
119
  resultados[nome] = classificar(nome, raw.replace(",", "."))
120
 
121
+ linhas = [f"{nome}: {resultados[nome]}" for nome in ordem if nome in resultados]
122
+ final = " / ".join(linhas)
 
 
 
 
 
123
  df = pd.DataFrame([[k, resultados[k]] for k in resultados], columns=["Exame", "Valor"])
124
  tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".csv")
125
  df.to_csv(tmp.name, index=False)
126
  return final, tmp.name
127
 
 
128
  demo = gr.Blocks()
129
  with demo:
130
+ gr.Markdown("## 🧪 Extrator Rápido com OCR seletivo")
131
  pdf_input = gr.File(file_types=[".pdf"], label="📄 PDF de exames")
132
  btn = gr.Button("🔍 Extrair")
133
  out_txt = gr.Textbox(lines=15, label="📋 Resultados")