---
kind: concept
slug: license-plate-recognition
title: License plate recognition
summary: >-
  Reading the characters of a vehicle plate from camera images or video: a chain of detectors (vehicle, plate, characters), then separate letter and digit recognisers, with a per-vehicle vote over frames. One design so far, built for Brazilian plates, reported at 93.53% on SSIG and 78.33% on UFPR-ALPR with voting. Choose by plate layout, whether you have video, and latency.
tags: [computer-vision, ocr, license-plates, real-time]
papers: ["1802.09567"]
related: [data-augmentation]
status: draft
updated_at: 2026-10-18
---

## Overview

Automatic license plate recognition (ALPR) turns a camera view of a vehicle
into a plate string. The usual chain has four links: find the vehicle, find the
plate on it, cut out each character, and decide what each character is. An
error in an early link cannot be repaired by a later one [arxiv:1802.09567 §1].

Only one paper with a card covers this concept so far. The page therefore
describes a single design and the constraints it was built for. It does not yet
compare alternatives, so the "when to use which" table asks what you have on
hand rather than ranking designs.

## Variants

### Detector cascade with per-vehicle voting (Brazilian plates)

Each link is a separate convolutional detector, so each can be tuned on its own
[arxiv:1802.09567 §4]:

1. A vehicle detector on the full frame. The paper uses Fast-YOLO on the
   SSIG benchmark and YOLOv2 on UFPR-ALPR, because Fast-YOLO reached at most
   97.33% vehicle recall there [arxiv:1802.09567 §5.2.1].
2. A plate detector on a margin-enlarged vehicle crop, keeping only its most
   confident box [arxiv:1802.09567 §4.1].
3. A character segmenter on the plate, stretched to a 2.75:1 aspect ratio
   before it runs. Overlapping boxes are merged, or the weakest are dropped,
   until seven remain [arxiv:1802.09567 §4.2].
4. Two character recognisers: one for the first three positions (letters) and
   one for the last four (digits). Position alone decides which one runs
   [arxiv:1802.09567 §4.3].
5. Across all frames of one vehicle, each character position takes its most
   frequent reading [arxiv:1802.09567 §4.4].

The author-reported results, with the voting step, are 93.53% of vehicles
read completely correct on SSIG [arxiv:1802.09567 tab. 4] and 78.33% on
UFPR-ALPR [arxiv:1802.09567 tab. 6]. Without voting, the per-frame figures are
85.45% and 64.89% [arxiv:1802.09567 tab. 4] [arxiv:1802.09567 tab. 6]. The
author-reported speed is 47 FPS on SSIG [arxiv:1802.09567 tab. 5] and 35 FPS on
UFPR-ALPR [arxiv:1802.09567 tab. 7], on one NVIDIA Titan Xp. The authors do not itemise the cropping and resizing time
[arxiv:1802.09567 §5.1].

Card: [Laroca et al., YOLO-based ALPR](/papers/1802.09567).

Caveats before reuse:

- **Layout-specific.** The letter/digit split and the sort order assume a
  three-letter, four-digit plate, with characters ordered left to right on
  cars and top to bottom on motorcycles. The authors say the design does not
  fit countries with several plate layouts [arxiv:1802.09567 §4.3].
- **No code or weights from the authors.** The paper links no repository, and
  it says the models are available for academic use only [arxiv:1802.09567 §1].
  Plan to retrain on your own data. Commercial reuse of their weights would
  need a separate check.
- **Thresholds and margins were tuned per dataset** on the validation split
  [arxiv:1802.09567 §5].
- **Training data is small.** The SSIG training set has under 800 examples.
  The authors add flipped and colour-inverted samples, and say augmentation was
  essential for the letters [arxiv:1802.09567 §1] [arxiv:1802.09567 §5.1.4].
  See [data augmentation](/concepts/data-augmentation) for the general pattern.

The detectors build on Fast-YOLO and [YOLOv2](https://arxiv.org/abs/1612.08242),
and the character segmenter builds on CR-NET. None of these has a card here,
so this page does not use their content.

## When to use which

| Situation | Use | Why |
|---|---|---|
| Brazilian plates, and you have video per vehicle | The cascade above, with voting | Voting raised the all-correct vehicle rate from 85.45% to 93.53% on SSIG [arxiv:1802.09567 tab. 4] and from 64.89% to 78.33% on UFPR-ALPR [arxiv:1802.09567 tab. 6]. |
| Brazilian plates, single still images only | The cascade without voting, and expect lower accuracy | The per-frame all-correct rates are 85.45% (SSIG) and 64.89% (UFPR-ALPR). Voting needs several frames of the same vehicle [arxiv:1802.09567 §4.4]. |
| Plates from several countries or layouts | Not this design as it stands | The paper's letter/digit split and sort order assume one layout [arxiv:1802.09567 §4.3]. The paper does not evaluate other layouts. |
| Live video, GPU available, real-time needed | The cascade, if the author-reported speed holds on your hardware | 47 FPS on SSIG and 35 FPS on UFPR-ALPR, on a Titan Xp [arxiv:1802.09567 tab. 5] [arxiv:1802.09567 tab. 7]. Cropping and resizing time is not itemised [arxiv:1802.09567 §5.1]. |
| Need to reject unreadable plates | Add a rejection rule yourself | The design always keeps the top box and the top reading, even with low confidence [arxiv:1802.09567 §4.1] [arxiv:1802.09567 §4.3]. The paper does not evaluate a rejection rule. |
| Benchmark against a commercial service | Compare on the same frames, with the same date | The commercial systems were queried in January 2018 and not tuned for these datasets [arxiv:1802.09567 §5]. |

## Implementation sketch

The common core of the cascade, in our own words. The per-stage thresholds
and margins differ between datasets; see the card for the values.

```pseudocode
# Read one vehicle track and vote per character position  [arxiv:1802.09567 §4]
# Input: frames of one vehicle, four trained detectors
# Output: a 7-character plate string

def read_track(frames):
    readings = []
    for frame in frames:
        # Find the vehicle; a frame with no vehicle gives no reading  [§4.1]
        vehicle = top_box(vehicle_detector(frame, thr=0.125))
        if vehicle is None:
            continue
        # Find the plate, keeping the top box even at low confidence  [§4.1]
        plate = top_box(plate_detector(margin_crop(frame, vehicle)))
        # Stretch to 2.75:1, then segment characters  [§4.2]
        plate_img = stretch(margin_crop(frame, plate), aspect=2.75)
        boxes = char_segmenter(plate_img)
        # Reduce to seven boxes by merging or dropping  [§4.2]
        boxes = reduce_to_seven(boxes, overlap_iou)
        # Letters first (three), then digits (four)  [§4.3]
        boxes = sort_for(vehicle.kind, boxes)
        readings.append([
            letter_net(b) if i < 3 else digit_net(b)
            for i, b in enumerate(boxes)
        ])
    # Most frequent reading at each position across frames  [§4.4]
    return [most_common(pos) for pos in zip(*readings)]
```

## Contradictions

None known. Only one source paper is on file. Its per-frame and voted results
are reported side by side, not in conflict.

## Papers

- [Laroca et al., YOLO-based ALPR](/papers/1802.09567): a four-stage detector cascade with separate letter and digit recognisers and per-vehicle voting, for Brazilian plates; introduces the UFPR-ALPR dataset.
