šŸš€ Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more →
Socket
Book a DemoInstallSign in
Socket

SimpleLPR

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

SimpleLPR

SimpleLPR License Plate Recognition (LPR/ANPR) library.

3.6.0
PyPI
Maintainers
1

SimpleLPR Python

šŸš— High-performance license plate recognition for Python developers. Detect and read vehicle license plates from images and video streams with just a few lines of code.

šŸŽÆ Overview

SimpleLPR brings professional-grade license plate recognition to Python. Whether you're building a parking management system, analyzing traffic patterns, or developing security applications, SimpleLPR handles the complexity of plate detection and OCR so you can focus on your application logic.

Built on over 10 years of continuous development and deployed in thousands of production systems worldwide, SimpleLPR delivers reliable performance with a developer-friendly API.

✨ Key Features

  • šŸ“Š High Accuracy: 85-95% recognition rate under typical conditions
  • ⚔ Real-time Processing: Analyze video streams with multi-threaded performance
  • šŸŒ Global Coverage: Support for ~90 countries with region-specific formats
  • šŸ”„ Video Tracking: Track plates across frames for improved accuracy
  • šŸ Pythonic API: Native Python interface that feels natural
  • šŸ­ Production Ready: Battle-tested in commercial deployments worldwide

šŸ“‹ What is SimpleLPR?

SimpleLPR is more than just an OCR library - it's a complete license plate recognition system that handles the entire workflow from image acquisition to final text output. The library automatically:

  • Detects license plates in complex scenes
  • Extracts the plate region with precise boundaries
  • Corrects perspective and lighting issues
  • Reads alphanumeric characters with high accuracy
  • Validates results against country-specific formats
  • Tracks plates across video frames for consistency

šŸŽÆ Real-World Performance

SimpleLPR achieves industry-leading accuracy under typical conditions:

  • Plate text height of at least 20 pixels
  • Reasonable image quality without severe motion blur
  • Plates in good physical condition
  • Viewing angles within ±30 degrees

The library has been extensively tested in production environments including:

  • šŸ…æļø Parking facilities with varying lighting conditions
  • šŸ›£ļø Highway toll collection systems processing thousands of vehicles
  • 🚪 Access control gates with fixed camera positions
  • šŸ“± Mobile enforcement units with handheld cameras
  • šŸ“¹ Traffic monitoring systems with wide-angle views

šŸ’» Requirements

  • Python: 3.8, 3.9, 3.10, 3.11, or 3.12 (64-bit)
  • OS: Windows x64, Linux x64 (Ubuntu 20.04+)
  • Memory: 2GB RAM minimum, 4GB+ recommended for video
  • License: 60-day trial included, production licenses available

šŸ“¦ Installation

pip install SimpleLPR

šŸš€ Quick Start

Basic Image Recognition

import simplelpr

# Initialize the engine
setup_params = simplelpr.EngineSetupParms()
engine = simplelpr.SimpleLPR(setup_params)

# Configure for your country (e.g., UK = 90)
engine.set_countryWeight(90, 1.0)
engine.realizeCountryWeights()

# Create a processor
processor = engine.createProcessor()

# Analyze an image
candidates = processor.analyze("car_image.jpg")

# Print results
for candidate in candidates:
    for match in candidate.matches:
        print(f"Plate: {match.text}")
        print(f"Confidence: {match.confidence:.3f}")

šŸŒ Country Configuration

SimpleLPR supports approximately 90 countries. Use --list-countries in the demo below to see all options.

Popular countries:

  • šŸ‡¦šŸ‡¹ Austria: 5
  • šŸ‡§šŸ‡· Brazil: 12
  • šŸ‡ØšŸ‡¦ Canada: 16
  • šŸ‡«šŸ‡· France: 32
  • šŸ‡©šŸ‡Ŗ Germany: 34
  • šŸ‡®šŸ‡³ India: 41
  • šŸ‡®šŸ‡¹ Italy: 45
  • šŸ‡ŖšŸ‡ø Spain: 82
  • šŸ‡¬šŸ‡§ UK: 90

šŸŽ¬ Complete Video Tracking Demo

This demo shows how to process video files or RTSP streams with real-time plate tracking:

#!/usr/bin/env python3
"""
SimpleLPR Video Tracking Demo

Track license plates across video frames with temporal correlation.
This improves accuracy by reducing transient misreads.

Usage:
    python tracking_demo.py <video_source> <country_id> [product_key]
    python tracking_demo.py --list-countries

Examples:
    python tracking_demo.py traffic.mp4 82
    python tracking_demo.py rtsp://camera:554/stream 90 license.xml
"""

import sys
import os
import argparse
import time
from datetime import datetime
from collections import deque

try:
    import simplelpr
except ImportError:
    print("āŒ Error: SimpleLPR is not installed. Run 'pip install SimpleLPR'")
    sys.exit(1)


def list_supported_countries():
    """Display all supported countries"""
    print("\nšŸŒ SimpleLPR - Supported Countries")
    print("="*50)
    
    setup_params = simplelpr.EngineSetupParms()
    engine = simplelpr.SimpleLPR(setup_params)
    
    # Show version
    ver = engine.versionNumber
    print(f"šŸ“Œ Version: {ver.A}.{ver.B}.{ver.C}.{ver.D}\n")
    
    print("ID  : Country")
    print("-"*30)
    for i in range(engine.numSupportedCountries):
        print(f"{i:3d} : {engine.get_countryCode(i)}")
    
    print("\nšŸ’” Use either the country name or ID as the country_id parameter.")


def configure_country(engine, country_id):
    """Configure engine for specific country"""
    # Disable all countries first
    for i in range(engine.numSupportedCountries):
        engine.set_countryWeight(i, 0.0)
    
    # Try as string first, then as integer
    country_name = None
    if isinstance(country_id, str):
        try:
            engine.set_countryWeight(country_id, 1.0)
            country_name = country_id
        except:
            # Try as integer
            try:
                country_idx = int(country_id)
                if 0 <= country_idx < engine.numSupportedCountries:
                    engine.set_countryWeight(country_idx, 1.0)
                    country_name = engine.get_countryCode(country_idx)
                else:
                    raise ValueError(f"Country ID {country_idx} out of range")
            except ValueError:
                print(f"\nāŒ Error: Invalid country '{country_id}'")
                print("šŸ’” Run with --list-countries to see valid options")
                sys.exit(1)
    else:
        # Integer country ID
        engine.set_countryWeight(country_id, 1.0)
        country_name = engine.get_countryCode(country_id)
    
    engine.realizeCountryWeights()
    return country_name


def process_tracker_result(tracker_result, track_count):
    """Process and display tracker results"""
    # Process new tracks
    for track in tracker_result.newTracks:
        track_count += 1
        if track.representativeCandidate.matches:
            match = track.representativeCandidate.matches[0]
            print(f"šŸ†• [NEW #{track_count:03d}] {match.text:12} "
                  f"Country: {match.country} "
                  f"Confidence: {match.confidence:.3f} "
                  f"Frame: {track.firstDetectionFrameId}")
    
    # Process closed tracks
    for track in tracker_result.closedTracks:
        if track.representativeCandidate.matches:
            match = track.representativeCandidate.matches[0]
            duration = (track.newestDetectionTimestamp - 
                       track.firstDetectionTimestamp)
            frames = (track.newestDetectionFrameId - 
                     track.firstDetectionFrameId + 1)
            print(f"āœ… [CLOSED] {match.text:12} "
                  f"Duration: {duration:.1f}s ({frames} frames)")
    
    return track_count


def process_pending_results(proc_pool, tracker, frame_queue, track_count):
    """Process all pending results from the pool"""
    while True:
        result = proc_pool.pollNextResult(0, simplelpr.TIMEOUT_IMMEDIATE)
        if result is None:
            break
        
        if frame_queue:
            orig_frame = frame_queue.popleft()
            tracker_result = tracker.processFrameCandidates(
                result, orig_frame
            )
            track_count = process_tracker_result(tracker_result, track_count)
    
    return track_count


def process_video(video_path, country_id, product_key=None):
    """Main video processing function"""
    
    print("\nšŸš— SimpleLPR Video Tracking Demo")
    print("="*50)
    
    # Initialize engine
    setup_params = simplelpr.EngineSetupParms()
    setup_params.maxConcurrentImageProcessingOps = 0   # Let the ANPR engine decide
    
    engine = simplelpr.SimpleLPR(setup_params)
    
    # Show version
    ver = engine.versionNumber
    print(f"šŸ“Œ Version: {ver.A}.{ver.B}.{ver.C}.{ver.D}")
    
    # Load product key if provided
    if product_key:
        try:
            engine.set_productKey(product_key)
            print("šŸ”‘ Product key loaded")
        except:
            print("āš ļø  Warning: Failed to load product key")
    else:
        print("šŸ”“ Running in evaluation mode")
    
    # Configure country
    country_name = configure_country(engine, country_id)
    print(f"šŸŒ Country: {country_name}")
    
    # Create processor pool
    proc_pool = engine.createProcessorPool(3) # 3 processors in the pool
    proc_pool.plateRegionDetectionEnabled = True
    
    # Create tracker
    tracker_params = simplelpr.PlateCandidateTrackerSetupParms()
    tracker = engine.createPlateCandidateTracker(tracker_params)
    print(f"šŸ”„ Tracker: {tracker_params.minTriggerFrameCount} min frames, "
          f"{tracker_params.maxIdleTimeInSec}s max idle")
    
    # Open video
    print(f"\nšŸ“¹ Opening: {video_path}")
    video = engine.openVideoSource(
        video_path,
        simplelpr.FrameFormat.FRAME_FORMAT_BGR24,
        1920, 1080  # Max resolution
    )
    
    if video.state != simplelpr.VideoSourceState.VIDEO_SOURCE_STATE_OPEN:
        print(f"āŒ Error: Failed to open video (state: {video.state})")
        sys.exit(1)
    
    print(f"šŸ“” Type: {'Live stream' if video.isLiveSource else 'Video file'}")
    print("\nā–¶ļø  Processing... Press Ctrl+C to stop\n")
    
    # Processing variables
    frame_queue = deque()
    frame_count = 0
    track_count = 0
    start_time = time.time()
    
    try:
        # Main processing loop
        while True:
            # Get next frame
            frame = video.nextFrame()
            if frame is None:
                if video.isLiveSource:
                    print("šŸ”„ Stream interrupted, reconnecting...")
                    video.reconnect()
                    time.sleep(0.1)
                    continue
                else:
                    break  # End of file
            
            frame_count += 1
            frame_queue.append(frame)
            
            # Submit for processing
            success = proc_pool.launchAnalyze(
                0,  # Stream ID
                frame.sequenceNumber,
                simplelpr.TIMEOUT_INFINITE,
                frame
            )
            
            if not success:
                frame_queue.pop()
                continue
            
            # Process all available results
            track_count = process_pending_results(
                proc_pool, tracker, frame_queue, track_count
            )
            
            # Progress update
            if frame_count % 100 == 0:
                elapsed = time.time() - start_time
                fps = frame_count / elapsed
                print(f"šŸ“Š [Progress] {frame_count} frames @ {fps:.1f} fps")
        
        # Process remaining results
        print("\nā³ Finalizing...")
        while proc_pool.ongoingRequestCount_get(0) > 0:
            result = proc_pool.pollNextResult(0, 100)  # 100ms timeout
            if result and frame_queue:
                orig_frame = frame_queue.popleft()
                tracker_result = tracker.processFrameCandidates(
                    result, orig_frame
                )
                track_count = process_tracker_result(tracker_result, track_count)
        
        # Final tracker flush
        final_result = tracker.flush()
        for track in final_result.closedTracks:
            if track.representativeCandidate.matches:
                match = track.representativeCandidate.matches[0]
                print(f"šŸ“ [FINAL] {match.text}")
        
    except KeyboardInterrupt:
        print("\n\nāš ļø  Stopped by user")
    
    # Statistics
    elapsed = time.time() - start_time
    print(f"\n{'='*50}")
    print(f"āœ… Processing Complete!")
    print(f"šŸ“Š Frames processed: {frame_count:,}")
    print(f"šŸš— Plates tracked: {track_count}")
    print(f"ā±ļø  Processing time: {elapsed:.1f}s")
    print(f"⚔ Average speed: {frame_count/elapsed:.1f} fps")


def main():
    parser = argparse.ArgumentParser(
        description="SimpleLPR Video Tracking Demo",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
šŸ“ Examples:
  %(prog)s traffic.mp4 82              # Spain
  %(prog)s video.avi France            # Using country name
  %(prog)s rtsp://camera:554 90 key.xml  # UK with license

šŸ’” Tips:
  - Use --list-countries to see all supported regions
  - Country can be specified by name or ID number
  - RTSP streams will reconnect automatically
        """
    )
    
    parser.add_argument("video_source", nargs='?',
                        help="Video file path or RTSP stream URL")
    parser.add_argument("country_id", nargs='?',
                        help="Country name (e.g., 'Spain') or ID (0-102)")
    parser.add_argument("product_key", nargs='?',
                        help="License key file path (optional)")
    parser.add_argument("--list-countries", action="store_true",
                        help="Show all supported countries and exit")
    
    args = parser.parse_args()
    
    if args.list_countries:
        list_supported_countries()
        return
    
    if not args.video_source or not args.country_id:
        parser.print_help()
        print("\nšŸ’” Tip: Use --list-countries to see supported regions")
        sys.exit(1)
    
    # Process video
    process_video(args.video_source, args.country_id, args.product_key)


if __name__ == "__main__":
    main()

šŸ”§ Key Concepts

šŸ”„ Processor Pools

Enable concurrent processing of multiple frames for better throughput:

pool = engine.createProcessorPool(3)  # 3 parallel processors
pool.plateRegionDetectionEnabled = True  # Better accuracy

šŸŽÆ Plate Tracking

The tracker correlates detections across frames, eliminating transient misreads:

tracker_params = simplelpr.PlateCandidateTrackerSetupParms()
tracker_params.minTriggerFrameCount = 3  # Frames needed to confirm
tracker_params.maxIdleTimeInSec = 2.0    # Time before closing track
tracker = engine.createPlateCandidateTracker(tracker_params)

šŸ“¹ Video Sources

SimpleLPR handles both files and live streams transparently:

# Video file
video = engine.openVideoSource("traffic.mp4", ...)

# RTSP stream
video = engine.openVideoSource("rtsp://camera:554/stream", ...)

# Auto-reconnect for streams
if video.isLiveSource:
    video.reconnect()

šŸ—ļø Architecture

SimpleLPR employs a multi-stage processing pipeline:

  • šŸ–¼ļø Image Preprocessing: Noise reduction, contrast enhancement
  • šŸ” Plate Detection: Locate potential plate regions
  • šŸ“ Perspective Correction: Transform skewed plates to frontal view
  • āœ‚ļø Character Segmentation: Identify individual character boundaries
  • šŸ”¤ OCR Recognition: Classify characters using specialized neural networks
  • āœ… Format Validation: Match against country-specific templates
  • šŸ”„ Tracking: Correlate detections across frames for consistency

šŸ’” Common Use Cases

šŸ…æļø Parking Management

  • Entry/exit gate control
  • Space occupancy monitoring
  • Permit verification
  • Duration tracking

šŸ›£ļø Toll Collection

  • High-speed highway processing
  • Multi-lane free-flow systems
  • Violation enforcement

🚨 Security & Access Control

  • Gated community entry
  • Corporate campus security
  • VIP list verification
  • Visitor management

šŸ“Š Analytics & Smart City

  • Traffic flow analysis
  • Journey time calculation
  • Congestion monitoring
  • Environmental zone enforcement

šŸ“š Documentation

  • šŸ“– User Guide - Complete documentation
  • šŸ Python API Reference - Detailed API docs
  • šŸš€ Python Quick Start - Get started fast
  • šŸ’» GitHub Examples - More sample code

šŸ¤ Support

šŸ“„ License

SimpleLPR is commercial software with a 60-day evaluation period. Production licenses include:

  • āœ… One-time payment - no recurring fees
  • āœ… Unlimited deployments
  • āœ… Royalty-free redistribution
  • āœ… 1 year of updates and support

Contact support@warelogic.com for pricing.

šŸš— SimpleLPR - Professional license plate recognition for Python developers

Keywords

LPR

FAQs

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts