The model was evaluated against leading proprietary Vision-Language models on standard Ottoman Turkish test sets using character accuracy (100% - CER).
1import os
2import torch
3from PIL import Image
4from transformers import AutoProcessor, Qwen3_5ForConditionalGeneration
5from qwen_vl_utils import process_vision_info
6
7# Device & dtype settings
8device = "cuda" if torch.cuda.is_available() else "cpu"
9dtype = torch.float16 if device == "cuda" else torch.float32
10
11model_id = "OttomanNLP/Azra-1-Mini-0.8b"
12
13print("[INFO] Loading model and processor...")
14processor = AutoProcessor.from_pretrained(model_id, trust_remote_code=True)
15model = Qwen3_5ForConditionalGeneration.from_pretrained(
16 model_id,
17 torch_dtype=dtype,
18 device_map="auto" if device == "cuda" else None,
19 trust_remote_code=True
20)
21model.eval()
22print("[INFO] Model loaded successfully!")
23
24def extract_text(image_path: str, prompt: str = "Görseldeki Osmanlıca metni transkribe et:") -> str:
25 """Extract Ottoman text from a line image"""
26 if not os.path.exists(image_path):
27 return f"File not found: {image_path}"
28
29 image = Image.open(image_path).convert("RGB")
30
31 # Adjust dimensions to multiples of 64
32 w, h = image.size
33 new_w = ((w + 63) // 64) * 64
34 new_h = ((h + 63) // 64) * 64
35 if (new_w, new_h) != (w, h):
36 image = image.resize((new_w, new_h), Image.Resampling.LANCZOS)
37
38 messages = [{
39 "role": "user",
40 "content": [
41 {"type": "image", "image": image},
42 {"type": "text", "text": prompt}
43 ]
44 }]
45
46 text_input = processor.apply_chat_template(
47 messages, tokenize=False, add_generation_prompt=True
48 )
49 image_inputs, _ = process_vision_info(messages)
50
51 inputs = processor(
52 text=[text_input],
53 images=image_inputs,
54 padding=True,
55 return_tensors="pt"
56 ).to(device)
57
58 with torch.inference_mode():
59 generated_ids = model.generate(
60 **inputs,
61 max_new_tokens=512,
62 do_sample=False,
63 repetition_penalty=1.2,
64 no_repeat_ngram_size=3,
65 pad_token_id=processor.tokenizer.pad_token_id,
66 eos_token_id=processor.tokenizer.eos_token_id,
67 )
68
69 input_len = inputs.input_ids.shape[1]
70 output_text = processor.batch_decode(
71 generated_ids[:, input_len:],
72 skip_special_tokens=True,
73 clean_up_tokenization_spaces=False
74 )[0]
75
76 return output_text.strip()
77
78if __name__ == "__main__":
79 image_path = "sample_line.png" # Path to line-level image
80 text = extract_text(image_path)
81 print("📝 Transcribed Text:\n", text)
1@article{usta2026cross,
2 title={Cross-Lingual Transfer Learning and Autonomous Data Bootstrapping for VLM-Based Ottoman Turkish Handwritten Text Recognition},
3 author={Usta, G{\"o}khan and Alpo{\u{g}}lu, O{\u{g}}uz and G{\"u}nayd{\i}n, Fatih},
4 journal={Research Square (Preprint)},
5 year={2026},
6 doi={10.21203/rs.3.rs-10418926/v1},
7 note={Under Review at International Journal on Document Analysis and Recognition (IJDAR)}
8}