Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -13,7 +13,7 @@ import os
|
|
| 13 |
import glob
|
| 14 |
import subprocess
|
| 15 |
import tempfile
|
| 16 |
-
import
|
| 17 |
from pathlib import Path
|
| 18 |
|
| 19 |
import gradio as gr
|
|
@@ -44,21 +44,21 @@ def pdf_to_images(pdf_path: str, out_dir: str) -> list[str]:
|
|
| 44 |
# Step 2 — Images → raw text (TrOCR)
|
| 45 |
# ──────────────────────────────────────────────────────────────────────────────
|
| 46 |
|
| 47 |
-
|
| 48 |
-
_trocr_model = None
|
| 49 |
|
| 50 |
def load_trocr(model_name: str):
|
| 51 |
-
global
|
| 52 |
-
if
|
| 53 |
from transformers import TrOCRProcessor, VisionEncoderDecoderModel
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
|
|
|
| 58 |
|
| 59 |
|
| 60 |
def images_to_text(image_paths: list[str], model_name: str,
|
| 61 |
-
|
| 62 |
import torch
|
| 63 |
from PIL import Image
|
| 64 |
|
|
@@ -107,6 +107,8 @@ def refine_text(raw_pages: list[str], progress: gr.Progress) -> list[str]:
|
|
| 107 |
chunks = [page[j:j+400] for j in range(0, len(page), 400)]
|
| 108 |
corrected_chunks = []
|
| 109 |
for chunk in chunks:
|
|
|
|
|
|
|
| 110 |
out = pipe(
|
| 111 |
f"grammar: {chunk}",
|
| 112 |
max_length=512,
|
|
@@ -114,7 +116,7 @@ def refine_text(raw_pages: list[str], progress: gr.Progress) -> list[str]:
|
|
| 114 |
early_stopping=True,
|
| 115 |
)
|
| 116 |
corrected_chunks.append(out[0]["generated_text"])
|
| 117 |
-
refined.append(" ".join(corrected_chunks))
|
| 118 |
|
| 119 |
return refined
|
| 120 |
|
|
@@ -144,11 +146,10 @@ def text_to_docx(pages: list[str], output_path: str):
|
|
| 144 |
doc.add_page_break()
|
| 145 |
for line in page_text.split("\n"):
|
| 146 |
line = line.strip()
|
|
|
|
| 147 |
if line:
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
else:
|
| 151 |
-
doc.add_paragraph("")
|
| 152 |
|
| 153 |
doc.save(output_path)
|
| 154 |
|
|
@@ -166,31 +167,32 @@ def run_pipeline(
|
|
| 166 |
if pdf_file is None:
|
| 167 |
raise gr.Error("Please upload a PDF file first.")
|
| 168 |
|
|
|
|
|
|
|
|
|
|
| 169 |
with tempfile.TemporaryDirectory() as tmp:
|
| 170 |
# Step 1
|
| 171 |
progress(0, desc="Converting PDF to images…")
|
| 172 |
-
images = pdf_to_images(
|
| 173 |
-
page_count = len(images)
|
| 174 |
|
| 175 |
# Step 2
|
| 176 |
-
progress(0, desc="Loading TrOCR model…")
|
| 177 |
raw_pages = images_to_text(images, trocr_model, progress)
|
| 178 |
raw_text = "\n\n[PAGE BREAK]\n\n".join(raw_pages)
|
| 179 |
|
| 180 |
# Step 3
|
| 181 |
if use_grammar:
|
| 182 |
-
progress(0, desc="Loading grammar correction model…")
|
| 183 |
final_pages = refine_text(raw_pages, progress)
|
| 184 |
else:
|
| 185 |
final_pages = raw_pages
|
| 186 |
|
| 187 |
# Step 4
|
| 188 |
-
progress(0.
|
| 189 |
out_docx = os.path.join(tmp, "output.docx")
|
| 190 |
text_to_docx(final_pages, out_docx)
|
| 191 |
|
| 192 |
# Copy out of the temp dir so Gradio can serve it
|
| 193 |
-
import shutil
|
| 194 |
dest = tempfile.NamedTemporaryFile(
|
| 195 |
suffix=".docx", delete=False, prefix="scriptflow_"
|
| 196 |
)
|
|
@@ -231,7 +233,7 @@ with gr.Blocks(title="ScriptFlow — Handwriting to Word", theme=gr.themes.Soft(
|
|
| 231 |
label="TrOCR model",
|
| 232 |
)
|
| 233 |
use_grammar = gr.Checkbox(
|
| 234 |
-
label="Apply T5 grammar correction (adds
|
| 235 |
value=False,
|
| 236 |
)
|
| 237 |
run_btn = gr.Button("Convert", variant="primary")
|
|
@@ -245,9 +247,8 @@ with gr.Blocks(title="ScriptFlow — Handwriting to Word", theme=gr.themes.Soft(
|
|
| 245 |
"""
|
| 246 |
---
|
| 247 |
**Notes**
|
| 248 |
-
- First run downloads models from
|
| 249 |
- Grammar correction uses [`vennify/t5-base-grammar-correction`](https://huggingface.co/vennify/t5-base-grammar-correction).
|
| 250 |
-
- For best results use clear scans at ≥ 200 DPI.
|
| 251 |
- Running on CPU: expect ~30–60 s per page for OCR.
|
| 252 |
"""
|
| 253 |
)
|
|
@@ -259,6 +260,4 @@ with gr.Blocks(title="ScriptFlow — Handwriting to Word", theme=gr.themes.Soft(
|
|
| 259 |
)
|
| 260 |
|
| 261 |
if __name__ == "__main__":
|
| 262 |
-
demo.launch(
|
| 263 |
-
|
| 264 |
-
|
|
|
|
| 13 |
import glob
|
| 14 |
import subprocess
|
| 15 |
import tempfile
|
| 16 |
+
import shutil
|
| 17 |
from pathlib import Path
|
| 18 |
|
| 19 |
import gradio as gr
|
|
|
|
| 44 |
# Step 2 — Images → raw text (TrOCR)
|
| 45 |
# ──────────────────────────────────────────────────────────────────────────────
|
| 46 |
|
| 47 |
+
_trocr_cache = {}
|
|
|
|
| 48 |
|
| 49 |
def load_trocr(model_name: str):
|
| 50 |
+
global _trocr_cache
|
| 51 |
+
if model_name not in _trocr_cache:
|
| 52 |
from transformers import TrOCRProcessor, VisionEncoderDecoderModel
|
| 53 |
+
processor = TrOCRProcessor.from_pretrained(model_name)
|
| 54 |
+
model = VisionEncoderDecoderModel.from_pretrained(model_name)
|
| 55 |
+
model.eval()
|
| 56 |
+
_trocr_cache[model_name] = (processor, model)
|
| 57 |
+
return _trocr_cache[model_name]
|
| 58 |
|
| 59 |
|
| 60 |
def images_to_text(image_paths: list[str], model_name: str,
|
| 61 |
+
progress: gr.Progress) -> list[str]:
|
| 62 |
import torch
|
| 63 |
from PIL import Image
|
| 64 |
|
|
|
|
| 107 |
chunks = [page[j:j+400] for j in range(0, len(page), 400)]
|
| 108 |
corrected_chunks = []
|
| 109 |
for chunk in chunks:
|
| 110 |
+
if not chunk.strip():
|
| 111 |
+
continue
|
| 112 |
out = pipe(
|
| 113 |
f"grammar: {chunk}",
|
| 114 |
max_length=512,
|
|
|
|
| 116 |
early_stopping=True,
|
| 117 |
)
|
| 118 |
corrected_chunks.append(out[0]["generated_text"])
|
| 119 |
+
refined.append(" ".join(corrected_chunks) if corrected_chunks else page)
|
| 120 |
|
| 121 |
return refined
|
| 122 |
|
|
|
|
| 146 |
doc.add_page_break()
|
| 147 |
for line in page_text.split("\n"):
|
| 148 |
line = line.strip()
|
| 149 |
+
p = doc.add_paragraph()
|
| 150 |
if line:
|
| 151 |
+
run = p.add_run(line)
|
| 152 |
+
run.font.size = Pt(12)
|
|
|
|
|
|
|
| 153 |
|
| 154 |
doc.save(output_path)
|
| 155 |
|
|
|
|
| 167 |
if pdf_file is None:
|
| 168 |
raise gr.Error("Please upload a PDF file first.")
|
| 169 |
|
| 170 |
+
# Handle Gradio filepath string vs object compatibility
|
| 171 |
+
pdf_path = pdf_file if isinstance(pdf_file, str) else pdf_file.name
|
| 172 |
+
|
| 173 |
with tempfile.TemporaryDirectory() as tmp:
|
| 174 |
# Step 1
|
| 175 |
progress(0, desc="Converting PDF to images…")
|
| 176 |
+
images = pdf_to_images(pdf_path, tmp)
|
|
|
|
| 177 |
|
| 178 |
# Step 2
|
| 179 |
+
progress(0.1, desc="Loading TrOCR model…")
|
| 180 |
raw_pages = images_to_text(images, trocr_model, progress)
|
| 181 |
raw_text = "\n\n[PAGE BREAK]\n\n".join(raw_pages)
|
| 182 |
|
| 183 |
# Step 3
|
| 184 |
if use_grammar:
|
| 185 |
+
progress(0.6, desc="Loading grammar correction model…")
|
| 186 |
final_pages = refine_text(raw_pages, progress)
|
| 187 |
else:
|
| 188 |
final_pages = raw_pages
|
| 189 |
|
| 190 |
# Step 4
|
| 191 |
+
progress(0.9, desc="Generating Word document…")
|
| 192 |
out_docx = os.path.join(tmp, "output.docx")
|
| 193 |
text_to_docx(final_pages, out_docx)
|
| 194 |
|
| 195 |
# Copy out of the temp dir so Gradio can serve it
|
|
|
|
| 196 |
dest = tempfile.NamedTemporaryFile(
|
| 197 |
suffix=".docx", delete=False, prefix="scriptflow_"
|
| 198 |
)
|
|
|
|
| 233 |
label="TrOCR model",
|
| 234 |
)
|
| 235 |
use_grammar = gr.Checkbox(
|
| 236 |
+
label="Apply T5 grammar correction (adds processing time)",
|
| 237 |
value=False,
|
| 238 |
)
|
| 239 |
run_btn = gr.Button("Convert", variant="primary")
|
|
|
|
| 247 |
"""
|
| 248 |
---
|
| 249 |
**Notes**
|
| 250 |
+
- First run downloads models from Hugging Face — subsequent runs use the cache.
|
| 251 |
- Grammar correction uses [`vennify/t5-base-grammar-correction`](https://huggingface.co/vennify/t5-base-grammar-correction).
|
|
|
|
| 252 |
- Running on CPU: expect ~30–60 s per page for OCR.
|
| 253 |
"""
|
| 254 |
)
|
|
|
|
| 260 |
)
|
| 261 |
|
| 262 |
if __name__ == "__main__":
|
| 263 |
+
demo.launch()
|
|
|
|
|
|