Unlimited OCR Works: Welcome the Era of One-shot Long-horizon Parsing.
이 번역은 AI가 원문 README를 옮긴 것입니다. 원문이 항상 우선합니다.
<p align="center"> <img src="assets/baidu.png" width="40%" alt="Baidu Inc." /> </p>
<hr>
<h1 align="center">Unlimited OCR Works</h1>
<div align="center"> <a href="https://github.com/baidu/Unlimited-OCR"> <img alt="GitHub" src="https://img.shields.io/badge/GitHub-Code-181717?logo=github&logoColor=white" /> </a> <a href="https://huggingface.co/baidu/Unlimited-OCR"> <img alt="Hugging Face" src="https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Model-ffc107?color=ffc107&logoColor=white" /> </a> </div>
<div align="center"> <a href="https://arxiv.org/abs/2606.23050"> <img alt="arXiv" src="https://img.shields.io/badge/arXiv-Unlimited OCR Works-b31b1b?logo=arxiv&logoColor=white" /> </a> <a href="https://x.com/BaiduInc" target="blank"> <img alt="Twitter Follow" src="https://img.shields.io/badge/Twitter-Baidu Inc.-white?logo=x&logoColor=white" /> </a> </div>
<h3 align="center">원샷 장기간 파싱 시대를 맞이합니다.</h3>
<p align="center"> <img src="assets/Unlimited-OCR.png" width="1000" alt="Unlimited OCR 개요" /> </p>
NVIDIA GPU에서 Huggingface transformers를 사용한 추론. 요구 사항은 python 3.12.3 + CUDA12.9에서 테스트되었습니다:
torch==2.10.0
torchvision==0.25.0
transformers==4.57.1
Pillow==12.1.1
matplotlib==3.10.8
einops==0.8.2
addict==2.4.0
easydict==1.13
pymupdf==1.27.2.2
psutil==7.2.2
import os
import torch
from transformers import AutoModel, AutoTokenizer
model_name = 'baidu/Unlimited-OCR'
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=torch.bfloat16,
)
model = model.eval().cuda()
# ── 단일 이미지는 gundam 또는 base 두 가지 설정을 지원합니다 ──
# gundam: base_size=1024, image_size=640, crop_mode=True
# base: base_size=1024, image_size=1024, crop_mode=False
model.infer(
tokenizer,
prompt='<image>document parsing.',
image_file='your_image.jpg',
output_path='your/output/dir',
base_size=1024, image_size=640, crop_mode=True,
max_length=32768,
no_repeat_ngram_size=35, ngram_window=128,
save_results=True,
)
# ── 여러 페이지 / PDF는 base만 사용합니다 (image_size=1024) ──
model.infer_multi(
tokenizer,
prompt='<image>Multi page parsing.',
image_files=['page1.png', 'page2.png', 'page3.png'],
output_path='your/output/dir',
image_size=1024,
max_length=32768,
no_repeat_ngram_size=35, ngram_window=1024,
save_results=True,
)
# ── PDF (페이지를 이미지로 변환한 후 여러 페이지 파싱) ──
import tempfile, fitz # PyMuPDF
def pdf_to_images(pdf_path, dpi=300):
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
model.infer_multi(
tokenizer,
prompt='<image>Multi page parsing.',
image_files=pdf_to_images('your_doc.pdf', dpi=300),
output_path='your/output/dir',
image_size=1024,
max_length=32768,
no_repeat_ngram_size=35, ngram_window=1024,
save_results=True,
)
배포 세부 사항은 공식 vLLM 레시피를 참조하십시오: 레시피: https://recipes.vllm.ai/baidu/Unlimited-OCR
GPU 플랫폼에 따라 다음 Docker 이미지를 사용하십시오:
기본 (CUDA 13.0):
docker pull vllm/vllm-openai:unlimited-ocr
Hopper GPU용 (CUDA 12.9)
docker pull vllm/vllm-openai:unlimited-ocr-cu129
환경 설정 (uv 관리 virtualenv). 먼저 로컬 SGLang wheel을 설치한 다음,
kernels==0.9.0을 고정하고 PDF를 이미지로 변환하기 위해 PyMuPDF를 설치하십시오:
uv venv --python 3.12
source.venv/bin/activate
uv pip install wheel/sglang-0.0.0.dev11416+g92e8bb79e-py3-none-any.whl
uv pip install kernels==0.11.7
uv pip install pymupdf==1.27.2.2
SGLang 서버 시작:
python -m sglang.launch_server \
--model baidu/Unlimited-OCR \
--served-model-name Unlimited-OCR \
--attention-backend fa3 \
--page-size 1 \
--mem-fraction-static 0.8 \
--context-length 32768 \
--enable-custom-logit-processor \
--disable-overlap-schedule \
--skip-server-warmup \
--host 0.0.0.0 \
--port 10000
OpenAI 호환 API로 스트리밍 요청 보내기:
import base64
import json
import os
import tempfile
import fitz
import requests
from sglang.srt.sampling.custom_logit_processor import DeepseekOCRNoRepeatNGramLogitProcessor
server_url = "http://127.0.0.1:10000"
session = requests.Session()
session.trust_env = False
def pdf_to_images(pdf_path, dpi=300):
doc = fitz.open(pdf_path)
tmp_dir = tempfile.mkdtemp(prefix="pdf_ocr_")
mat = fitz.Matrix(dpi / 72, dpi / 72)
image_paths = []
for i, page in enumerate(doc):
image_path = os.path.join(tmp_dir, f"page_{i + 1:04d}.png")
page.get_pixmap(matrix=mat).save(image_path)
image_paths.append(image_path)
doc.close()
return image_paths
def encode_image(image_path):
ext = os.path.splitext(image_path)[1].lower()
mime = "image/jpeg" if ext in (".jpg", ".jpeg") else f"image/{ext.lstrip('.')}"
with open(image_path, "rb") as f:
data = base64.b64encode(f.read()).decode("utf-8")
return {"type": "image_url", "image_url": {"url": f"data:{mime};base64,{data}"}}
def build_content(prompt, image_paths):
return [{"type": "text", "text": prompt}] + [encode_image(path) for path in image_paths]
def generate(prompt, image_paths, image_mode, ngram_window):
payload = {
"model": "Unlimited-OCR",
"messages": [{"role": "user", "content": build_content(prompt, image_paths)}],
"temperature": 0,
"skip_special_tokens": False,
"images_config": {"image_mode": image_mode},
"custom_logit_processor": DeepseekOCRNoRepeatNGramLogitProcessor.to_str(),
"custom_params": {
"ngram_size": 35,
"window_size": ngram_window,
},
"stream": True,
}
response = session.post(
f"{server_url}/v1/chat/completions",
headers={"Content-Type": "application/json"},
data=json.dumps(payload),
timeout=1200,
stream=True,
)
response.raise_for_status()
chunks = []
for line in response.iter_lines(chunk_size=1, decode_unicode=True):
if not line or not line.startswith("data: "):
continue
data = line[len("data: "):]
if data == "[DONE]":
break
event = json.loads(data)
delta = event["choices"][0].get("delta", {}).get("content", "")
if delta:
print(delta, end="", flush=True)
chunks.append(delta)
print()
return "".join(chunks)
# 단일 이미지는 gundam 또는 base 두 가지 설정을 지원합니다. 아래 예제는 gundam을 사용합니다.
generate("document parsing.", ["your_image.jpg"], image_mode="gundam", ngram_window=128)
# 여러 이미지 (base만)
generate("Multi page parsing.", ["page1.png", "page2.png"], image_mode="base", ngram_window=1024)
# PDF (base만)
generate("Multi page parsing.", pdf_to_images("your_doc.pdf", dpi=300), image_mode="base", ngram_window=1024)
배치 추론의 경우, infer.py가 SGLang 서버를 자동으로 시작하고 이미지 디렉토리 또는 PDF에 대해 동시 요청을 보냅니다:
# 이미지 디렉토리
python infer.py \
--image_dir./examples/images \
--output_dir./outputs \
--concurrency 8 \
--image_mode gundam
# PDF 페이지
python infer.py \
--pdf./examples/document.pdf \
--output_dir./outputs \
--concurrency 8 \
--image_mode gundam
유용한 옵션:
--model_dir baidu/Unlimited-OCR # 로컬 경로 또는 Hugging Face 모델 ID
--gpu 0 # CUDA_VISIBLE_DEVICES 값
--server_log./log/sglang_server.log
<img src="assets/long-horizon-ocr.gif" width="100%" alt="장기간 OCR 데모" />
Deepseek-OCR, Deepseek-OCR-2, PaddleOCR의 귀중한 모델과 아이디어에 감사드립니다.
@misc{yin2026unlimitedocrworks,
title={Unlimited OCR Works},
author={Youyang Yin and Huanhuan Liu and YY and Qunyi Xie and Chaorun Liu and Shiqi Yang and Shaohua Wang and Zhanlong Liu and Hao Zou and Jinyue Chen and Shu Wei and Jingjing Wu and Mingxin Huang and Zhen Wu and Guibin Wang and Tengyu Du and Lei Jia},
year={2026},
eprint={2606.23050},
archivePrefix={arXiv},
primaryClass={cs.CV},
url={https://arxiv.org/abs/2606.23050},
}