Article General May 23, 2026 3 min read

Deploying a lightweight Automatic Number Plate Recognition model

Pax Durand

Technical Writer

May 23, 2026
3 min read

Deploying an Automatic Number Plate Recognition (ANPR) system on microcontrollers, ESP32-CAMs, or tinyML edge devices means working with incredibly tight memory constraints. Standard computer vision models like YOLOv8 or YOLO11 start at several megabytes, making them unusable for these setups.

To fit an entire ANPR workflow into a 100 KB footprint, you need to bypass heavy end-to-end deep learning networks. Instead, use highly optimized, single-task architectures paired with classic image processing.

Strategy 1: The Segmentation & Micro-Classifier Pipeline

Instead of forcing a single massive model to process the whole video frame, split the workload into two steps: programmatic image handling and a microscopic character classifier.

  • Plate Localization (0 KB): Use OpenCV (Python or C++) instead of a deep learning detector. Apply Grayscale conversion, Gaussian Blur, and Canny Edge Detection to find and isolate rectangular plate contours.
  • Character Segmentation (0 KB): Extract the localized license plate area and slice individual characters using simple contour bounding boxes.
  • Micro-CNN Character Classifier (< 80 KB): Train a bare-minimum Convolutional Neural Network (CNN) solely to recognize single alphanumeric characters (36 classes for English letters and digits).

Ultra-Lightweight CNN Architecture (PyTorch)

Here is a minimal PyTorch structure designed to stay well under the 100 KB limit once optimized and quantized:

import torch.nn as nn

class UltraLightANPR(nn.Module):
    def __init__(self):
        super(UltraLightANPR, self).__init__()
        # Input: 28x28 grayscale cropped character images
        self.features = nn.Sequential(
            nn.Conv2d(1, 8, kernel_size=3, padding=1), # 8 filters keeps parameter count tiny
            nn.ReLU(),
            nn.MaxPool2d(2, 2), # Reducer: down to 14x14
            
            nn.Conv2d(8, 16, kernel_size=3, padding=1),
            nn.ReLU(),
            nn.MaxPool2d(2, 2) # Reducer: down to 7x7
        )
        self.classifier = nn.Sequential(
            nn.Linear(16 * 7 * 7, 36), # 784 input features -> 36 alphanumeric character outputs
        )

    def forward(self, x):
        x = self.features(x)
        x = x.view(x.size(0), -1)
        return self.classifier(x)

Strategy 2: Strip Down an End-to-End LPRNet

If your project demands an end-to-end reading network without manual character segmentation steps, look into a heavily pruned version of LPRNet (License Plate Recognition Network).

  • Structure: LPRNet relies on depthwise separable convolutions coupled with a Connectionist Temporal Classification (CTC) loss function to read text sequences.
  • Shrinkage: To hit the 100 KB mark, you must strip the backbone down to 3 or 4 total convolutional layers and clamp global context channels to a maximum of 32 or 64.

How to Force Your Model Under 100 KB

Building a small architecture is only half the battle. You must apply aggressive optimization steps right after training:

  1. INT8 Quantization: Convert your model weights from FP32 (32-bit floating-point) down to INT8 (8-bit integers) using TensorFlow Lite (TFLite) Micro or PyTorch Edge. This instantly cuts your file size by 75% without changing a single line of your network architecture.
  2. Weight Pruning: Run sparsity routines during training to strip out 30% to 50% of the weakest weight connections, removing unnecessary parameters entirely.
  3. Depthwise Separable Convolutions: Swap out any standard convolutional layers for depthwise separable versions to dramatically reduce internal math and storage footprints.

Training Data Resources

Ready to train your micro-model? You can source bounding box configurations and plate crop collections from public repositories like the Kaggle Car License Plate Detection Dataset or regional open-source platforms.

Leave a Reply

Your email address will not be published. Required fields are marked *