# fast-alpr: ONNX license plate detection and OCR in Python

> fast-alpr wraps a plate detector and an OCR model behind one ALPR class, with ONNX Runtime as the only inference backend. The design is clean, the install has one real trap, and the model zoo is narrower than the README implies.

**ankandrew/fast-alpr** — Fast Automatic License Plate Recognition (ALPR) framework.

- Repository: https://github.com/ankandrew/fast-alpr
- Website: https://ankandrew.github.io/fast-alpr/
- Stars: 823 · Forks: 131
- Language: Python
- License: MIT
- Published: 2026-09-16 · Updated: 2026-09-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/ankandrew-fast-alpr

## What fast-alpr actually does, and who it is for

fast-alpr is a Python library that takes an image and returns license plate detections with recognised text. It is aimed at developers who are building the recognition step into their own pipeline, not at operators who want a finished product. The README describes it as a "high-performance, customizable Automatic License Plate Recognition (ALPR) system" and says the defaults are ONNX models that you can swap out.

The scope is deliberately two-stage. Detection finds the plate rectangle; OCR reads the characters inside it. Everything else that people associate with ALPR products, such as camera management, storage, search, retention policy or a review interface, is outside the library. If your requirement list includes any of those, fast-alpr is one component of the system you would build, not the system itself.

The package metadata lists three runtime dependencies: fast-plate-ocr, open-image-models and opencv-python-headless. Those two first-party projects are where the actual models live, and fast-alpr is the glue that connects them behind a single class. That is worth knowing before you evaluate it, because the quality of your results depends mostly on those two dependencies rather than on this repository.

## How the ALPR class wires detection and OCR together

The public entry point is the ALPR class, constructed with a detector_model name and an ocr_model name. Both strings resolve to pretrained checkpoints that the underlying libraries download and run through ONNX Runtime. Calling predict() on an image path returns results that carry the plate boxes and the recognised text.

The interesting part is the substitution seam. The OCR side is typed against an abstract BaseOCR class with a predict method that takes a cropped plate as a NumPy array and returns an OcrResult with text and confidence. The README's Tesseract example implements exactly that interface, so a non-ONNX OCR engine can be dropped in without touching the detection path. Detection is configured by model name rather than by object, which means swapping detectors is a string change, but writing a custom detector is not shown in the README.

The second convenience method is draw_predictions, which takes a frame already loaded with OpenCV and returns an object holding both the annotated image and the raw results. That split matters in practice: you get the visual for debugging and the structured data for your own logic from the same call. The README's example uses cv2.imread to load the frame first, so image decoding is your responsibility, not the library's, when you use that path.

## Installing fast-alpr with the right ONNX backend

The installation section contains the one thing that trips people up. The package declares no ONNX runtime as a hard dependency, so a bare pip install fast-alpr gives you the code without an inference backend. The README states this plainly: "By default, no ONNX runtime is installed. To run inference, you must install at least one ONNX backend using an appropriate extra." Choose the extra that matches your hardware.

```bash
pip install fast-alpr[onnx]
```

That is the cross-platform CPU option. The README's table lists alternatives: onnx-gpu for NVIDIA CUDA on Linux and Windows, onnx-openvino described as best on Intel CPUs, onnx-directml for DirectML on Windows, and onnx-qnn for Qualcomm chipsets. Pick one. Installing several runtime packages into the same environment is a known source of import conflicts in the ONNX ecosystem generally, and nothing in the README suggests fast-alpr guards against it.

Once installed, the quick start is short. Construct the ALPR object with the default model names and call predict on an image path.

```python
from fast_alpr import ALPR

alpr = ALPR(
    detector_model="yolo-v9-t-384-license-plate-end2end",
    ocr_model="cct-xs-v2-global-model",
)

alpr_results = alpr.predict("assets/test_image.png")
print(alpr_results)
```

The README notes that assets/test_image.png can be found in the repository root, so clone the repo or point the path at your own file. Expect the first call to be slow: the model weights have to be fetched before inference runs. If you want the annotated image as well, load the frame yourself and use the drawing helper.

```python
import cv2
from fast_alpr import ALPR

alpr = ALPR(
    detector_model="yolo-v9-t-384-license-plate-end2end",
    ocr_model="cct-xs-v2-global-model",
)
frame = cv2.imread("assets/test_image.png")
drawn = alpr.draw_predictions(frame)
annotated_frame = drawn.image
results = drawn.results
```

The README also points at a Hugging Face Space for trying it without installing anything, which is the cheaper way to sanity-check the model output before you commit to a backend choice.

## Swapping in Tesseract when the bundled OCR does not fit

The README's clearest statement of intent is the custom OCR example. It subclasses BaseOCR, runs pytesseract.image_to_data on the cropped plate with lang="eng" and config="--oem 3 --psm 6", joins the text tokens, strips non-alphanumeric characters with a regex, and averages the confidences above zero, dividing by 100 to normalise to a 0 to 1 range. The method returns an OcrResult built from that text and confidence.

```python
class PytesseractOCR(BaseOCR):
    def predict(self, cropped_plate: np.ndarray) -> OcrResult | None:
        if cropped_plate is None:
            return None
        data = pytesseract.image_to_data(
            cropped_plate,
            lang="eng",
            config="--oem 3 --psm 6",
            output_type=pytesseract.Output.DICT,
        )
        plate_text = " ".join(data["text"]).strip()
        plate_text = re.sub(r"[^A-Za-z0-9]", "", plate_text)
        avg_confidence = mean(conf for conf in data["conf"] if conf > 0) / 100.0
        return OcrResult(text=plate_text, confidence=avg_confidence)
```

Two things stand out. First, the interface is small enough that a weekend port to another OCR engine is realistic. Second, the example's confidence calculation silently drops any token with a confidence of zero or below, which means a plate where Tesseract is unsure about every token produces an empty generator and mean() raises. The README does not address that case. If you copy this class into production, handle the empty sequence yourself.

The constructor call changes shape in this mode: you pass ocr=PytesseractOCR() instead of ocr_model, keeping detector_model as a string. That asymmetry is the API telling you detection and OCR are configured differently, and it is the main thing to internalise before writing your own integration.

## Where fast-alpr stops short

The repository has no tracking component. There is no code path that associates a plate across frames, no Kalman filter, no track IDs in the results. If your use case is a video stream where the same car appears in twenty consecutive frames, deduplication and tracking are yours to write. The related-search term "ALPR tracker" describes something this library does not provide.

There is also no speed estimation, no vehicle make or model classification, no colour detection, and no REST server. The library returns plate text and boxes. Every one of those extra capabilities is a separate model and a separate integration.

Geographic coverage is a property of the OCR model you choose, not of fast-alpr. The default is named cct-xs-v2-global-model, and the README does not enumerate which countries or plate formats it handles. That is the single most important thing to test before adopting: run your own images through the default and check the character-level accuracy on the plate formats you actually see. The README's Tesseract example changes lang to "eng", which hints that language is a per-model concern rather than something fast-alpr coordinates.

Finally, the README does not document rollback, version pinning of model weights, or offline operation. If you need to run without network access, you would have to verify how the underlying libraries cache their checkpoints, because fast-alpr itself says nothing about it.

## fast-alpr against OpenALPR and rolling your own

The obvious comparison is OpenALPR, the older C++ project with a Python binding and a country-specific training pipeline. The difference in approach is stark. OpenALPR ships per-country recognition trained on regional plate fonts and includes tracking and a daemon. fast-alpr ships a general detector plus a general OCR model and no daemon at all. If your plates are from a country with a distinctive font and OpenALPR has a trained configuration for it, that project's narrower training may beat a global model. If you want a pip-installable Python package with no build step and a swappable OCR interface, fast-alpr is the shorter path.

The other alternative is assembling the pieces yourself: open-image-models for detection, fast-plate-ocr for recognition, and your own code in between. That is essentially what fast-alpr is, so the honest question is whether the ALPR class and draw_predictions save you enough code to justify the extra dependency. For a single image path, probably not. For a pipeline where you want the substitution seam and the annotated output together, the wrapper earns its place.

## Maintenance, licence and the cost of upgrading

The repository is not archived, and the last push was on 2026-03-16. Release v0.4.0 was tagged on 2026-03-15, following v0.3.0 in August 2025 and v0.2.0 in July 2025. The cadence is irregular rather than steady, and there is no published support policy or deprecation window in the README.

The practical upgrade cost sits in the model names. Because detector_model and ocr_model are strings resolved by the dependencies, a bump in fast-plate-ocr or open-image-models can change what those strings resolve to or remove them. The pyproject pins floors, not ceilings: fast-plate-ocr>=1.1.0 and open-image-models>=0.5.1. A fresh install today can therefore pull a newer minor version than the one you tested. Pin your lockfile.

The code is MIT licensed, which is permissive and places few obligations on how you redistribute the library. The licence covers the code in this repository. It does not automatically cover the ONNX weights, which are hosted separately by the two dependency projects, and the README does not state their terms. If you are shipping a product, check the model licences at their source rather than assuming the MIT badge on this repository extends to them. That is a factual gap in the documentation, not a legal opinion.

## Conclusion

Adopt fast-alpr if you are writing Python and want a plate detector and OCR model behind one predict() call, with the option to swap either half for your own class. Do not adopt it if you need a running service with a database, a REST API or a review UI; it is a library, and the README documents no such components. Before committing, verify that the ONNX Runtime extra matching your hardware installs cleanly, that your plate format is covered by the OCR model you pick, and what licence the weights carry separately from the MIT code, since the repository licence covers the code and not necessarily the model files.

## FAQ

### Can fast-alpr detect speed?

No. The library returns license plate detections and recognised text from an image. There is no speed estimation, no tracking across frames, and no vehicle motion model anywhere in the documented API.

### How much does an ALPR system cost?

The fast-alpr code is MIT licensed and installs from PyPI, so the library itself carries no licence fee. The documentation does not state pricing for the model weights hosted by fast-plate-ocr and open-image-models, so the total cost of an ALPR system built on it cannot be determined from what is published.

### What is the best free license plate reader software?

The published documentation only describes fast-alpr, so no comparison with other free readers can be made from it. fast-alpr is a Python library that pairs an ONNX detector with an ONNX OCR model and lets you substitute your own OCR class.

### How to avoid ALPR?

This is not covered. fast-alpr is a recognition library and the README documents no countermeasures, evasion techniques or privacy features.

## Sources

- [ankandrew/fast-alpr on GitHub](https://github.com/ankandrew/fast-alpr)
- [License: MIT](https://github.com/ankandrew/fast-alpr/blob/master/LICENSE)
- [Project website](https://ankandrew.github.io/fast-alpr/)
- [README](https://github.com/ankandrew/fast-alpr/blob/master/README.md)
- [Releases](https://github.com/ankandrew/fast-alpr/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ankandrew-fast-alpr
