Skip to content

Commit 62eb4d0

Browse files
authored
Revise README for KarantaOCR project details
Updated project name and enhanced README content for clarity and organization.
1 parent e22987b commit 62eb4d0

1 file changed

Lines changed: 247 additions & 9 deletions

File tree

README.md

Lines changed: 247 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,255 @@
55
<h1>Karanta OCR</h1>
66
</div>
77

8-
# Karanta OCR
8+
# KarantaOCR: Efficient Document Processing for African Languages
99
Karanta means "read" in Hausa, a language spoken in Nigeria and other West African countries. This project is a OCR toolkit for processing scanned documents containing content in african languages and extracting the text in them at scale.
1010

1111
We would like to give huge credits to the [OlmoOCR](https://github.com/allenai/olmocr) project and team for providing the blueprint that we've adapted for Karanta.
1212

13-
## OCR Pipeline
13+
## Table of Contents
1414

15-
### Document Type Classification
16-
...
15+
- [Model Description](#model-description)
16+
- [Training Data](#training-data)
17+
- [Stage 1: General OCR Training](#stage-1-general-ocr-training)
18+
- [Stage 2: African Language Fine-Tuning](#stage-2-african-language-fine-tuning)
19+
- [Training Plots](#training-plots)
20+
- [Capabilities](#capabilities)
21+
- [Evaluation](#evaluation)
22+
- [Results -- KarantaOCR-Bench](#results----karantaocr-bench)
23+
- [Results -- OlmoOCR-Bench](#results----olmocr-bench)
24+
- [How to Use](#how-to-use)
25+
- [Load the Model and Processor](#load-the-model-and-processor)
26+
- [Prepare a PDF Page for Inference](#prepare-a-pdf-page-for-inference)
27+
- [Run OCR Inference](#run-ocr-inference)
28+
- [End-to-End Example](#end-to-end-example)
29+
- [Citation Information](#citation-information)
1730

18-
### Document Article Segmentation
19-
...
31+
---
2032

21-
### OCR
22-
...
33+
## Model Description
34+
35+
#### [Paper](....)
36+
37+
**KarantaOCR** is an open-source document OCR and processing model designed for **high-accuracy text extraction in African languages**.
38+
The model focuses on preserving language-specific characters and diacritics that are often lost, normalized, or mis-transcribed by existing OCR systems.
39+
40+
KarantaOCR is fine-tuned from [Qwen/Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct), a vision-language model that combines a strong vision encoder with a large language model.
41+
Through targeted curriculum fine-tuning, KarantaOCR extends these capabilities to robust document understanding across diverse PDF formats and multilingual settings.
42+
43+
## Training Data
44+
45+
KarantaOCR was trained using a **two-stage curriculum fine-tuning strategy**.
46+
47+
### Stage 1: General OCR Training
48+
49+
* **100,000 documents** sampled from [Allenai OCRMix](allenai/olmOCR-mix-0225)
50+
* Purpose: learn general OCR skills across layouts, fonts, tables, and document structures
51+
52+
### Stage 2: African Language Fine-Tuning
53+
54+
* **50,000 PDFs** containing text in **10 African languages**, crawled from the web
55+
* Domains include:
56+
57+
* Religious texts
58+
* Legal documents
59+
* Dictionaries
60+
* Novels
61+
* Other long-form and structured documents
62+
63+
This stage emphasizes accurate transcription of **diacritics, special characters, and region-specific typography**.
64+
65+
### Training Plots
66+
67+
<div align="left">
68+
<img src="https://cdn-uploads.huggingface.co/production/uploads/604b97e27032db3f5e8d6e8e/5Jf26jOGs12rrMwy3hwrI.png" alt="Train Loss" width="600"/>
69+
</div>
70+
71+
<div align="left">
72+
<img src="https://cdn-uploads.huggingface.co/production/uploads/604b97e27032db3f5e8d6e8e/9z4t6so8DIaykrFQHs0Su.png" alt="Eval Loss" width="600"/>
73+
</div>
74+
75+
<div align="left">
76+
<img src="https://cdn-uploads.huggingface.co/production/uploads/604b97e27032db3f5e8d6e8e/-TJltGBXNFABTkShCyvsL.png" alt="Learning Rate" width="600"/>
77+
</div>
78+
79+
80+
---
81+
82+
## Capabilities
83+
84+
KarantaOCR supports:
85+
86+
* High-accuracy **text extraction** from PDFs
87+
* **Table extraction** and structured document understanding
88+
* Robust handling of:
89+
90+
* Multi-column layouts
91+
* Headers and footers
92+
* Mixed scanned and digital PDFs
93+
94+
While improved performance on African languages was our priority, KarantaOCR **maintains strong performance on English and other high-resource languages**, making it suitable for mixed-language document collections.
95+
96+
## Evaluation
97+
98+
KarantaOCR is evaluated on the OLMOocr benchmark using pass-rate accuracy. Scores are reported as averages across JSONL files with 95% confidence intervals.
99+
In addition to OLMOocr benchmark, we also create a KarantaOCR-Bench, which focuses specifically on testing OCR extraction on special characters and diacritics.
100+
101+
### Results -- KarantaOCR-Bench
23102

103+
| JSONL File | KarantaOCR (3B) | RoLMOCR (7B) | NanoNetsOCR-2 (3B) | OLMOCR-2 (7B) |
104+
| --------------- | ---------- | -------- | ------------- | -------- |
105+
| Diacritics & Special Characters | **61.6** | 53.6 | 55.1 | 55.5
106+
107+
### Results -- OlmoOCR-Bench
108+
109+
| JSONL File | KarantaOCR (3B) | RoLMOCR (7B) | NanoNetsOCR-2 (3B) | OLMOCR-1 (7B) | OLMOCR-2 (7B) | Mistral OCR API | DeepSeek-OCR (3B) |
110+
| --------------- | ---------- | -------- | ------------- | -------- | -------- |-------- |-------- |
111+
| arxiv_math | 74.2 | 76.8 | 73.7 | 63.3 | 83.0 | 77.2 | 77.2 |
112+
| baseline | 99.4 | 97.9 | 99.5 | 97.9 | 99.7 | 99.4 | 99.8 |
113+
| headers_footers | 95.3 | 94.1 | 32.8 | 93.4 | 96.1 | 93.6 | 96.1 |
114+
| long_tiny_text | 72.2 | 61.3 | 92.1 | 54.8 | 81.9 | 77.1 | 79.4 |
115+
| multi_column | 75.6 | 70.0 | 82.5 | 67.6 | 83.7 | 71.3 | 66.4 |
116+
| old_scans | 41.3 | 42.4 | 41.4 | 38.6 | 47.7 | 29.3 | 33.3 |
117+
| old_scans_math | 70.3 | 80.1 | 44.1 | 67.5 | 82.3 | 67.5 | 73.6 |
118+
| table_tests | 64.3 | 72.2 | 84.2 | 62.3 | 84.9 | 60.6 | 80.2 |
119+
| Average | 74.1% | 74.4% | 68.8% | 68.2% | 82.4% | 72.0 | 75.7 |
120+
121+
122+
## How to Use - Inference
123+
124+
KarantaOCR processes PDF documents by rendering pages into images and combining them with structured prompts for inference.
125+
126+
### Load the Model and Processor
127+
128+
```python
129+
import torch
130+
from transformers import Qwen2_5_VLForConditionalGeneration, AutoProcessor
131+
132+
def load_model(model_path: str, device_map: str = "auto", dtype: str = "auto"):
133+
model = Qwen2_5_VLForConditionalGeneration.from_pretrained(
134+
model_path,
135+
torch_dtype=getattr(torch, dtype) if dtype != "auto" else "auto",
136+
device_map=device_map,
137+
)
138+
return model
139+
140+
def load_processor(processor_name: str, min_pixels=None, max_pixels=None):
141+
if min_pixels and max_pixels:
142+
return AutoProcessor.from_pretrained(
143+
processor_name, min_pixels=min_pixels, max_pixels=max_pixels
144+
)
145+
return AutoProcessor.from_pretrained(processor_name)
146+
```
147+
148+
### Prepare a PDF Page for Inference
149+
150+
```python
151+
from jinja2 import Template
152+
153+
def render_pdf_to_base64png(
154+
local_pdf_path: str, page_num: int, target_longest_image_dim: int = 2048
155+
) -> str:
156+
longest_dim = max(get_pdf_media_box_width_height(local_pdf_path, page_num))
157+
158+
# Convert PDF page to PNG using pdftoppm
159+
pdftoppm_result = subprocess.run(
160+
[
161+
"pdftoppm",
162+
"-png",
163+
"-f",
164+
str(page_num),
165+
"-l",
166+
str(page_num),
167+
"-r",
168+
str(
169+
target_longest_image_dim * 72 / longest_dim
170+
), # 72 pixels per point is the conversion factor
171+
local_pdf_path,
172+
],
173+
timeout=120,
174+
stdout=subprocess.PIPE,
175+
stderr=subprocess.PIPE,
176+
)
177+
assert pdftoppm_result.returncode == 0, pdftoppm_result.stderr
178+
return base64.b64encode(pdftoppm_result.stdout).decode("utf-8")
179+
180+
def build_message(image_url: str, system_prompt: str, page: int = 0):
181+
image_base64 = render_pdf_to_base64png(image_url, page, TARGET_IMAGE_DIM)
182+
183+
prompt = [
184+
{
185+
"role": "user",
186+
"content": [
187+
{
188+
"type": "text",
189+
"text": system_prompt
190+
},
191+
{
192+
"type": "image",
193+
"image": f"data:image/png;base64,{image_base64}",
194+
},
195+
],
196+
}
197+
]
198+
return prompt
199+
```
200+
201+
### Run OCR Inference
202+
203+
```python
204+
from qwen_vl_utils import process_vision_info
205+
206+
def run_inference(model, processor, messages, max_new_tokens=128, device="cuda"):
207+
text = processor.apply_chat_template(
208+
messages, tokenize=False, add_generation_prompt=True
209+
)
210+
211+
image_inputs, _ = process_vision_info(messages)
212+
inputs = processor(
213+
text=[text],
214+
images=image_inputs,
215+
padding=False,
216+
return_tensors="pt",
217+
).to(device)
218+
219+
generated_ids = model.generate(**inputs, max_new_tokens=max_new_tokens)
220+
trimmed_ids = [
221+
out_ids[len(in_ids):] for in_ids, out_ids in zip(inputs.input_ids, generated_ids)
222+
]
223+
224+
outputs = processor.batch_decode(
225+
trimmed_ids,
226+
skip_special_tokens=True,
227+
clean_up_tokenization_spaces=False,
228+
)
229+
return outputs[0]
230+
```
231+
232+
### End-to-End Example
233+
234+
```python
235+
model = load_model("taresco/KarantaOCR")
236+
processor = load_processor("taresco/KarantaOCR")
237+
238+
prompt = """Below is the image of one page of a PDF document.
239+
Just return the plain text representation of this document as if you were reading it naturally.
240+
Turn equations into a LaTeX representation, and tables into markdown format. Remove the headers and footers, but keep references and footnotes.
241+
Read any natural handwriting.
242+
This is likely one page out of several in the document, so be sure to preserve any sentences that come from the previous page, or continue onto the next page, exactly as they are.
243+
If there is no text at all that you think you should read, you can output null.
244+
if the document contains diacritics, please include them in the output.
245+
Do not hallucinate.
246+
"""
247+
248+
messages = build_message(
249+
image_url="example.pdf",
250+
system_prompt=prompt,
251+
page=0
252+
)
253+
254+
output_text = run_inference(model, processor, messages)
255+
print(output_text)
256+
```
24257

25258
## Setup
26259

@@ -38,7 +271,12 @@ Our repository uses uv environment to manage dependencies. To set up the environ
38271
```
39272

40273

274+
## Citation Information
275+
276+
Coming soon ...
277+
278+
41279
## License
42280
...
43281

44-
## Citing
282+

0 commit comments

Comments
 (0)