In this tutorial, we build a complete workflow for running Baidu’s Unlimited-OCR model on document images and multi-page PDFs. We configure the GPU environment, install the required dependencies, load the 3B-parameter vision-language model with automatic selection of bfloat16 or float16, and generate structured sample documents for testing. We then evaluate both the tiled Gundam inference mode and the faster Base mode for single-page OCR before extending the pipeline to multi-page PDF parsing with PyMuPDF and infer_multi(). Throughout the workflow, we preserve long-context generation settings, repetition controls, and structured output handling to process dense layouts, tables, paragraphs, and cross-page content in a reproducible end-to-end pipeline. Copy CodeCopiedUse a different Browser import subprocess, sys def pip_install(*pkgs): subprocess.check_call([sys.executable, “-m”, “pip”, “install”, “-q”, *pkgs]) print(“>> Installing dependencies (1-2 min)…”) pip_install( “transformers==4.57.1”, “Pillow”, “matplotlib”, “einops”, “addict”, “easydict”, “pymupdf”, “psutil”, “accelerate”, ) print(“>> Done.”) import os import torch from transformers import AutoModel, AutoTokenizer assert torch.cuda.is_available(), ( “No GPU detected! In Colab: Runtime -> Change runtime type -> GPU.” ) gpu_name = torch.cuda.get_device_name(0) print(f”>> GPU: {gpu_name}”) use_bf16 = torch.cuda.is_bf16_supported() DTYPE = torch.bfloat16 if use_bf16 else torch.float16 print(f”>> Using dtype: {DTYPE}”) MODEL_NAME = “baidu/Unlimited-OCR” print(“>> Downloading model (~6 GB for 3B params in BF16). First run takes a while…”) tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True) model = AutoModel.from_pretrained( MODEL_NAME, trust_remote_code=True, use_safetensors=True, torch_dtype=DTYPE, ) model = model.eval().cuda() print(“>> Model loaded and moved to GPU.”) We install the required libraries and prepare the Google Colab environment for Unlimited-OCR inference. We verify that a CUDA-enabled GPU is available and automatically choose bfloat16 or float16 based on hardware support. We then load the tokenizer and the 3B-parameter model from Hugging Face, switch them to evaluation mode, and move them to the GPU. Copy CodeCopiedUse a different Browser from PIL import Image, ImageDraw, ImageFont import textwrap os.makedirs(“inputs”, exist_ok=True) os.makedirs(“outputs/single_gundam”, exist_ok=True) os.makedirs(“outputs/single_base”, exist_ok=True) os.makedirs(“outputs/multi_page”, exist_ok=True) def load_font(size): for path in [ “/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf”, “/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf”, ]: if os.path.exists(path): return ImageFont.truetype(path, size) return ImageFont.load_default() def make_sample_page(path, page_no): W, H = 1240, 1754 img = Image.new(“RGB”, (W, H), “white”) d = ImageDraw.Draw(img) title_f, head_f, body_f = load_font(48), load_font(34), load_font(26) d.text((80, 70), f”Quarterly Operations Report — Page {page_no}”, fill=”black”, font=title_f) d.line([(80, 145), (W – 80, 145)], fill=”black”, width=3) body = ( “This document demonstrates Unlimited-OCR’s one-shot long-horizon ” “parsing. The model reads an entire page — headings, paragraphs, ” “and tables — and emits structured text in a single decoding pass. ” “Unlike classic OCR pipelines, no separate layout-analysis stage ” “is required.” ) y = 190 for line in textwrap.wrap(body, width=72): d.text((80, y), line, fill=”black”, font=body_f) y += 40 y += 30 d.text((80, y), f”Table {page_no}: Regional Revenue (USD, millions)”, fill=”black”, font=head_f) y += 60 rows = [ [“Region”, “Q1”, “Q2”, “Q3”], [“North”, “12.4”, “13.1”, “15.0”], [“South”, “9.8”, “10.2”, “11.7”], [“East”, “14.3”, “13.9”, “16.2”], [“West”, “11.1”, “12.5”, “12.9”], ] col_w, row_h, x0 = 260, 56, 80 for r, row in enumerate(rows): for c, cell in enumerate(row): x = x0 + c * col_w d.rectangle([x, y, x + col_w, y + row_h], outline=”black”, width=2) d.text((x + 14, y + 12), cell, fill=”black”, font=body_f) y += row_h y += 50 footer = ( f”Note {page_no}: Figures are illustrative. Multi-page mode stitches ” “context across pages, so cross-page references remain coherent.” ) for line in textwrap.wrap(footer, width=72): d.text((80, y), line, fill=”black”, font=body_f) y += 40 img.save(path) return path IMAGE_PATH = make_sample_page(“inputs/sample_page_1.png”, 1) PAGE_2 = make_sample_page(“inputs/sample_page_2.png”, 2) PAGE_3 = make_sample_page(“inputs/sample_page_3.png”, 3) print(f”>> Sample pages written: {IMAGE_PATH}, {PAGE_2}, {PAGE_3}”) import matplotlib.pyplot as plt plt.figure(figsize=(6, 8)) plt.imshow(Image.open(IMAGE_PATH)) plt.axis(“off”) plt.title(“Input document (page 1)”) plt.show() We create the required input and output directories and generate three realistic sample document pages with PIL. We add headings, paragraphs, tables, and footnotes to test the model on structured, layout-rich content. We also preview the first generated page with Matplotlib before sending it to the OCR pipeline. Copy CodeCopiedUse a different Browser print(“n” + “=” * 76) print(“STEP 4: Single image — GUNDAM mode (tiled, high detail)”) print(“=” * 76) model.infer( tokenizer, prompt=”<image>document parsing.”, image_file=IMAGE_PATH, output_path=”outputs/single_gundam”, base_size=1024, image_size=640, crop_mode=True, max_length=32768, no_repeat_ngram_size=35, ngram_window=128, save_results=True, ) We run single-image OCR using Gundam mode, which combines a global document view with tiled image crops. We enable crop_mode and use a smaller tile size to preserve fine text and improve recognition on dense document layouts. We also configure long-output generation and repetition controls to ensure the model produces stable, structured results. Copy CodeCopiedUse a different Browser print(“n” + “=” * 76) print(“STEP 5: Single image — BASE mode (single view, faster)”) print(“=” * 76) model.infer( tokenizer, prompt=”<image>document parsing.”, image_file=IMAGE_PATH, output_path=”outputs/single_base”, base_size=1024, image_size=1024, crop_mode=False, max_length=32768, no_repeat_ngram_size=35, ngram_window=128, save_results=True, ) We process the same document using Base mode with a single 1024-pixel image view. We turn off image cropping to reduce inference complexity and improve processing speed for clean, clearly printed pages. We retain the same output length and repetition-control settings to directly compare Base mode with Gundam mode. Copy CodeCopiedUse a different Browser print(“n” + “=” * 76) print(“STEP 6: Multi-page / PDF parsing”) print(“=” * 76) import tempfile import fitz def pdf_to_images(pdf_path, dpi=300): “””Rasterize every PDF page to a PNG; return the list of image paths.””” doc = fitz.open(pdf_path) tmp_dir = tempfile.mkdtemp(prefix=”pdf_ocr_”) mat = fitz.Matrix(dpi / 72, dpi / 72) paths = [] for i, page in enumerate(doc): out = os.path.join(tmp_dir, f”page_{i + 1:04d}.png”) page.get_pixmap(matrix=mat).save(out) paths.append(out) doc.close() return paths SAMPLE_PDF = “inputs/sample_doc.pdf” pdf = fitz.open() for p in [IMAGE_PATH, PAGE_2, PAGE_3]: img_doc = fitz.open(p) rect = img_doc[0].rect pdf_bytes = img_doc.convert_to_pdf() img_pdf = fitz.open(“pdf”, pdf_bytes) page = pdf.new_page(width=rect.width, height=rect.height) page.show_pdf_page(rect, img_pdf, 0) pdf.save(SAMPLE_PDF) pdf.close() print(f”>> Built sample PDF: {SAMPLE_PDF}”) page_images = pdf_to_images(SAMPLE_PDF, dpi=300) print(f”>> Rasterized {len(page_images)} pages”) model.infer_multi( tokenizer, prompt=”<image>Multi page parsing.”, image_files=page_images, output_path=”outputs/multi_page”, image_size=1024, max_length=32768, no_repeat_ngram_size=35, ngram_window=1024, save_results=True, ) We create a three-page PDF from the generated document images and rasterize each page of the PDF into a high-resolution PNG using PyMuPDF. We pass the resulting page-image sequence to infer_multi() so that the model can parse the complete document in a single long-horizon inference operation. We also widen the n-gram repetition window to maintain stable decoding across multiple pages. Copy CodeCopiedUse a different Browser print(“n”