
Research
Security News
The Growing Risk of Malicious Browser Extensions
Socket researchers uncover how browser extensions in trusted stores are used to hijack sessions, redirect traffic, and manipulate user behavior.
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.
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.
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.
SimpleLPR achieves typical recognition rates of 85-95% under normal operating conditions:
The library has been extensively tested in production environments including:
SimpleLPR is distributed as a commercial SDK with a 60-day evaluation period. The SDK includes:
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)");
}
}
}
}
}
SimpleLPR is designed for multi-threaded operation:
using
statementsSimpleLPR is actively maintained with regular updates:
SimpleLPR has been helping developers implement reliable license plate recognition since 2009.
FAQs
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.
We found that simplelpr demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
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.
Research
Security News
Socket researchers uncover how browser extensions in trusted stores are used to hijack sessions, redirect traffic, and manipulate user behavior.
Research
Security News
An in-depth analysis of credential stealers, crypto drainers, cryptojackers, and clipboard hijackers abusing open source package registries to compromise Web3 development environments.
Security News
pnpm 10.12.1 introduces a global virtual store for faster installs and new options for managing dependencies with version catalogs.