Views
No views yet

| Property | Value |
|---|---|
| Architecture | CNN (5 conv blocks) + BiLSTM (128 hidden × 2 layers) + CTC decoder |
| Parameters | 1,117,170 |
| Input | [batch, 96, 192, 1] — grayscale, NHWC, uint8 (0-255) |
| Output | [batch, 48, 38] — 48 CTC timesteps, 38 classes |
| Alphabet | 0-9, A-Z, _ (pad) + CTC blank (index 37) |
| ONNX size | 4.4 MB |
| ONNX opset | 18 (IR version 8) |
| Inference | ~5ms CPU, ~2ms GPU |
| Metric | Score |
|---|---|
| Plate accuracy (exact match) | 98.4% |
| Character accuracy | 97.8% |
| Validation set | 9,074 crops |
| Training set | 81,666 crops from 9,727 labeled detections |
AAP096)672625)O)1import numpy as np
2import cv2
3import onnxruntime as ort
4
5ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_"
6BLANK = 37 # CTC blank token
7
8def load_and_preprocess(image_path, h=96, w=192):
9 """Load image, convert to grayscale, resize with aspect-preserving padding."""
10 img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
11 src_h, src_w = img.shape[:2]
12 scale = min(h / src_h, w / src_w)
13 new_h, new_w = int(src_h * scale), int(src_w * scale)
14 resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)
15 canvas = np.full((h, w), 128, dtype=np.uint8)
16 y_off, x_off = (h - new_h) // 2, (w - new_w) // 2
17 canvas[y_off:y_off+new_h, x_off:x_off+new_w] = resized
18 return canvas
19
20def ctc_decode(logits):
21 """Greedy CTC decode: collapse repeats, remove blanks."""
22 indices = np.argmax(logits, axis=-1)
23 chars = []
24 prev = -1
25 for idx in indices:
26 if idx != prev and idx != BLANK and idx < len(ALPHABET):
27 chars.append(ALPHABET[idx])
28 prev = idx
29 return ''.join(chars)
30
31def predict(session, image_path):
32 """Run OCR on a plate crop image."""
33 gray = load_and_preprocess(image_path)
34 blob = gray.reshape(1, 96, 192, 1).astype(np.uint8)
35 logits = session.run(None, {"input": blob})[0][0] # [48, 38]
36 return ctc_decode(logits)
37
38# Usage
39session = ort.InferenceSession("wink-lpr-ocr-cr.onnx")
40plate_text = predict(session, "plate_crop.jpg")
41print(f"Plate: {plate_text}")[batch, 96, 192, 1] NHWC format — no normalization needed, the model handles it internally[batch, 48, 38] logits — 48 CTC timesteps over 38 classes:0-9A-Z_1@misc{wink-lpr-ocr-cr-2026,
2 title={WINK LPR OCR — Costa Rica License Plate Recognition},
3 author={WINK Streaming},
4 year={2026},
5 url={https://www.wink.co},
6 note={98.4\% plate accuracy CTC-CRNN model}
7}