Lucas Gagneten commited on
Commit
ced724a
·
1 Parent(s): a77e9d7

fix: Mejorar manejo de errores y configuración de Tesseract

Browse files
Files changed (1) hide show
  1. app.py +105 -38
app.py CHANGED
@@ -22,22 +22,70 @@ ALL_NER_TAGS = [
22
  ]
23
  ALL_NER_TAGS = sorted(list(set(ALL_NER_TAGS))) # Limpiar y ordenar
24
 
25
- # Configuración de Tesseract (la instalación se maneja con setup.sh)
26
- # Asegurar que Tesseract utiliza el idioma español
27
- pytesseract.tesseract_cmd = 'tesseract'
 
 
 
 
 
 
 
 
 
28
 
29
  # --- 2. FUNCIONES DE PROCESAMIENTO ---
30
 
31
  def get_ocr_data(image: Image.Image):
32
  """Ejecuta Tesseract y devuelve la imagen, tokens y bboxes normalizados."""
33
  if image is None:
34
- return None, []
35
 
36
- W, H = image.size
37
- # Obtener datos de la imagen con idioma español
38
- hocr_data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT, lang='spa')
39
 
40
- tokens_data = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
42
  for i in range(len(hocr_data['text'])):
43
  text = hocr_data['text'][i].strip()
@@ -67,7 +115,12 @@ def get_ocr_data(image: Image.Image):
67
  'ner_tag': 'O' # Inicializar con 'O'
68
  })
69
 
70
- return image, tokens_data
 
 
 
 
 
71
 
72
  def draw_boxes(image: Image.Image, tokens_data: list, highlight_index: int = -1):
73
  """Dibuja un resaltado en la imagen para el bounding box seleccionado."""
@@ -94,8 +147,12 @@ def process_and_setup(image_file):
94
  empty_df = {'token': [], 'ner_tag': []}
95
  return None, [], None, empty_df, "Cargue una imagen para comenzar."
96
 
97
- image_orig, tokens_data = get_ocr_data(image_file)
98
 
 
 
 
 
99
  if not tokens_data:
100
  empty_df = {'token': [], 'ner_tag': []}
101
  return image_orig, [], None, empty_df, "OCR completado. No se detectaron tokens válidos."
@@ -141,34 +198,40 @@ def export_data(image_orig: Image.Image, tokens_data: list):
141
  if not tokens_data:
142
  return None, "Error: No hay datos de anotación para exportar."
143
 
144
- # Se usa JSON estructurado en lugar de PASCAL VOC, ya que PASCAL VOC es para
145
- # detección de objetos, y este es un problema de NER a nivel de token/bbox,
146
- # siendo JSON el formato estándar para el fine-tuning de LayoutXLM.
147
-
148
- W, H = image_orig.size
149
-
150
- output_data = {
151
- 'metadata': {
152
- 'image_size': [W, H],
153
- 'format': 'Structured JSON for LayoutXLM Fine-Tuning',
154
- 'note': 'Contains tokens, bboxes normalized to 0-1000, and NER tags.'
155
- },
156
- 'annotations': []
157
- }
158
-
159
- for item in tokens_data:
160
- output_data['annotations'].append({
161
- 'token': item['token'],
162
- 'bbox_normalized': item['bbox_norm'],
163
- 'ner_tag': item['ner_tag']
164
- })
165
-
166
- # Guardar el archivo temporal en el disco del Space para que pueda ser descargado
167
- temp_file = "anotacion_factura.json"
168
- with open(temp_file, 'w', encoding='utf-8') as f:
169
- json.dump(output_data, f, ensure_ascii=False, indent=4)
170
 
171
- return temp_file, "✅ Exportación exitosa. Descarga el archivo JSON."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
172
 
173
  # --- 3. INTERFAZ GRADIO (GR.BLOCKS) ---
174
 
@@ -257,4 +320,8 @@ with gr.Blocks(title="Anotador NER de Facturas (LayoutXLM)") as app:
257
  )
258
 
259
  if __name__ == "__main__":
260
- app.launch()
 
 
 
 
 
22
  ]
23
  ALL_NER_TAGS = sorted(list(set(ALL_NER_TAGS))) # Limpiar y ordenar
24
 
25
+ # Configuración de Tesseract
26
+ # En Windows, necesitamos especificar la ruta completa al ejecutable de Tesseract
27
+ if os.name == 'nt': # Windows
28
+ tesseract_path = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
29
+ if os.path.exists(tesseract_path):
30
+ pytesseract.tesseract_cmd = tesseract_path
31
+ else:
32
+ print("ADVERTENCIA: Tesseract no encontrado en la ruta por defecto de Windows.")
33
+ print("Por favor, instale Tesseract-OCR desde: https://github.com/UB-Mannheim/tesseract/wiki")
34
+ print("O actualice la variable pytesseract.tesseract_cmd con la ruta correcta.")
35
+ else: # Linux/Mac
36
+ pytesseract.tesseract_cmd = 'tesseract'
37
 
38
  # --- 2. FUNCIONES DE PROCESAMIENTO ---
39
 
40
  def get_ocr_data(image: Image.Image):
41
  """Ejecuta Tesseract y devuelve la imagen, tokens y bboxes normalizados."""
42
  if image is None:
43
+ return None, [], "Error: No se proporcionó ninguna imagen"
44
 
45
+ if not os.path.exists(pytesseract.tesseract_cmd):
46
+ return None, [], "Error: Tesseract no está instalado o la ruta no es correcta"
 
47
 
48
+ try:
49
+ W, H = image.size
50
+
51
+ # Obtener datos de la imagen con idioma español
52
+ hocr_data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT, lang='spa')
53
+ tokens_data = []
54
+
55
+ for i in range(len(hocr_data['text'])):
56
+ text = hocr_data['text'][i].strip()
57
+
58
+ # Filtro: Nivel de palabra (5), texto no vacío, confianza > 50
59
+ if hocr_data['level'][i] == 5 and text and hocr_data['conf'][i] > 50:
60
+ left = hocr_data['left'][i]
61
+ top = hocr_data['top'][i]
62
+ width = hocr_data['width'][i]
63
+ height = hocr_data['height'][i]
64
+
65
+ # BBox normalizado a 0-1000 (para LayoutXLM)
66
+ bbox_normalized = [
67
+ int(left * 1000 / W),
68
+ int(top * 1000 / H),
69
+ int((left + width) * 1000 / W),
70
+ int((top + height) * 1000 / H)
71
+ ]
72
+
73
+ # BBox original (en píxeles, para dibujar)
74
+ bbox_original = [left, top, left + width, top + height]
75
+
76
+ tokens_data.append({
77
+ 'token': text,
78
+ 'bbox_norm': bbox_normalized,
79
+ 'bbox_orig': bbox_original,
80
+ 'ner_tag': 'O' # Inicializar con 'O'
81
+ })
82
+
83
+ return image, tokens_data, None
84
+
85
+ except Exception as e:
86
+ if "tesseract is not installed" in str(e):
87
+ return None, [], "Error: Tesseract no está instalado o no se encuentra en el PATH del sistema"
88
+ return None, [], f"Error durante el OCR: {str(e)}"
89
 
90
  for i in range(len(hocr_data['text'])):
91
  text = hocr_data['text'][i].strip()
 
115
  'ner_tag': 'O' # Inicializar con 'O'
116
  })
117
 
118
+ return image, tokens_data, None
119
+
120
+ except pytesseract.TesseractNotFoundError:
121
+ return None, [], "Error: Tesseract no está instalado o no se encuentra en el PATH del sistema"
122
+ except Exception as e:
123
+ return None, [], f"Error durante el OCR: {str(e)}"
124
 
125
  def draw_boxes(image: Image.Image, tokens_data: list, highlight_index: int = -1):
126
  """Dibuja un resaltado en la imagen para el bounding box seleccionado."""
 
147
  empty_df = {'token': [], 'ner_tag': []}
148
  return None, [], None, empty_df, "Cargue una imagen para comenzar."
149
 
150
+ image_orig, tokens_data, error_msg = get_ocr_data(image_file)
151
 
152
+ if error_msg:
153
+ empty_df = {'token': [], 'ner_tag': []}
154
+ return None, [], None, empty_df, error_msg
155
+
156
  if not tokens_data:
157
  empty_df = {'token': [], 'ner_tag': []}
158
  return image_orig, [], None, empty_df, "OCR completado. No se detectaron tokens válidos."
 
198
  if not tokens_data:
199
  return None, "Error: No hay datos de anotación para exportar."
200
 
201
+ try:
202
+ # Se usa JSON estructurado en lugar de PASCAL VOC, ya que PASCAL VOC es para
203
+ # detección de objetos, y este es un problema de NER a nivel de token/bbox,
204
+ # siendo JSON el formato estándar para el fine-tuning de LayoutXLM.
205
+
206
+ W, H = image_orig.size
207
+
208
+ output_data = {
209
+ 'metadata': {
210
+ 'image_size': [W, H],
211
+ 'format': 'Structured JSON for LayoutXLM Fine-Tuning',
212
+ 'note': 'Contains tokens, bboxes normalized to 0-1000, and NER tags.'
213
+ },
214
+ 'annotations': []
215
+ }
 
 
 
 
 
 
 
 
 
 
 
216
 
217
+ for item in tokens_data:
218
+ output_data['annotations'].append({
219
+ 'token': item['token'],
220
+ 'bbox_normalized': item['bbox_norm'],
221
+ 'ner_tag': item['ner_tag']
222
+ })
223
+
224
+ # Guardar el archivo temporal en el disco del Space para que pueda ser descargado
225
+ temp_file = "anotacion_factura.json"
226
+ with open(temp_file, 'w', encoding='utf-8') as f:
227
+ json.dump(output_data, f, ensure_ascii=False, indent=4)
228
+
229
+ return temp_file, "✅ Exportación exitosa. Descarga el archivo JSON."
230
+
231
+ except IOError as e:
232
+ return None, f"Error al guardar el archivo: {str(e)}"
233
+ except Exception as e:
234
+ return None, f"Error durante la exportación: {str(e)}"
235
 
236
  # --- 3. INTERFAZ GRADIO (GR.BLOCKS) ---
237
 
 
320
  )
321
 
322
  if __name__ == "__main__":
323
+ try:
324
+ app.launch()
325
+ except Exception as e:
326
+ print(f"Error crítico durante la ejecución de la aplicación: {str(e)}")
327
+ raise