Vladt-Tempest commited on
Commit
63995e0
·
1 Parent(s): c4fb207

app y comercial_invoice están funcionando trayendo inf. hasta producto 1

Browse files
Files changed (6) hide show
  1. .gitignore +8 -1
  2. app.py +6 -0
  3. commercial_invoice.py +125 -56
  4. coordinates_CI.json +6 -142
  5. packages.txt +2 -1
  6. test.py +72 -0
.gitignore CHANGED
@@ -7,4 +7,11 @@ ENV/
7
  pyvenv.cfg
8
 
9
  # Carpeta de trabajo
10
- invoices/
 
 
 
 
 
 
 
 
7
  pyvenv.cfg
8
 
9
  # Carpeta de trabajo
10
+ invoices/
11
+
12
+ # Log de errores
13
+ *.log
14
+
15
+ # archivos de resultados
16
+ data/
17
+
app.py CHANGED
@@ -10,6 +10,12 @@ def procesar_pdf(pdf_archivo):
10
  if not os.path.exists(carpeta_salida):
11
  os.makedirs(carpeta_salida)
12
 
 
 
 
 
 
 
13
  # Convertir el PDF a imágenes
14
  paginas = convert_from_path(pdf_archivo, dpi=300)
15
 
 
10
  if not os.path.exists(carpeta_salida):
11
  os.makedirs(carpeta_salida)
12
 
13
+ # Si en carpeta_salida ya hay archivos, eliminarlos
14
+ for archivo in os.listdir(carpeta_salida):
15
+ archivo_path = os.path.join(carpeta_salida, archivo)
16
+ if os.path.isfile(archivo_path):
17
+ os.remove(archivo_path)
18
+
19
  # Convertir el PDF a imágenes
20
  paginas = convert_from_path(pdf_archivo, dpi=300)
21
 
commercial_invoice.py CHANGED
@@ -1,78 +1,147 @@
1
  import json
2
  from PIL import Image
3
  import pytesseract
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4
 
5
  def load_field_areas(coordinates_json):
6
  """Carga y procesa las coordenadas desde el archivo JSON"""
7
- with open(coordinates_json, 'r') as f:
8
- data = json.load(f)
9
-
10
- field_areas = {}
11
- for box in data['boxes']:
12
- x = float(box['x'])
13
- y = float(box['y'])
14
- width = float(box['width'])
15
- height = float(box['height'])
16
 
17
- field_areas[box['label']] = {
18
- "x1": int(x - width/2),
19
- "y1": int(y - height/2),
20
- "x2": int(x + width/2),
21
- "y2": int(y + height/2)
22
- }
23
- return field_areas, data['width'], data['height']
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  def extract_text_from_area(image, area, margin=10):
26
  """Extrae texto de un área específica de la imagen con margen de tolerancia"""
27
- # Aplicar margen a las coordenadas
28
- x1 = max(0, area["x1"] - margin)
29
- y1 = max(0, area["y1"] - margin)
30
- x2 = min(image.width, area["x2"] + margin)
31
- y2 = min(image.height, area["y2"] + margin)
 
 
32
 
33
- # Recortar la imagen al área especificada
34
- crop = image.crop((x1, y1, x2, y2))
35
 
36
- # Configurar parámetros de OCR para mejor precisión
37
- custom_config = r'--oem 3 --psm 6'
38
- text = pytesseract.image_to_string(crop, lang='eng', config=custom_config).strip()
39
- return text
 
 
 
 
 
 
 
 
40
 
41
  def process_invoice(image_path, coordinates_json, margin=10):
42
  """Procesa la factura y extrae los campos con margen de tolerancia"""
43
- # Cargar imagen
44
- image = Image.open(image_path)
45
-
46
- # Cargar áreas de los campos
47
- field_areas, img_width, img_height = load_field_areas(coordinates_json)
48
-
49
- # Ajustar imagen si es necesario
50
- if image.size != (img_width, img_height):
51
- image = image.resize((img_width, img_height))
52
-
53
- # Extraer texto de cada área
54
- extracted_fields = {}
55
- for label, area in field_areas.items():
56
- text = extract_text_from_area(image, area, margin)
57
- if text:
58
- extracted_fields[label] = text
59
- else:
60
- # Si no se encuentra texto, intentar con un margen mayor
61
- text = extract_text_from_area(image, area, margin * 2)
62
  if text:
63
- extracted_fields[label] = text
 
 
 
 
 
64
 
65
- return extracted_fields
 
 
 
 
 
 
 
66
 
67
  if __name__ == "__main__":
68
- # Rutas de archivos
69
- image_path = "./invoices/pagina_9.jpg"
 
 
 
70
  coordinates_json = "./coordinates_CI.json"
71
 
72
- # Procesar factura
73
- results = process_invoice(image_path, coordinates_json, margin=10)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- # Imprimir resultados
76
- print("\nCampos encontrados:")
77
- for field, value in results.items():
78
- print(f"{field}: {value}")
 
1
  import json
2
  from PIL import Image
3
  import pytesseract
4
+ import pandas as pd
5
+ import os
6
+ from pathlib import Path
7
+ import logging
8
+
9
+ # Configurar logging
10
+ logging.basicConfig(
11
+ level=logging.INFO,
12
+ format='%(asctime)s - %(levelname)s - %(message)s',
13
+ handlers=[
14
+ logging.FileHandler('invoice_processing.log'),
15
+ logging.StreamHandler()
16
+ ]
17
+ )
18
+ logger = logging.getLogger(__name__)
19
 
20
  def load_field_areas(coordinates_json):
21
  """Carga y procesa las coordenadas desde el archivo JSON"""
22
+ logger.debug(f"Cargando coordenadas desde: {coordinates_json}")
23
+ try:
24
+ with open(coordinates_json, 'r') as f:
25
+ data = json.load(f)
 
 
 
 
 
26
 
27
+ field_areas = {}
28
+ for box in data['boxes']:
29
+ x = float(box['x'])
30
+ y = float(box['y'])
31
+ width = float(box['width'])
32
+ height = float(box['height'])
33
+
34
+ field_areas[box['label']] = {
35
+ "x1": int(x - width/2),
36
+ "y1": int(y - height/2),
37
+ "x2": int(x + width/2),
38
+ "y2": int(y + height/2)
39
+ }
40
+ logger.debug(f"Se cargaron {len(field_areas)} áreas de campos")
41
+ return field_areas, data['width'], data['height']
42
+ except Exception as e:
43
+ logger.error(f"Error al cargar coordenadas: {str(e)}")
44
+ raise
45
 
46
  def extract_text_from_area(image, area, margin=10):
47
  """Extrae texto de un área específica de la imagen con margen de tolerancia"""
48
+ logger.debug(f"Extrayendo texto del área: {area} con margen: {margin}")
49
+ try:
50
+ # Aplicar margen a las coordenadas
51
+ x1 = max(0, area["x1"] - margin)
52
+ y1 = max(0, area["y1"] - margin)
53
+ x2 = min(image.width, area["x2"] + margin)
54
+ y2 = min(image.height, area["y2"] + margin)
55
 
56
+ # Recortar la imagen al área especificada
57
+ crop = image.crop((x1, y1, x2, y2))
58
 
59
+ # Configurar parámetros de OCR para mejor precisión
60
+ custom_config = r'--oem 3 --psm 6'
61
+ text = pytesseract.image_to_string(crop, lang='eng', config=custom_config).strip()
62
+
63
+ if not text:
64
+ logger.debug("No se encontró texto en el área")
65
+ else:
66
+ logger.debug(f"Texto extraído: {text[:50]}...")
67
+ return text
68
+ except Exception as e:
69
+ logger.error(f"Error al extraer texto: {str(e)}")
70
+ return ""
71
 
72
  def process_invoice(image_path, coordinates_json, margin=10):
73
  """Procesa la factura y extrae los campos con margen de tolerancia"""
74
+ logger.info(f"Procesando factura: {image_path}")
75
+ try:
76
+ # Cargar imagen
77
+ image = Image.open(image_path)
78
+
79
+ # Cargar áreas de los campos
80
+ field_areas, img_width, img_height = load_field_areas(coordinates_json)
81
+
82
+ # Ajustar imagen si es necesario
83
+ if image.size != (img_width, img_height):
84
+ logger.debug(f"Redimensionando imagen de {image.size} a ({img_width}, {img_height})")
85
+ image = image.resize((img_width, img_height))
86
+
87
+ # Extraer texto de cada área
88
+ extracted_fields = {}
89
+ for label, area in field_areas.items():
90
+ logger.debug(f"Procesando campo: {label}")
91
+ text = extract_text_from_area(image, area, margin)
 
92
  if text:
93
+ extracted_fields[label] = text
94
+ else:
95
+ logger.debug(f"Reintentando {label} con margen mayor")
96
+ text = extract_text_from_area(image, area, margin * 2)
97
+ if text:
98
+ extracted_fields[label] = text
99
 
100
+ # Agregar el nombre del archivo como identificador
101
+ extracted_fields['filename'] = os.path.basename(image_path)
102
+
103
+ logger.info(f"Extracción completada: {len(extracted_fields)} campos encontrados")
104
+ return extracted_fields
105
+ except Exception as e:
106
+ logger.error(f"Error procesando factura {image_path}: {str(e)}")
107
+ return {'filename': os.path.basename(image_path)}
108
 
109
  if __name__ == "__main__":
110
+ logger.info("Iniciando procesamiento de facturas")
111
+
112
+ # Configurar directorios
113
+ invoice_dir = "./invoices"
114
+ data_dir = "./data"
115
  coordinates_json = "./coordinates_CI.json"
116
 
117
+ # Crear directorio data si no existe
118
+ Path(data_dir).mkdir(parents=True, exist_ok=True)
119
+ logger.debug(f"Directorio de datos creado: {data_dir}")
120
+
121
+ # Lista para almacenar los resultados de todas las facturas
122
+ all_results = []
123
+
124
+ # Procesar todas las imágenes en el directorio
125
+ total_files = len([f for f in os.listdir(invoice_dir)
126
+ if f.endswith(('.jpg', '.jpeg', '.png'))])
127
+ logger.info(f"Se encontraron {total_files} archivos para procesar")
128
+
129
+ for filename in os.listdir(invoice_dir):
130
+ if filename.endswith(('.jpg', '.jpeg', '.png')):
131
+ image_path = os.path.join(invoice_dir, filename)
132
+ results = process_invoice(image_path, coordinates_json, margin=5)
133
+ all_results.append(results)
134
+
135
+ # Crear DataFrame con todos los resultados
136
+ df = pd.DataFrame(all_results)
137
+
138
+ # Reordenar columnas (filename al inicio)
139
+ cols = ['filename'] + [col for col in df.columns if col != 'filename']
140
+ df = df[cols]
141
+
142
+ # Guardar resultados en CSV
143
+ csv_path = os.path.join(data_dir, 'ci_data.csv')
144
+ df.to_csv(csv_path, index=False)
145
 
146
+ logger.info(f"Proceso completado. Resultados guardados en: {csv_path}")
147
+ logger.info(f"Total de facturas procesadas: {len(all_results)}")
 
 
coordinates_CI.json CHANGED
@@ -66,9 +66,9 @@
66
  {
67
  "id": "8",
68
  "label": "Client_city_country",
69
- "x": "621.67",
70
- "y": "761.67",
71
- "width": "390.00",
72
  "height": "56.67",
73
  "confidence": null
74
  },
@@ -93,9 +93,9 @@
93
  {
94
  "id": "B",
95
  "label": "invoice_number",
96
- "x": "1868.33",
97
  "y": "255.00",
98
- "width": "223.33",
99
  "height": "50.00",
100
  "confidence": null
101
  },
@@ -175,7 +175,7 @@
175
  "id": "K",
176
  "label": "Tariff_number_01",
177
  "x": "1318.33",
178
- "y": "1053.33",
179
  "width": "283.33",
180
  "height": "60.00",
181
  "confidence": null
@@ -206,144 +206,8 @@
206
  "width": "206.67",
207
  "height": "50.00",
208
  "confidence": null
209
- },
210
- {
211
- "id": "O",
212
- "label": "Boxes_02",
213
- "x": "210.00",
214
- "y": "1108.33",
215
- "width": "186.67",
216
- "height": "43.33",
217
- "confidence": null
218
- },
219
- {
220
- "id": "P",
221
- "label": "Pieces_02",
222
- "x": "446.67",
223
- "y": "1108.33",
224
- "width": "260.00",
225
- "height": "50.00",
226
- "confidence": null
227
- },
228
- {
229
- "id": "Q",
230
- "label": "Product_02",
231
- "x": "886.67",
232
- "y": "1103.33",
233
- "width": "540.00",
234
- "height": "46.67",
235
- "confidence": null
236
- },
237
- {
238
- "id": "R",
239
- "label": "Tariff_number_02",
240
- "x": "1316.67",
241
- "y": "1110.00",
242
- "width": "273.33",
243
- "height": "46.67",
244
- "confidence": null
245
- },
246
- {
247
- "id": "S",
248
- "label": "Stems_02",
249
- "x": "1781.67",
250
- "y": "1106.67",
251
- "width": "143.33",
252
- "height": "46.67",
253
- "confidence": null
254
- },
255
- {
256
- "id": "T",
257
- "label": "Unit_price_02",
258
- "x": "1978.33",
259
- "y": "1105.00",
260
- "width": "196.67",
261
- "height": "50.00",
262
- "confidence": null
263
- },
264
- {
265
- "id": "U",
266
- "label": "Extended_price_02",
267
- "x": "2211.67",
268
- "y": "1106.67",
269
- "width": "216.67",
270
- "height": "40.00",
271
- "confidence": null
272
- },
273
- {
274
- "id": "V",
275
- "label": "Boxes_03",
276
- "x": "208.33",
277
- "y": "1161.67",
278
- "width": "183.33",
279
- "height": "50.00",
280
- "confidence": null
281
- },
282
- {
283
- "id": "W",
284
- "label": "Pieces_03",
285
- "x": "446.67",
286
- "y": "1165.00",
287
- "width": "260.00",
288
- "height": "56.67",
289
- "confidence": null
290
- },
291
- {
292
- "id": "X",
293
- "label": "Product_03",
294
- "x": "888.33",
295
- "y": "1161.67",
296
- "width": "543.33",
297
- "height": "50.00",
298
- "confidence": null
299
- },
300
- {
301
- "id": "Y",
302
- "label": "Tarif_number_03",
303
- "x": "1318.33",
304
- "y": "1166.67",
305
- "width": "270.00",
306
- "height": "53.33",
307
- "confidence": null
308
- },
309
- {
310
- "id": "Z",
311
- "label": "Stems_03",
312
- "x": "1781.67",
313
- "y": "1158.33",
314
- "width": "143.33",
315
- "height": "50.00",
316
- "confidence": null
317
- },
318
- {
319
- "id": "a",
320
- "label": "Unit_price_03",
321
- "x": "1985.00",
322
- "y": "1158.33",
323
- "width": "203.33",
324
- "height": "50.00",
325
- "confidence": null
326
- },
327
- {
328
- "id": "b",
329
- "label": "Extended_price_03",
330
- "x": "2216.67",
331
- "y": "1158.33",
332
- "width": "226.67",
333
- "height": "43.33",
334
- "confidence": null
335
- },
336
- {
337
- "id": "c",
338
- "label": "Forwarder",
339
- "x": "1786.67",
340
- "y": "1486.67",
341
- "width": "1086.67",
342
- "height": "426.67",
343
- "confidence": null
344
  }
345
  ],
346
  "height": 3509,
347
- "key": "pagina_1.jpg",
348
  "width": 2480
349
  }
 
66
  {
67
  "id": "8",
68
  "label": "Client_city_country",
69
+ "x": "951.33",
70
+ "y": "750.00",
71
+ "width": "1056.00",
72
  "height": "56.67",
73
  "confidence": null
74
  },
 
93
  {
94
  "id": "B",
95
  "label": "invoice_number",
96
+ "x": "1888.33",
97
  "y": "255.00",
98
+ "width": "238.00",
99
  "height": "50.00",
100
  "confidence": null
101
  },
 
175
  "id": "K",
176
  "label": "Tariff_number_01",
177
  "x": "1318.33",
178
+ "y": "1048.33",
179
  "width": "283.33",
180
  "height": "60.00",
181
  "confidence": null
 
206
  "width": "206.67",
207
  "height": "50.00",
208
  "confidence": null
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
209
  }
210
  ],
211
  "height": 3509,
 
212
  "width": 2480
213
  }
packages.txt CHANGED
@@ -1,2 +1,3 @@
1
  tesseract-ocr-all
2
- poppler-utils
 
 
1
  tesseract-ocr-all
2
+ poppler-utils
3
+ pandas
test.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ from PIL import Image
3
+ import pytesseract
4
+
5
+ def extract_text_from_area(image, area, margin=5):
6
+ """Extrae texto de un área específica de la imagen"""
7
+ try:
8
+ # Aplicar margen a las coordenadas
9
+ x1 = max(0, area["x1"] - margin)
10
+ y1 = max(0, area["y1"] - margin)
11
+ x2 = min(image.width, area["x2"] + margin)
12
+ y2 = min(image.height, area["y2"] + margin)
13
+
14
+ # Recortar la imagen al área especificada
15
+ crop = image.crop((x1, y1, x2, y2))
16
+
17
+ # Configurar OCR
18
+ custom_config = r'--oem 3 --psm 6'
19
+ return pytesseract.image_to_string(crop, lang='eng', config=custom_config).strip()
20
+ except Exception as e:
21
+ print(f"Error al extraer texto: {str(e)}")
22
+ return ""
23
+
24
+ def test_single_invoice():
25
+ """Prueba la extracción de campos en una sola factura"""
26
+ try:
27
+ # Rutas de archivos
28
+ image_path = "./invoices/pagina_1.jpg"
29
+ json_path = "./coordinates_CI.json"
30
+
31
+ # Cargar imagen
32
+ print(f"\nProcesando imagen: {image_path}")
33
+ image = Image.open(image_path)
34
+
35
+ # Cargar coordenadas
36
+ print("Cargando coordenadas...")
37
+ with open(json_path, 'r') as f:
38
+ data = json.load(f)
39
+
40
+ # Procesar cada campo
41
+ print("\nCampos encontrados:")
42
+ print("-" * 50)
43
+
44
+ for box in data['boxes']:
45
+ # Calcular coordenadas
46
+ x = float(box['x'])
47
+ y = float(box['y'])
48
+ width = float(box['width'])
49
+ height = float(box['height'])
50
+
51
+ area = {
52
+ "x1": int(x - width/2),
53
+ "y1": int(y - height/2),
54
+ "x2": int(x + width/2),
55
+ "y2": int(y + height/2)
56
+ }
57
+
58
+ # Extraer texto
59
+ text = extract_text_from_area(image, area)
60
+ if text:
61
+ print(f"{box['label']}: {text}")
62
+
63
+
64
+
65
+
66
+ except FileNotFoundError:
67
+ print("Error: No se encontró el archivo de imagen o coordenadas")
68
+ except Exception as e:
69
+ print(f"Error inesperado: {str(e)}")
70
+
71
+ if __name__ == "__main__":
72
+ test_single_invoice()