Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -775,6 +775,52 @@ def invoice_text_to_json(
|
|
| 775 |
final_json = _prune_empty_items(final_json)
|
| 776 |
return final_json
|
| 777 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 778 |
# ---------- Gradio UI ----------
|
| 779 |
TITLE = "docTR OCR — Text Extractor"
|
| 780 |
DESC = (
|
|
@@ -785,28 +831,43 @@ DESC = (
|
|
| 785 |
with gr.Blocks(theme="soft", title=TITLE) as demo:
|
| 786 |
gr.Markdown(f"# {TITLE}\n{DESC}")
|
| 787 |
|
| 788 |
-
with gr.
|
| 789 |
-
|
| 790 |
-
|
| 791 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 792 |
|
| 793 |
-
|
| 794 |
-
run_btn.
|
| 795 |
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
],
|
| 801 |
-
inputs=inp,
|
| 802 |
outputs=out,
|
| 803 |
-
cache_examples=False,
|
| 804 |
-
label="(Optional) Examples"
|
| 805 |
)
|
| 806 |
|
| 807 |
gr.Markdown(
|
| 808 |
-
"
|
| 809 |
-
"
|
| 810 |
)
|
| 811 |
|
| 812 |
if __name__ == "__main__":
|
|
|
|
| 775 |
final_json = _prune_empty_items(final_json)
|
| 776 |
return final_json
|
| 777 |
|
| 778 |
+
from typing import Optional
|
| 779 |
+
|
| 780 |
+
# ----- replace old run_ocr with unified dispatcher -----
|
| 781 |
+
def run_pipeline(file: Optional[gr.File], raw_txt: Optional[str]) -> str:
|
| 782 |
+
"""
|
| 783 |
+
Orchestrates two intake lanes:
|
| 784 |
+
1) If raw_txt is provided (non-empty), skip OCR → directly map to schema.
|
| 785 |
+
2) Else, run OCR on the uploaded file and map to schema.
|
| 786 |
+
"""
|
| 787 |
+
raw_txt = (raw_txt or "").strip()
|
| 788 |
+
|
| 789 |
+
# Lane A: Raw text → JSON
|
| 790 |
+
if raw_txt:
|
| 791 |
+
try:
|
| 792 |
+
result_json = invoice_text_to_json(raw_txt)
|
| 793 |
+
return json.dumps(result_json, indent=2, ensure_ascii=False)
|
| 794 |
+
except Exception as e:
|
| 795 |
+
return f"Error while converting pasted text to JSON schema: {e}"
|
| 796 |
+
|
| 797 |
+
# Lane B: File → OCR → JSON
|
| 798 |
+
if not file:
|
| 799 |
+
return "No input received. Upload an image/PDF or paste raw text."
|
| 800 |
+
|
| 801 |
+
try:
|
| 802 |
+
name = (file.name or "").lower()
|
| 803 |
+
|
| 804 |
+
# Load as DocumentFile (handles PNG/JPG/PDF)
|
| 805 |
+
if name.endswith(".pdf"):
|
| 806 |
+
doc = DocumentFile.from_pdf(file=file.name)
|
| 807 |
+
else:
|
| 808 |
+
doc = DocumentFile.from_images([file.name])
|
| 809 |
+
|
| 810 |
+
# Inference
|
| 811 |
+
result = MODEL(doc)
|
| 812 |
+
exported = result.export()
|
| 813 |
+
text = _collect_text_from_export(exported)
|
| 814 |
+
if not text:
|
| 815 |
+
return "No text detected by OCR."
|
| 816 |
+
|
| 817 |
+
result_json = invoice_text_to_json(text)
|
| 818 |
+
return json.dumps(result_json, indent=2, ensure_ascii=False)
|
| 819 |
+
|
| 820 |
+
except Exception as e:
|
| 821 |
+
return f"OCR pipeline error: {e}"
|
| 822 |
+
|
| 823 |
+
|
| 824 |
# ---------- Gradio UI ----------
|
| 825 |
TITLE = "docTR OCR — Text Extractor"
|
| 826 |
DESC = (
|
|
|
|
| 831 |
with gr.Blocks(theme="soft", title=TITLE) as demo:
|
| 832 |
gr.Markdown(f"# {TITLE}\n{DESC}")
|
| 833 |
|
| 834 |
+
with gr.TabbedInterface(
|
| 835 |
+
[
|
| 836 |
+
gr.TabItem("Upload File"),
|
| 837 |
+
gr.TabItem("Paste Raw Text")
|
| 838 |
+
],
|
| 839 |
+
tab_names=["Upload File", "Paste Raw Text"]
|
| 840 |
+
) as tabs:
|
| 841 |
+
with tabs.children[0]:
|
| 842 |
+
inp = gr.File(
|
| 843 |
+
label="Upload image/PDF",
|
| 844 |
+
file_types=[".png", ".jpg", ".jpeg", ".tif", ".tiff", .".pdf"]
|
| 845 |
+
)
|
| 846 |
+
# hidden paired textbox to keep a single click path
|
| 847 |
+
raw_txt_hidden = gr.Textbox(visible=False)
|
| 848 |
+
|
| 849 |
+
with tabs.children[1]:
|
| 850 |
+
# expose text lane; hide file lane counterpart
|
| 851 |
+
raw_txt = gr.Textbox(
|
| 852 |
+
label="Paste raw invoice text (we’ll map directly to JSON schema)",
|
| 853 |
+
lines=18,
|
| 854 |
+
placeholder="Paste the OCR’d/plain text of the invoice here…"
|
| 855 |
+
)
|
| 856 |
+
file_hidden = gr.File(visible=False)
|
| 857 |
|
| 858 |
+
out = gr.Code(label="Extracted JSON", language="json")
|
| 859 |
+
run_btn = gr.Button("Generate JSON", variant="primary")
|
| 860 |
|
| 861 |
+
# One button → unified function; we pass both lanes (visible/hidden)
|
| 862 |
+
run_btn.click(
|
| 863 |
+
fn=run_pipeline,
|
| 864 |
+
inputs=[inp, raw_txt],
|
|
|
|
|
|
|
| 865 |
outputs=out,
|
|
|
|
|
|
|
| 866 |
)
|
| 867 |
|
| 868 |
gr.Markdown(
|
| 869 |
+
"ℹ️ **Usage:** Prefer the *Paste Raw Text* tab when you already have text and only need schema mapping. "
|
| 870 |
+
"If both file and text are provided, we’ll **prioritize the pasted text** by design to collapse cycle time."
|
| 871 |
)
|
| 872 |
|
| 873 |
if __name__ == "__main__":
|