🚀 Big News: Socket Acquires Coana to Bring Reachability Analysis to Every Appsec Team.Learn more
Socket
DemoInstallSign in
Socket

SimpleLPR

Package Overview
Dependencies
Maintainers
3
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

SimpleLPR

SimpleLPR is a professional-grade license plate recognition (LPR/ANPR) library that provides automatic detection and reading of vehicle license plates in images and video streams. Developed over 10+ years and deployed in thousands of production systems worldwide, SimpleLPR delivers enterprise-level reliability with a simple, developer-friendly API. **Proven Performance** Achieves 85-95% recognition accuracy under typical conditions (plate text height ≥20 pixels, reasonable image quality). The specialized OCR engine is trained specifically for license plate fonts and formats, handling challenging real-world scenarios including varying lighting, camera angles up to ±30°, and vehicle motion. **Comprehensive Input Support** • Static images: JPEG, PNG, TIFF, BMP from files or memory buffers • Video files: AVI, MP4, MKV and other common formats • Live streams: RTSP, HTTP, and other network protocols • Configurable resolution limits for performance optimization **Advanced Features** • Multi-threaded processor pools enable concurrent analysis of multiple streams • Intelligent plate tracking correlates detections across video frames, reducing false positives • Plate region detection provides exact coordinates and confidence scores • Character-level analysis with individual confidence metrics • Contrast sensitivity adjustment for shadow and glare compensation • Automatic perspective correction for angled plate views • Support for approximately 90 countries with region-specific templates **Developer-Focused Design** SimpleLPR offers native APIs for C++, .NET (C#, VB.NET, F#), and Python, each following platform conventions. The library handles all complexity internally - developers simply provide an image and receive structured results containing the plate text, country, confidence scores, and location coordinates. **Video Processing Capabilities** The video API includes frame extraction, automatic buffering, stream reconnection for network sources, and synchronized result delivery. The plate tracker maintains temporal consistency across frames, eliminating transient misreads common in frame-by-frame processing. **System Requirements** • Windows 10/11 (x64) or Linux Ubuntu 20.04+ (x64) • .NET Standard 2.0 or higher for .NET integration • Python 3.8, 3.9, 3.10, 3.11, or 3.12 for Python integration • Optional: CUDA-capable GPU for performance boost (SDK version) **Licensing** 60-day evaluation included. Production license is one-time purchase with unlimited, royalty-free redistribution rights. Includes one year of technical support and updates. **Resources** • Project Site: https://www.warelogi.com • Documentation: https://www.warelogic.com/doc/SimpleLPR3.pdf • .NET API Reference: https://www.warelogic.com/doc/SimpleLPR.chm • Python API Reference: https://www.warelogic.com/doc/simplelpr_python_api_reference.htm • Python Quick Start: https://www.warelogic.com/doc/simplelpr_python_quickstart_guide.htm • Sample Code: https://github.com/xgirones/SimpleLPR-samples • Support: support@warelogic.com See the project README for complete documentation and code examples.

3.6.0
NuGet
Version published
Maintainers
3
Created
Source

SimpleLPR

SimpleLPR is a comprehensive software library designed for automatic license plate recognition (LPR/ANPR) in both static images and real-time video streams. Built on years of real-world deployment experience, SimpleLPR combines optical character recognition with preprocessing algorithms to deliver reliable plate detection and reading capabilities across a wide range of challenging conditions.

What is SimpleLPR?

SimpleLPR is more than an OCR library - it's a complete license plate recognition system that handles the workflow from image acquisition to final text output. The library automatically detects license plates in images or video frames, extracts the plate region, performs perspective correction, and reads the alphanumeric characters with high accuracy.

Core Capabilities

  • Automatic Plate Detection: Locates license plates in complex scenes without manual intervention
  • Robust Character Recognition: Specialized OCR engine trained specifically for license plate fonts and formats
  • Video Stream Processing: Real-time analysis of video files and live streams (RTSP, HTTP, etc.)
  • Multi-threaded Architecture: Process multiple images or video streams concurrently for maximum throughput
  • Temporal Tracking: Track plates across video frames to improve accuracy and reduce false readings
  • Global Coverage: Pre-configured templates for approximately 90 countries worldwide
  • Flexible Integration: Native APIs for C++, .NET, and Python with language-appropriate conventions

Real-World Performance

SimpleLPR achieves typical recognition rates of 85-95% under normal operating 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 from perpendicular

The library has been extensively tested in production environments including:

  • Highway toll collection systems processing thousands of vehicles per hour
  • Parking facilities with varying lighting conditions
  • Access control gates with fixed camera positions
  • Mobile enforcement units with handheld cameras
  • Traffic monitoring systems with wide-angle views

Key Features

Image Processing

  • Support for all major image formats (JPEG, PNG, TIFF, BMP)
  • Direct processing from memory buffers
  • Automatic contrast enhancement for poor lighting conditions
  • Perspective distortion correction
  • Multi-scale detection for plates at various distances

Video Processing

  • Frame extraction from video files (AVI, MP4, MKV, etc.)
  • Real-time RTSP stream analysis
  • Configurable frame size limits for performance optimization
  • Automatic reconnection for unreliable network streams
  • Frame buffering and synchronization

Advanced Capabilities

  • Processor Pools: Distribute work across multiple CPU cores for parallel processing
  • Plate Tracking: Correlate detections across frames to eliminate transient misreads
  • Confidence Scoring: Character-level and plate-level confidence metrics
  • Region Detection: Precise plate boundary extraction with quadrilateral coordinates
  • Country Validation: Verify plate formats against country-specific templates
  • GPU Acceleration: Optional CUDA support for enhanced performance (SDK version)

Getting Started

System Requirements

  • Operating Systems: Windows 10/11, Ubuntu 20.04+, other major Linux distributions
  • Architecture: x86-64 (both 32-bit and 64-bit builds available)
  • Memory: Minimum 2GB RAM, 4GB+ recommended for video processing
  • .NET Requirements: .NET Standard 2.0 or higher
  • Python: Version 3.8, 3.9, 3.10, 3.11, or 3.12 (64-bit)

Installation

SimpleLPR is distributed as a commercial SDK with a 60-day evaluation period. The SDK includes:

  • Native libraries for C++ integration
  • .NET assemblies with full IntelliSense support
  • Python wheel packages for pip installation
  • Comprehensive documentation and sample code
  • Demo applications with source code

Documentation

Quick Start Example

Here's a comprehensive example demonstrating video processing with concurrent analysis and plate tracking:

using System;
using System.Collections.Generic;
using System.IO;
using SimpleLPR3;

namespace SimpleLPR_VideoTracking
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                // Parse command line arguments
                if (args.Length < 3 || args.Length > 4)
                {
                    ShowUsage();
                    return;
                }

                string videoPath = args[0];
                uint countryId = uint.Parse(args[1]);
                string outputDir = args[2];
                string productKey = args.Length > 3 ? args[3] : null;

                // Validate inputs
                if (!File.Exists(videoPath))
                {
                    Console.WriteLine($"Error: Video file not found: {videoPath}");
                    return;
                }

                // Create output directory
                Directory.CreateDirectory(outputDir);
                Console.WriteLine($"Results will be saved to: {Path.GetFullPath(outputDir)}");

                // Process the video
                ProcessVideo(videoPath, countryId, outputDir, productKey);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"\nError: {ex.Message}");
                if (ex.InnerException != null)
                    Console.WriteLine($"Details: {ex.InnerException.Message}");
            }
        }

        static void ShowUsage()
        {
            Console.WriteLine("SimpleLPR Video Processing Demo");
            Console.WriteLine("\nThis demo processes a video file and tracks license plates across frames.");
            Console.WriteLine("\nUsage: SimpleLPR_VideoTracking <video_file> <country_id> <output_dir> [product_key]");
            Console.WriteLine("\nParameters:");
            Console.WriteLine("  video_file   : Path to video file (MP4, AVI, etc.) or RTSP stream URL");
            Console.WriteLine("  country_id   : Numeric country identifier (see list below)");
            Console.WriteLine("  output_dir   : Directory for results and plate thumbnails");
            Console.WriteLine("  product_key  : Optional path to license key file");
            Console.WriteLine("\nExample:");
            Console.WriteLine("  SimpleLPR_VideoTracking traffic.mp4 9 ./results");
            Console.WriteLine("  SimpleLPR_VideoTracking rtsp://camera.local:554/stream 52 ./detections key.xml");

            // Display supported countries
            Console.WriteLine("\nSupported Countries:");
            
            // Create temporary engine to list countries
            var setupParams = new EngineSetupParms
            {
                cudaDeviceId = -1,  // CPU mode
                enableImageProcessingWithGPU = false,
                enableClassificationWithGPU = false,
                maxConcurrentImageProcessingOps = 1
            };

            using (var tempEngine = SimpleLPR.Setup(setupParams))
            {
                for (uint i = 0; i < tempEngine.numSupportedCountries; i++)
                {
                    Console.WriteLine($"  {i,3}: {tempEngine.get_countryCode(i)}");
                }
            }
        }

        static void ProcessVideo(string videoPath, uint countryId, string outputDir, string productKey)
        {
            Console.WriteLine("\n=== SimpleLPR Video Processing Demo ===\n");

            // Initialize SimpleLPR engine
            // The engine is the main entry point for all SimpleLPR functionality
            var engineParams = new EngineSetupParms
            {
                cudaDeviceId = -1,                     // Use CPU (-1). Set 0+ for GPU if available
                enableImageProcessingWithGPU = false,   // GPU processing requires CUDA
                enableClassificationWithGPU = false,    // GPU classification requires CUDA
                maxConcurrentImageProcessingOps = 3     // Number of parallel processing threads
            };

            using var lpr = SimpleLPR.Setup(engineParams);

            // Display version information
            var ver = lpr.versionNumber;
            Console.WriteLine($"SimpleLPR Version: {ver.A}.{ver.B}.{ver.C}.{ver.D}");

            // Supply product key if provided
            // Without a valid key, SimpleLPR runs in evaluation mode (60 days)
            if (!string.IsNullOrEmpty(productKey))
            {
                lpr.set_productKey(productKey);
                Console.WriteLine("Product key loaded");
            }
            else
            {
                Console.WriteLine("Running in evaluation mode");
            }

            // Configure country recognition
            // SimpleLPR can detect plates from ~90 countries
            // For best results, enable only the countries you expect to encounter
            
            // First, disable all countries
            for (uint i = 0; i < lpr.numSupportedCountries; i++)
                lpr.set_countryWeight(i, 0.0f);

            // Enable only the selected country with full weight
            lpr.set_countryWeight(countryId, 1.0f);
            
            // Apply the country configuration
            lpr.realizeCountryWeights();

            string countryCode = lpr.get_countryCode(countryId);
            Console.WriteLine($"Country configured: {countryCode} (ID: {countryId})");

            // Create processor pool for concurrent frame processing
            // The pool manages multiple processor instances for parallel analysis
            using var pool = lpr.createProcessorPool((uint)engineParams.maxConcurrentImageProcessingOps);
            
            // Enable plate region detection
            // This improves accuracy by identifying the exact plate boundaries
            pool.plateRegionDetectionEnabled = true;

            // Create plate tracker
            // The tracker correlates plate detections across multiple frames
            // This reduces false positives and handles temporary occlusions
            var trackerParams = PlateCandidateTrackerSetupParms.Default;
            
            Console.WriteLine("\nTracker configuration:");
            Console.WriteLine($"  Trigger window: {trackerParams.triggerWindowInSec} seconds");
            Console.WriteLine($"  Max idle time: {trackerParams.maxIdleTimeInSec} seconds");
            Console.WriteLine($"  Min detections: {trackerParams.minTriggerFrameCount} frames");
            
            using var tracker = lpr.createPlateCandidateTracker(trackerParams);

            // Open video source
            // SimpleLPR supports video files and live streams (RTSP, HTTP, etc.)
            // Frame size caps help manage memory usage and processing time
            const int maxWidth = 1920;   // Full HD width
            const int maxHeight = 1080;  // Full HD height
            
            Console.WriteLine($"\nOpening video source: {videoPath}");
            using var video = lpr.openVideoSource(videoPath, 
                FrameFormat.FRAME_FORMAT_BGR24, maxWidth, maxHeight);

            // Check if source opened successfully
            if (video.state != VideoSourceState.VIDEO_SOURCE_STATE_OPEN)
            {
                throw new Exception($"Failed to open video source. State: {video.state}");
            }

            Console.WriteLine($"Video type: {(video.isLiveSource ? "Live stream" : "File")}");
            Console.WriteLine("\nProcessing frames... Press Ctrl+C to stop.\n");

            // Frame queue to synchronize frames with their processing results
            // This is necessary because processing is asynchronous
            var frameQueue = new Queue<IVideoFrame>();
            
            // Statistics
            int frameCount = 0;
            int trackCount = 0;
            var startTime = DateTime.Now;

            // Main processing loop
            IVideoFrame frame;
            while ((frame = video.nextFrame()) != null)
            {
                frameCount++;
                
                // Keep frame reference until its result is ready
                frameQueue.Enqueue(frame);

                // Submit frame for asynchronous processing
                // The pool will use the next available processor
                pool.launchAnalyze(
                    streamId: 0,                              // Stream identifier (for multi-stream scenarios)
                    requestId: frame.sequenceNumber,          // Unique ID to match results with frames
                    timeoutInMs: IProcessorPoolConstants.TIMEOUT_INFINITE,  // Wait for available processor
                    frame: frame
                );

                // Check for completed results
                // This is non-blocking, so we can continue processing new frames
                ProcessCompletedResults(pool, tracker, frameQueue, outputDir, ref trackCount);

                // Show progress every 100 frames
                if (frameCount % 100 == 0)
                {
                    var elapsed = DateTime.Now - startTime;
                    var fps = frameCount / elapsed.TotalSeconds;
                    Console.WriteLine($"Processed {frameCount} frames ({fps:F1} fps) - {trackCount} plates tracked");
                }
            }

            Console.WriteLine("\nVideo processing complete. Waiting for final results...");

            // Process any remaining results in the pipeline
            while (pool.get_ongoingRequestCount(0) > 0)
            {
                // TIMEOUT_INFINITE means this will block until a result is available
                var result = pool.pollNextResult(0, IProcessorPoolConstants.TIMEOUT_INFINITE);
                ProcessResult(result, tracker, frameQueue, outputDir, ref trackCount);
            }

            // Flush the tracker to close any remaining active tracks
            // This ensures we don't miss plates that were still being tracked
            using var flushResult = tracker.flush();
            ProcessTrackerResult(flushResult, outputDir, ref trackCount, -1.0);

            // Clean up any remaining frames
            while (frameQueue.Count > 0)
                frameQueue.Dequeue().Dispose();

            // Final statistics
            var totalTime = DateTime.Now - startTime;
            Console.WriteLine("\n=== Processing Complete ===");
            Console.WriteLine($"Total frames: {frameCount}");
            Console.WriteLine($"Processing time: {totalTime.TotalSeconds:F1} seconds");
            Console.WriteLine($"Average FPS: {frameCount / totalTime.TotalSeconds:F1}");
            Console.WriteLine($"License plates tracked: {trackCount}");
            Console.WriteLine($"Results saved to: {Path.GetFullPath(outputDir)}");
        }

        static void ProcessCompletedResults(IProcessorPool pool, IPlateCandidateTracker tracker,
            Queue<IVideoFrame> frameQueue, string outputDir, ref int trackCount)
        {
            // Poll for all available results without blocking
            IProcessorPoolResult result;
            while ((result = pool.pollNextResult(0, IProcessorPoolConstants.TIMEOUT_IMMEDIATE)) != null)
            {
                ProcessResult(result, tracker, frameQueue, outputDir, ref trackCount);
            }
        }

        static void ProcessResult(IProcessorPoolResult result, IPlateCandidateTracker tracker,
            Queue<IVideoFrame> frameQueue, string outputDir, ref int trackCount)
        {
            using (result)
            {
                // Match the result with its corresponding frame
                var frame = frameQueue.Dequeue();
                using (frame)
                {
                    // Let the tracker process the detection results
                    // Even frames without detections are important for tracking timing
                    using var trackerResult = tracker.processFrameCandidates(
                        result.candidates, frame);

                    // Process the tracking results
                    ProcessTrackerResult(trackerResult, outputDir, ref trackCount, 
                        frame.timestamp);
                }
            }
        }

        static void ProcessTrackerResult(IPlateCandidateTrackerResult trackerResult, 
            string outputDir, ref int trackCount, double timestamp)
        {
            // Process newly created tracks
            foreach (var track in trackerResult.NewTracks)
            {
                trackCount++;
                
                var candidate = track.representativeCandidate;
                if (candidate.matches.Count > 0)
                {
                    var match = candidate.matches[0];
                    var timeStr = timestamp >= 0 ? $"{timestamp:F2}s" : "final";
                    
                    Console.WriteLine($"[NEW PLATE] Frame {track.firstDetectionFrameId} @ {timeStr}: " +
                                    $"{match.text} ({match.country}) - Confidence: {match.confidence:F3}");

                    // Save thumbnail if available
                    if (track.representativeThumbnail != null)
                    {
                        string filename = $"plate_{trackCount:D4}_{match.text}.jpg";
                        string filepath = Path.Combine(outputDir, filename);
                        
                        try
                        {
                            track.representativeThumbnail.saveAsJPEG(filepath, 95);
                            Console.WriteLine($" -> Saved thumbnail: {filename}");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine($"  → Failed to save thumbnail: {ex.Message}");
                        }
                    }
                }
            }

            // Process closed tracks (plates that are no longer visible)
            foreach (var track in trackerResult.ClosedTracks)
            {
                var candidate = track.representativeCandidate;
                if (candidate.matches.Count > 0)
                {
                    var match = candidate.matches[0];
                    var duration = track.newestDetectionTimestamp - track.firstDetectionTimestamp;
                    var frameSpan = track.newestDetectionFrameId - track.firstDetectionFrameId + 1;
                    
                    Console.WriteLine($"[TRACK CLOSED] {match.text} - " +
                                    $"Duration: {duration:F1}s ({frameSpan} frames)");
                }
            }
        }
    }
}

Architecture Considerations

Threading Model

SimpleLPR is designed for multi-threaded operation:

  • The engine instance is thread-safe and can be shared
  • Individual processors are NOT thread-safe (use one per thread)
  • Processor pools handle thread management automatically
  • Video sources should be accessed from a single thread

Memory Management

  • All SimpleLPR objects implement IDisposable - use using statements
  • Video frames should be disposed after processing
  • Processor pools manage their own memory efficiently
  • Consider frame size caps for high-resolution video

Performance Optimization

  • Country Selection: Enable only expected countries for faster processing
  • Processor Count: Set based on CPU cores (typically cores - 1)
  • Frame Size: Cap at the minimum resolution that maintains accuracy
  • GPU Acceleration: Use CUDA-enabled builds for 2-3x speedup
  • Plate Tracking: Reduces false positives without significant overhead

Common Use Cases

Parking Management

  • Entry/exit gate control with fixed cameras
  • Space occupancy monitoring
  • Permit verification
  • Duration tracking for billing

Toll Collection

  • High-speed highway toll processing
  • Multi-lane free-flow systems
  • Violation enforcement
  • Cross-plaza journey time analysis

Access Control

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

Law Enforcement

  • Stolen vehicle alerts
  • Warrant plate matching
  • Traffic violation recording
  • Investigative plate searches

Smart City Applications

  • Traffic flow analysis
  • Congestion pricing
  • Parking guidance systems
  • Environmental zone enforcement

Support and Resources

Technical Support

  • Email: support@warelogic.com
  • Include version information and error logs
  • Provide sample images/videos when possible

Licensing

  • 60-day evaluation period included
  • One-time license purchase - no recurring fees
  • Unlimited, royalty-free redistribution rights
  • Include SimpleLPR runtime with your applications
  • One year of technical support and updates included
  • Contact support@warelogic.com for pricing

Updates

SimpleLPR is actively maintained with regular updates:

  • New country template additions
  • OCR accuracy improvements
  • Performance optimizations
  • Bug fixes and stability enhancements

SimpleLPR has been helping developers implement reliable license plate recognition since 2009.

Keywords

LPR

FAQs

Package last updated on 09 Jun 2025

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