AndresIgnacio commited on
Commit
a358cff
·
verified ·
1 Parent(s): 4bcea11

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +125 -129
app.py CHANGED
@@ -1,129 +1,125 @@
1
- import os
2
- import gradio as gr
3
- import pdfplumber
4
- from PIL import Image, ImageEnhance
5
- import pytesseract
6
- from docx import Document
7
- from docx.shared import Pt, RGBColor
8
- from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
9
- import pandas as pd
10
-
11
- # Silenciar advertencias de TensorFlow
12
- os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'
13
-
14
- # Configurar Tesseract (para Visual Studio: ajustar según tu ruta local)
15
- if os.getenv("PYTHON_ENV") == "local":
16
- pytesseract.pytesseract.tesseract_cmd = r'C:\Users\andre\Documents\Aplicaciones y Softwear\Tesseeract\tesseract.exe'
17
-
18
- def apply_text_formatting(run, font_name=None, font_size=None, is_bold=None, is_italic=None, alignment=None):
19
- """Aplica formato al texto en el documento Word."""
20
- if font_name:
21
- run.font.name = font_name
22
- if font_size:
23
- run.font.size = Pt(font_size)
24
- if is_bold is not None:
25
- run.bold = is_bold
26
- if is_italic is not None:
27
- run.italic = is_italic
28
-
29
- def process_file(file):
30
- try:
31
- print(f"Procesando archivo: {file.name}")
32
- extracted_text = ""
33
- tables = []
34
-
35
- # Crear documento Word sin formato predefinido
36
- doc = Document()
37
-
38
- # Verifica el tipo de archivo
39
- if file.name.endswith(".pdf"):
40
- print("Archivo identificado como PDF.")
41
- try:
42
- with pdfplumber.open(file.name) as pdf:
43
- for page_num, page in enumerate(pdf.pages):
44
- print(f"Procesando página {page_num + 1}")
45
-
46
- # Extraer texto y sus características
47
- for char in page.chars: # Extrae caracteres y sus estilos
48
- text = char.get("text", "")
49
- font_name = char.get("fontname", None)
50
- font_size = char.get("size", None)
51
- is_bold = "Bold" in font_name if font_name else False
52
- alignment = page.bbox # Aquí podrías refinar el análisis si es necesario
53
-
54
- if text.strip():
55
- para = doc.add_paragraph()
56
- run = para.add_run(text)
57
- apply_text_formatting(run, font_name=font_name, font_size=font_size, is_bold=is_bold)
58
-
59
- # Extraer tablas
60
- tables_on_page = page.extract_tables()
61
- for table in tables_on_page:
62
- if table:
63
- df = pd.DataFrame(table)
64
- table_in_doc = doc.add_table(rows=0, cols=len(df.columns))
65
- for i, row in df.iterrows():
66
- cells = table_in_doc.add_row().cells
67
- for j, cell in enumerate(row):
68
- cells[j].text = str(cell)
69
- tables.append(df)
70
- except Exception as pdf_error:
71
- return f"Error procesando el PDF: {str(pdf_error)}"
72
-
73
- elif file.name.endswith((".png", ".jpg", ".jpeg")):
74
- print("Archivo identificado como imagen.")
75
- try:
76
- # Preprocesar imagen para mejorar OCR
77
- img = Image.open(file)
78
- img = img.convert("L") # Convertir a escala de grises
79
- img = ImageEnhance.Contrast(img).enhance(2) # Aumentar contraste
80
- img = img.resize((img.width * 2, img.height * 2)) # Aumentar tamaño
81
-
82
- # OCR en español
83
- extracted_text = pytesseract.image_to_string(img, lang="spa")
84
- doc.add_paragraph(extracted_text)
85
- except Exception as img_error:
86
- return f"Error procesando la imagen: {str(img_error)}"
87
-
88
- else:
89
- print("Archivo no soportado.")
90
- return "Tipo de archivo no soportado. Sube un PDF o una imagen."
91
-
92
- # Validar texto extraído
93
- if len(extracted_text.strip()) == 0:
94
- return "No se encontró texto en el archivo proporcionado."
95
-
96
- # Guardar archivo Word
97
- output_word_path = "output_document.docx"
98
- doc.save(output_word_path)
99
-
100
- # Crear salida con tablas
101
- output = f"Texto extraído del PDF:\n\n{extracted_text}\n\nArchivo Word generado: {output_word_path}\n"
102
- if tables:
103
- output += "Tablas extraídas:\n\n"
104
- for idx, table in enumerate(tables):
105
- output += f"Tabla {idx + 1}:\n{table.to_string(index=False)}\n\n"
106
-
107
- return output, output_word_path
108
-
109
- except Exception as e:
110
- print(f"Error general: {str(e)}")
111
- return f"Error general procesando el archivo: {str(e)}", None
112
-
113
- # Interfaz Gradio
114
- def interface_fn(file):
115
- output, word_path = process_file(file)
116
- if word_path:
117
- return output, word_path
118
- return output
119
-
120
- demo = gr.Interface(
121
- fn=interface_fn,
122
- inputs=gr.File(label="Sube tu archivo (PDF o imagen)"),
123
- outputs=["text", gr.File(label="Descargar Word generado")],
124
- title="Análisis Avanzado de Licitaciones",
125
- description="Sube un archivo PDF o una imagen para extraer texto y generar un documento Word con formato original.",
126
- )
127
-
128
- # Ejecuta la aplicación
129
- demo.launch(share=True)
 
1
+ import os
2
+ from pathlib import Path
3
+ from typing import Tuple, Union
4
+ import logging
5
+ from transformers import DonutProcessor, VisionEncoderDecoderModel
6
+ from PIL import Image
7
+ from pdf2image import convert_from_path
8
+ from docx import Document
9
+ from docx.shared import Pt
10
+ from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
11
+ import gradio as gr
12
+
13
+ # Configuración avanzada de logging
14
+ logging.basicConfig(
15
+ level=logging.DEBUG,
16
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
17
+ handlers=[
18
+ logging.FileHandler("app.log", mode="a", encoding="utf-8"),
19
+ logging.StreamHandler()
20
+ ]
21
+ )
22
+
23
+ class HuggingFaceProcessor:
24
+ """Clase para manejar modelos avanzados de Hugging Face para procesamiento de documentos."""
25
+ def __init__(self, model_name: str = "naver-clova-ix/donut-base-finetuned-docvqa"):
26
+ self.logger = logging.getLogger("HuggingFaceProcessor")
27
+ self.logger.info("Cargando modelo de Hugging Face...")
28
+ try:
29
+ self.processor = DonutProcessor.from_pretrained(model_name)
30
+ self.model = VisionEncoderDecoderModel.from_pretrained(model_name)
31
+ except Exception as e:
32
+ self.logger.error(f"Error cargando el modelo: {e}")
33
+ raise
34
+
35
+ def process_image(self, image: Image.Image) -> str:
36
+ """Procesa una imagen y extrae texto usando el modelo Donut."""
37
+ try:
38
+ pixel_values = self.processor(image, return_tensors="pt").pixel_values
39
+ outputs = self.model.generate(pixel_values, max_length=512)
40
+ result = self.processor.batch_decode(outputs, skip_special_tokens=True)[0]
41
+ return result.strip()
42
+ except Exception as e:
43
+ self.logger.error(f"Error procesando la imagen con Donut: {e}")
44
+ return ""
45
+
46
+ class PDFToWordProcessor:
47
+ """Procesa un PDF escaneado y genera un documento Word."""
48
+ def __init__(self):
49
+ self.logger = logging.getLogger("PDFToWordProcessor")
50
+ self.hf_processor = HuggingFaceProcessor()
51
+
52
+ def process_pdf(self, file_path: Path) -> Document:
53
+ """Convierte un PDF a un documento Word."""
54
+ self.logger.info(f"Procesando PDF: {file_path}")
55
+ doc = Document()
56
+
57
+ try:
58
+ # Convertir cada página del PDF a imagen
59
+ images = convert_from_path(file_path)
60
+
61
+ for page_num, image in enumerate(images, start=1):
62
+ self.logger.debug(f"Procesando página {page_num}")
63
+
64
+ # Extraer texto usando el modelo Donut
65
+ page_text = self.hf_processor.process_image(image)
66
+
67
+ # Agregar encabezado para cada página
68
+ doc.add_heading(f"Página {page_num}", level=2)
69
+
70
+ # Agregar texto extraído al documento Word
71
+ self._add_text_to_doc(doc, page_text)
72
+
73
+ except Exception as e:
74
+ self.logger.error(f"Error procesando PDF: {e}")
75
+ raise
76
+
77
+ return doc
78
+
79
+ def _add_text_to_doc(self, doc: Document, text: str):
80
+ """Agrega texto extraído al documento Word."""
81
+ for line in text.split('\n'):
82
+ if line.strip(): # Evitar líneas vacías
83
+ paragraph = doc.add_paragraph(line.strip(), style="Normal")
84
+ paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT
85
+
86
+ def process_file(self, file_path: Union[str, Path]) -> Tuple[str, str]:
87
+ """Procesa un archivo PDF y guarda el documento Word generado."""
88
+ file_path = Path(file_path)
89
+ output_path = file_path.with_suffix(".docx")
90
+
91
+ try:
92
+ if file_path.suffix.lower() != ".pdf":
93
+ raise ValueError(f"Formato no soportado: {file_path.suffix}")
94
+
95
+ doc = self.process_pdf(file_path)
96
+ doc.save(output_path)
97
+ return "Documento procesado exitosamente", str(output_path)
98
+ except Exception as e:
99
+ return f"Error: {e}", ""
100
+
101
+ def create_interface():
102
+ """Crea la interfaz de usuario con Gradio."""
103
+ processor = PDFToWordProcessor()
104
+
105
+ def process_file(file):
106
+ if not file:
107
+ return "Por favor, seleccione un archivo", None
108
+ return processor.process_file(file.name)
109
+
110
+ with gr.Blocks(title="Procesador de PDF a Word") as demo:
111
+ gr.Markdown("# Procesador PDF a Word con Hugging Face")
112
+ gr.Markdown("Convierte documentos PDF escaneados a Word utilizando modelos avanzados de Hugging Face.")
113
+
114
+ file_input = gr.File(label="Seleccionar PDF", file_types=[".pdf"], type="filepath")
115
+ process_button = gr.Button("Procesar", variant="primary")
116
+ output_text = gr.Textbox(label="Estado del Proceso")
117
+ output_file = gr.File(label="Documento Procesado")
118
+
119
+ process_button.click(process_file, inputs=[file_input], outputs=[output_text, output_file])
120
+
121
+ return demo
122
+
123
+ if __name__ == "__main__":
124
+ demo = create_interface()
125
+ demo.launch(share=True)