File size: 8,632 Bytes
70c9fdf
 
 
 
 
 
6edb1f6
 
70c9fdf
 
6edb1f6
70c9fdf
 
6edb1f6
43fa232
 
6edb1f6
70c9fdf
6edb1f6
 
70c9fdf
 
6edb1f6
70c9fdf
 
 
6edb1f6
 
 
70c9fdf
 
6edb1f6
 
 
 
 
 
 
 
70c9fdf
 
 
6edb1f6
70c9fdf
 
62ba456
6edb1f6
62ba456
6edb1f6
b0dd76e
6edb1f6
43fa232
 
6edb1f6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43fa232
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70c9fdf
 
 
6edb1f6
70c9fdf
cf1832c
6edb1f6
70c9fdf
6edb1f6
70c9fdf
62ba456
6edb1f6
62ba456
 
 
6edb1f6
cf1832c
70c9fdf
62ba456
 
43fa232
 
 
 
62ba456
6edb1f6
43fa232
 
 
6edb1f6
62ba456
 
6edb1f6
 
 
43fa232
6edb1f6
43fa232
6edb1f6
 
 
 
 
 
 
 
 
 
 
 
 
 
43fa232
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6edb1f6
62ba456
 
 
6edb1f6
 
43fa232
 
 
6edb1f6
 
70c9fdf
6edb1f6
cf1832c
70c9fdf
f7e376f
70c9fdf
 
6edb1f6
 
43fa232
6edb1f6
 
 
 
 
 
 
 
 
 
 
 
70c9fdf
 
 
 
62ba456
70c9fdf
6edb1f6
62ba456
6edb1f6
 
 
 
43fa232
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
import gradio as gr
import yaml
import re
import os

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 ────────────────────────────────────────
with open(config_path, "r") as f:
    config = yaml.safe_load(f)

config["pipeline"]["maas"]["enabled"]  = True
config["pipeline"]["maas"]["api_key"]  = "4570c28bdea5493c9efae9dae68edc66.sGbA9DLlcX1GlvqV"

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")

# ── STEP 2: Fix result_formatter.py ──────────────────────────
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")

ABANDON = set(config["pipeline"]["result_formatter"]["abandon"])

# ── STEP 3: Extract header/footer from PDF text layer ─────────
# The MaaS API only returns 'text' and 'table' labels.
# Headers and footers are extracted directly from the PDF using PyMuPDF.
def extract_pdf_headers(path):
    import fitz
    page_headers = []
    doc = fitz.open(path)
    for page in doc:
        page_rect   = page.rect
        page_height = page_rect.height
        header_rect = fitz.Rect(0, 0, page_rect.width, page_height * 0.12)
        footer_rect = fitz.Rect(0, page_height * 0.88, page_rect.width, page_height)
        header_text = page.get_text(clip=header_rect).strip()
        footer_text = page.get_text(clip=footer_rect).strip()
        page_headers.append({"header": header_text, "footer": footer_text})
    doc.close()
    return page_headers

# ── STEP 4: Parse one page result into regions list ───────────
# result from parse() is a LIST of PipelineResult objects (one per page)
# Each PipelineResult has:
#   .json_result  β†’ list of region dicts with 'label' and 'content'
#   .markdown_result β†’ plain markdown string
def get_page_regions(page_result):
    # Try json_result first (structured regions)
    if hasattr(page_result, "json_result"):
        jr = page_result.json_result
        if isinstance(jr, list) and len(jr) > 0:
            # json_result is a list of lists (one list per page image)
            # since we pass one image per call, take index 0
            regions = jr[0] if isinstance(jr[0], list) else jr
            if isinstance(regions, list):
                return regions, "json"
    # Fallback to markdown_result
    if hasattr(page_result, "markdown_result"):
        md = page_result.markdown_result
        if md and isinstance(md, str) and md.strip():
            return md.strip(), "raw"
    return [], "empty"

# ── STEP 5: Main OCR function ─────────────────────────────────
def run_ocr(uploaded_file):
    if uploaded_file is None:
        return "Please upload a file.", "No regions detected."

    try:
        from glmocr import parse

        path = uploaded_file.name if hasattr(uploaded_file, "name") else str(uploaded_file)

        if path.lower().endswith(".pdf"):
            import fitz
            pdf_headers = extract_pdf_headers(path)
            doc = fitz.open(path)
            page_images = []
            for i in range(len(doc)):
                pix      = doc[i].get_pixmap(matrix=fitz.Matrix(1.5, 1.5), alpha=False)
                img_path = f"/tmp/maas_page_{i}.png"
                pix.save(img_path)
                page_images.append(img_path)
            doc.close()
            # parse() returns a LIST β€” one PipelineResult per page
            results = parse(page_images)
            if not isinstance(results, list):
                results = [results]
        else:
            pdf_headers = []
            results = parse(path)
            if not isinstance(results, list):
                results = [results]

        total_headers = 0
        total_footers = 0
        all_markdown  = []
        all_summary   = []

        for page_num, page_result in enumerate(results):
            page_md      = []
            page_summary = [f"── PAGE {page_num + 1} of {len(results)} ──"]

            # Inject header/footer from PDF text layer
            if pdf_headers and page_num < len(pdf_headers):
                hdr = pdf_headers[page_num]["header"]
                ftr = pdf_headers[page_num]["footer"]
                if hdr:
                    total_headers += 1
                    page_md.append("<!-- HEADER -->\n" + hdr)
                    page_summary.append("πŸ”΅ HEADER: " + hdr[:100])
                if ftr:
                    total_footers += 1
                    page_md.append("<!-- FOOTER -->\n" + ftr)
                    page_summary.append("🟒 FOOTER: " + ftr[:100])

            # Get regions from this page result
            data, dtype = get_page_regions(page_result)

            if dtype == "json":
                for region in data:
                    if not isinstance(region, dict):
                        continue
                    label   = region.get("label", "text")
                    content = str(region.get("content", ""))

                    if label in ABANDON:
                        continue

                    if label == "header":
                        total_headers += 1
                        page_summary.append("πŸ”΅ HEADER (API): " + content[:100])
                        page_md.append("<!-- HEADER -->\n" + content)
                    elif label == "footer":
                        total_footers += 1
                        page_summary.append("🟒 FOOTER (API): " + content[:100])
                        page_md.append("<!-- FOOTER -->\n" + content)
                    else:
                        page_summary.append("[" + label + "]: " + content[:100])
                        page_md.append(content)

            elif dtype == "raw":
                page_md.append(data)
                page_summary.append("[markdown output]")
                page_summary.append("Content length: " + str(len(data)) + " chars")

            else:
                page_summary.append("[empty page]")

            all_markdown.append("\n\n".join(page_md))
            all_summary.extend(page_summary)
            all_summary.append("")

        summary = (
            "Total pages   : " + str(len(results)) + "\n"
            + "Headers found : " + str(total_headers) + "\n"
            + "Footers found : " + str(total_footers) + "\n"
            + "─" * 40 + "\n"
            + "\n".join(all_summary)
        )

        markdown = "\n\n---\n\n".join(all_markdown) if all_markdown else "(No content)"
        return markdown, summary

    except Exception as e:
        import traceback
        return "Error: " + str(e) + "\n\n" + traceback.format_exc(), "Failed."

# ── STEP 6: Gradio UI ─────────────────────────────────────────
with gr.Blocks(title="GLM-OCR β€” MaaS (Header & Footer)") as demo:
    gr.Markdown("""
    # πŸ” GLM-OCR β€” Zhipu MaaS
    Upload PDF or image. Headers πŸ”΅ and Footers 🟒 now appear in output.
    Multi-page PDFs fully supported.
    """)

    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=30, label="")
        with gr.Column():
            gr.Markdown("### πŸ—‚οΈ Detected Regions")
            regions_out = gr.Textbox(lines=30, label="")

    run_btn.click(fn=run_ocr, inputs=file_input,
                  outputs=[markdown_out, regions_out])

demo.launch()