Views
No views yet
pip install "vllm==0.22.1" pillow1from PIL import Image
2from vllm import LLM, SamplingParams
3
4
5class OvisOCR2Parser:
6 def __init__(self, model_name_or_path: str):
7 self.model = LLM(
8 model=model_name_or_path,
9 tensor_parallel_size=1,
10 gpu_memory_utilization=0.8,
11 gdn_prefill_backend="triton"
12 )
13
14 prompt = '\nExtract all readable content from the image in natural human reading order and output the result as a single Markdown document. For charts or images, represent them using an HTML image tag: <' + 'img src="images/bbox_{left}_{top}_{right}_{bottom}.jpg" />, where left, top, right, bottom are bounding box coordinates scaled to [0, 1000). Format formulas as LaTeX. Format tables as HTML: <table>...</table>. Transcribe all other text as standard Markdown. Preserve the original text without translation or paraphrasing.'
15 self.prompt = self.model.get_tokenizer().apply_chat_template(
16 [{"role": "user", "content": [{"type": "image"}, {"type": "text", "text": prompt}]}],
17 tokenize=False,
18 add_generation_prompt=True,
19 enable_thinking=False
20 )
21
22 self.sampling_params = SamplingParams(
23 max_tokens=16384,
24 temperature=0.0
25 )
26
27 def _clean_truncated_repeats(
28 self,
29 text: str,
30 min_text_len: int = 8000,
31 max_period: int = 200,
32 min_period: int = 1,
33 min_repeat_chars: int = 100,
34 min_repeat_times: int = 5
35 ) -> str:
36 n = len(text)
37 if n < min_text_len:
38 return text
39
40 max_period = min(max_period, n - 1)
41 for unit_len in range(min_period, max_period + 1):
42 if text[n - 1] != text[n - 1 - unit_len]:
43 continue
44
45 match_len = 1
46 idx = n - 2
47 while idx >= unit_len and text[idx] == text[idx - unit_len]:
48 match_len += 1
49 idx -= 1
50
51 total_len = match_len + unit_len
52 repeat_times = total_len // unit_len
53 tail_len = total_len % unit_len
54
55 if repeat_times >= min_repeat_times and total_len >= min_repeat_chars:
56 return text[: n - total_len + unit_len] + text[n - tail_len:]
57
58 return text
59
60 def parse(self, images: list[Image.Image], filter_imgtags: bool = True) -> list[str]:
61 vllm_inputs = [
62 {
63 "prompt": self.prompt,
64 "multi_modal_data": {"image": image},
65 "mm_processor_kwargs": {
66 "images_kwargs": {
67 "min_pixels": 448 * 448,
68 "max_pixels": 2880 * 2880
69 }
70 }
71 }
72 for image in images
73 ]
74
75 outputs = self.model.generate(vllm_inputs, self.sampling_params)
76
77 markdowns = []
78 for output in outputs:
79 text = output.outputs[0].text.strip()
80 if filter_imgtags:
81 text = "\n\n".join(
82 block
83 for block in text.split("\n\n")
84 if not block.strip().startswith('<img src="images/bbox_')
85 )
86 markdowns.append(self._clean_truncated_repeats(text))
87
88 return markdowns
89
90
91if __name__ == "__main__":
92 parser = OvisOCR2Parser("your-model-path")
93 images = [Image.open("test1.jpg"), Image.open("test2.jpg")]
94 markdowns = parser.parse(images)
95 print(markdowns[0])parse removes HTML image tags for visual regions. To render Markdown with visual regions, set filter_imgtags=False and save the Markdown file together with the referenced image crops as follows:1import re
2from pathlib import Path
3
4from PIL import Image
5
6
7BBOX_IMAGE_PATTERN = re.compile(
8 r'<img src=' + r'"images/bbox_(\d+)_(\d+)_(\d+)_(\d+)\.jpg" />'
9)
10
11
12def save_renderable_markdown_with_visual_regions(
13 markdown: str,
14 page_image: Image.Image,
15 output_dir: str,
16) -> None:
17 output_dir = Path(output_dir)
18 images_dir = output_dir / "images"
19 images_dir.mkdir(parents=True, exist_ok=True)
20
21 width, height = page_image.size
22 for left, top, right, bottom in BBOX_IMAGE_PATTERN.findall(markdown):
23 x1 = max(0, min(width, round(int(left) * width / 1000)))
24 y1 = max(0, min(height, round(int(top) * height / 1000)))
25 x2 = max(0, min(width, round(int(right) * width / 1000)))
26 y2 = max(0, min(height, round(int(bottom) * height / 1000)))
27 if x2 <= x1 or y2 <= y1:
28 continue
29
30 crop_path = images_dir / f"bbox_{left}_{top}_{right}_{bottom}.jpg"
31 page_image.crop((x1, y1, x2, y2)).convert("RGB").save(crop_path)
32
33 (output_dir / "output.md").write_text(markdown, encoding="utf-8")
34
35
36parser = OvisOCR2Parser("your-model-path")
37page_image = Image.open("test1.jpg")
38markdown = parser.parse([page_image], filter_imgtags=False)[0]
39save_renderable_markdown_with_visual_regions(markdown, page_image, "output")1@article{lu2026ovisocr2,
2 title={OvisOCR2 Technical Report},
3 author={Shiyin Lu and Yinglun Li and Yu Xia and Yuhui Chen and An-Yang Ji and Jun-Peng Jiang and Qing-Guo Chen and Jianshan Zhao and En Lin and Haijun Li and Cheng Qin and Zhao Xu and Weihua Luo},
4 journal={arXiv preprint arXiv:2607.13639},
5 year={2026}
6}