File size: 6,061 Bytes
a5b0808
 
 
 
 
 
 
fd29010
 
 
 
 
 
c855e0a
a5b0808
 
 
 
 
 
 
 
 
c855e0a
a5b0808
c855e0a
a5b0808
 
a48260e
 
 
 
 
a5b0808
 
 
 
 
 
 
c855e0a
a5b0808
 
 
 
 
 
 
 
 
 
 
c855e0a
a5b0808
 
 
 
fd29010
c855e0a
 
 
 
 
 
 
 
 
 
 
 
 
a5b0808
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
fd29010
a5b0808
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c855e0a
 
 
a5b0808
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137

import gradio as gr
import yaml
import re
import os
import torch

# Paths from installed glmocr package (required on HF Space)
import glmocr
GLMOCR_BASE = os.path.dirname(glmocr.__file__)
config_path = os.path.join(GLMOCR_BASE, "config.yaml")
formatter_path = os.path.join(GLMOCR_BASE, "postprocess", "result_formatter.py")

# ── STEP 1: Fix config — keep header & footer (do NOT add them to abandon) ──
with open(config_path, "r") as f:
    config = yaml.safe_load(f)
config["pipeline"]["result_formatter"]["abandon"] = [
    "number", "footnote", "aside_text", "reference",
    "footer_image", "header_image",
]
config["pipeline"]["enable_layout"] = True
with open(config_path, "w") as f:
    yaml.dump(config, f, default_flow_style=False, sort_keys=False)
print("✅ config.yaml fixed (header & footer kept in output)")

# ── STEP 2: Fix result_formatter.py (remove hardcoded header/footer) ───────
with open(formatter_path, "r") as f:
    source = f.read()
labels_to_remove = [
    '"header"', "'header'", '"footer"', "'footer'",
    '"doc_header"', "'doc_header'", '"doc_footer"', "'doc_footer'"
]
for label in labels_to_remove:
    source = re.sub(r',\s*' + re.escape(label), '', source)
    source = re.sub(re.escape(label) + r'\s*,', '', source)
    source = re.sub(re.escape(label), '', source)
with open(formatter_path, "w") as f:
    f.write(source)
print("✅ result_formatter.py fixed")

# ── STEP 3: Load model ────────────────────────────────────────────────────
from transformers import AutoProcessor, GlmOcrForConditionalGeneration
print("Loading model... (~2GB first run)")
processor = AutoProcessor.from_pretrained("zai-org/GLM-OCR")
model = GlmOcrForConditionalGeneration.from_pretrained(
    "zai-org/GLM-OCR",
    torch_dtype=torch.bfloat16,
    device_map="auto",
)
print("✅ Model ready on", next(model.parameters()).device)
ABANDON = set(config["pipeline"]["result_formatter"]["abandon"])

# ── STEP 4: OCR (PDF → image then run model) ───────────────────────────────
def run_ocr(uploaded_file):
    if uploaded_file is None:
        return "Please upload a file.", "No regions detected."
    try:
        path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)
        if path.lower().endswith(".pdf"):
            try:
                import fitz
                doc = fitz.open(path)
                page = doc[0]
                pix = page.get_pixmap(matrix=fitz.Matrix(1, 1), alpha=False)
                img_path = path[:-4] + "_page0.png"
                pix.save(img_path)
                doc.close()
                path = img_path
            except Exception as e:
                return "PDF conversion failed: " + str(e), "Failed."

        messages = [
            {"role": "user", "content": [
                {"type": "image", "url": path},
                {"type": "text", "text": "Document Parsing:"}
            ]}
        ]
        inputs = processor.apply_chat_template(
            messages, tokenize=True, add_generation_prompt=True,
            return_dict=True, return_tensors="pt"
        ).to(model.device)
        inputs.pop("token_type_ids", None)
        with torch.no_grad():
            output_ids = model.generate(**inputs, max_new_tokens=2048)
        raw = processor.decode(
            output_ids[0][inputs["input_ids"].shape[1]:],
            skip_special_tokens=False
        )
        raw = raw.replace("<|user|>", "").strip()
        json_match = re.search(r'\[.*\]', raw, re.DOTALL)
        regions = json.loads(json_match.group()) if json_match else []
        header_count = footer_count = 0
        region_lines = []
        markdown_parts = []
        for region in regions:
            label = region.get("label", "text")
            content = str(region.get("content", ""))
            if label in ABANDON:
                continue
            if label == "header":
                header_count += 1
                region_lines.append("🔵 HEADER:\n" + content + "\n")
                markdown_parts.append("<!-- HEADER -->\n" + content)
            elif label == "footer":
                footer_count += 1
                region_lines.append("🟢 FOOTER:\n" + content + "\n")
                markdown_parts.append("<!-- FOOTER -->\n" + content)
            else:
                region_lines.append("[" + label + "]: " + content[:150])
                markdown_parts.append(content)
        summary = (
            "Headers found : " + str(header_count) + "\n"
            "Footers found : " + str(footer_count) + "\n"
            "Total regions : " + str(len(regions)) + "\n" + "─"*40 + "\n"
            + "\n".join(region_lines)
        )
        markdown = "\n\n".join(markdown_parts) if markdown_parts else raw
        return markdown, summary
    except Exception as e:
        import traceback
        return "Error: " + str(e) + "\n\n" + traceback.format_exc(), "Failed."

# ── STEP 5: Gradio UI ──────────────────────────────────────────────────────
with gr.Blocks(title="GLM-OCR — Header & Footer Kept") as demo:
    gr.Markdown("# 🔍 GLM-OCR — Header & Footer Kept\nUpload PDF or image. Headers 🔵 and Footers 🟢 are kept in output.")
    file_input = gr.File(label="Upload PDF or Image", file_types=[".pdf", ".png", ".jpg", ".jpeg", ".tiff", ".bmp"])
    run_btn = gr.Button("▶  Run OCR", variant="primary", size="lg")
    with gr.Row():
        with gr.Column():
            gr.Markdown("### 📄 Markdown Output")
            markdown_out = gr.Textbox(lines=25, label="")
        with gr.Column():
            gr.Markdown("### 🗂️ Detected Regions")
            regions_out = gr.Textbox(lines=25, label="")
    run_btn.click(fn=run_ocr, inputs=file_input, outputs=[markdown_out, regions_out])
demo.launch()