Hurricane OCR is a high-performance OCR model specifically fine-tuned for reading
Thai license plates. Built on top of
SCB-10X's Typhoon-OCR 1.5 (2B) using
LoRA (Low-Rank Adaptation), this model efficiently extracts structured information from license plate images with
86.7% accuracy.
1import torch
2from transformers import AutoProcessor, AutoModelForVision2Seq
3from peft import PeftModel
4from PIL import Image
5
6# Load processor and base model
7base_model_name = "scb10x/typhoon-ocr1.5-2b"
8processor = AutoProcessor.from_pretrained(base_model_name)
9base_model = AutoModelForVision2Seq.from_pretrained(
10 base_model_name,
11 torch_dtype=torch.float16,
12 device_map="auto"
13)
14
15# Load LoRA adapter
16model = PeftModel.from_pretrained(base_model, "Rattatammanoon/hurricane-ocr-tlpr-v1-LoRA")
17model.eval()
18
19# Process license plate image
20image = Image.open("license_plate.jpg").convert("RGB")
21pixel_values = processor(images=image, return_tensors="pt").pixel_values.to(model.device)
22
23# Generate OCR output
24with torch.no_grad():
25 generated_ids = model.generate(
26 pixel_values,
27 max_length=512,
28 num_beams=4,
29 early_stopping=True
30 )
31
32text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
33print(text)
1# Process multiple plates at once
2images = [Image.open(f"plate{i}.jpg").convert("RGB") for i in range(5)]
3pixel_values = processor(images=images, return_tensors="pt").pixel_values.to(model.device)
4
5with torch.no_grad():
6 generated_ids = model.generate(pixel_values, max_length=512, num_beams=4)
7
8texts = processor.batch_decode(generated_ids, skip_special_tokens=True)
9for i, text in enumerate(texts):
10 print(f"Plate {i+1}: {text}")
1**Plate Number:** กก 1234
2**Characters:** กก
3**Digits:** 1234
4**Province:** กรุงเทพมหานคร
1import re
2
3# Parse the OCR output
4lines = text.strip().split('\n')
5result = {}
6for line in lines:
7 if '**' in line:
8 key, value = line.split(':', 1)
9 key = key.strip('*').strip()
10 result[key] = value.strip()
11
12print(result)
13# {'Plate Number': 'กก 1234', 'Characters': 'กก', ...}
1generated_ids = model.generate(
2 pixel_values,
3 max_length=512,
4 num_beams=5, # Increase for better quality (slower)
5 temperature=0.7, # Lower = more deterministic
6 top_p=0.9,
7 repetition_penalty=1.2,
8 early_stopping=True
9)
1from ultralytics import YOLO
2
3# 1. Detect license plate region
4detector = YOLO("path/to/plate_detector.pt")
5results = detector("car_image.jpg")
6
7# 2. Extract and process each detected plate
8for result in results:
9 for box in result.boxes:
10 # Crop plate region
11 x1, y1, x2, y2 = map(int, box.xyxy[0])
12 plate_crop = image.crop((x1, y1, x2, y2))
13
14 # 3. Run Hurricane OCR
15 pixel_values = processor(images=plate_crop, return_tensors="pt").pixel_values
16 generated_ids = model.generate(pixel_values)
17 text = processor.batch_decode(generated_ids, skip_special_tokens=True)[0]
18
19 print(f"Detected plate: {text}")
1from PIL import Image, ImageEnhance
2
3def preprocess_plate(image_path):
4 img = Image.open(image_path).convert("RGB")
5
6 # Resize if needed (maintain aspect ratio)
7 if img.width > 400:
8 ratio = 400 / img.width
9 new_size = (400, int(img.height * ratio))
10 img = img.resize(new_size, Image.LANCZOS)
11
12 # Enhance contrast (optional)
13 enhancer = ImageEnhance.Contrast(img)
14 img = enhancer.enhance(1.2)
15
16 return img
This model is licensed under
Apache 2.0. See
LICENSE for details.
1@misc{hurricane-ocr-v1-2025,
2 author = {Rattatammanoon},
3 title = {Hurricane OCR - Thai License Plate Recognition},
4 year = {2025},
5 publisher = {Hugging Face},
6 journal = {Hugging Face Model Hub},
7 howpublished = {\url{https://huggingface.co/Rattatammanoon/hurricane-ocr-tlpr-v1-LoRA}}
8}