SimpleLPR 3.6.0
dotnet add package SimpleLPR --version 3.6.0
NuGet\Install-Package SimpleLPR -Version 3.6.0
<PackageReference Include="SimpleLPR" Version="3.6.0" />
<PackageVersion Include="SimpleLPR" Version="3.6.0" />
<PackageReference Include="SimpleLPR" />
paket add SimpleLPR --version 3.6.0
#r "nuget: SimpleLPR, 3.6.0"
#addin nuget:?package=SimpleLPR&version=3.6.0
#tool nuget:?package=SimpleLPR&version=3.6.0
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
- User Guide: 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
- Example Projects: https://github.com/xgirones/SimpleLPR-samples
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.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version 3.6.0 - Major Release
• IPlateCandidateTracker class for tracking license plates across multiple video frames (now stable)
• Added comprehensive samples in C#, Python, and C++ demonstrating plate tracking functionality
• New interactive WPF sample application with real-time display of captured plates
• Updated Russia template to support three-digit region codes (1xx, 2xx, 3xx, 5xx, 6xx, 7xx, 9xx)
• Enhanced Azerbaijan template with diplomatic, motorcycle, and newer format support
• Various performance improvements and bug fixes