Automatic Number Plate Recognition and Vehicle Make & Model recognition for Windows
applications, with native C/C++, C# and Delphi bindings, still-image and live-video pipelines,
and support for CPU, GPU and edge AI accelerators.
Windows x64 SDKC/C++ · C# · DelphiDocumentation v1.0 · July 2026
1Overview
The SDK provides two neural-network engines behind one simple C API exported by nnAnpr.dll:
ANPR — plate finding & reading
Detects number plates in an image (plate finder) and reads the characters (plate recognizer), returning the plate text, per-character confidences and coordinates, country and region, plate type and colour.
VMM — vehicle make & model
Detects vehicles (vehicle finder: car, bus, truck) and classifies the make and model (vehicle model recognizer) with a confidence score.
Video pipeline
Frame capture from video files, IP streams (RTSP/HTTP) and supported industrial cameras (GigE/USB3 vision), with synchronous or callback-driven asynchronous processing and a recognition accumulator for multi-frame voting.
Processing devices
One NNANPR_Device parameter selects the backend: Intel CPU, Intel GPU, Myriad VPU, NVIDIA GPU, Hailo accelerator, or the vendor cloud service.
Five geographic dataset families are included — Europe, Middle East, Africa, Asia & Australasia, USA & South America — selected per engine instance with the NNANPR_GeographicalArea parameter.
2Package contents
Folder
Contents
bin\
The runtime: nnAnpr.dll plus all dependencies (OpenVINO, CUDA/cuDNN, Hailo runtime, camera-vendor runtimes), the data\ folder with encrypted neural-network datasets, ready-to-run demo executables, License Manager.exe, and testdata\ sample images.
Include\
Everything an integration needs: C headers nnAnpr.h / nnVmm.h, the import library nnAnpr.lib, C# bindings nnAnpr.cs / nnVmm.cs, Delphi bindings nnAnpr.pas / nnVmm.pas.
Examples\
Sample projects: C++ console and MFC GUI samples, C# WinForms samples, Delphi samples (Delphi 10.3 and Delphi 11 project versions). See section 12.
Datasets\full\
Complete master/backup copy of all datasets. Nothing is loaded from here at runtime — the engine reads only bin\data (the data\ folder next to nnAnpr.dll). Use it to restore or complete a bin\data deployment.
Doc\
This documentation.
3Requirements & setup
System requirements
OS: Windows 10 / Windows 11 / Windows Server, 64-bit only. All SDK binaries and bindings are x64; build your application as x64.
Runtime: Microsoft Visual C++ x64 redistributable (2015–2022).
CPU path: works on any modern x64 CPU via the bundled OpenVINO runtime — no extra installation.
NVIDIA GPU path (nndNvidiaGPU): a CUDA-capable NVIDIA GPU with a current driver. The required CUDA 11/12 and cuDNN 8/9 runtime DLLs are already included in bin\.
Intel GPU / Myriad (nndIntelGPU, nndMyriad): supported through the bundled OpenVINO runtime with the corresponding Intel drivers.
Hailo (nndHailo): a Hailo-8 accelerator with the Hailo driver; the libhailort runtime is included.
Industrial cameras: runtimes for supported GigE/USB3 vision cameras (Basler pylon, LUCID Arena, Daheng Galaxy) are included in bin\.
Cloud edition (nndIntertraffCloud): outbound internet access and an API key (section 4).
Setup
1
Copy the SDK folder
Copy the SDK to a writable location, e.g. C:\NNANPR-SDK\. Keep bin\ intact — nnAnpr.dll, its dependent DLLs and the data\ dataset folder must stay together.
2
Verify with a demo
Run bin\PlateRecognize.exe from a console: PlateRecognize.exe demo "testdata\EU2.jpg" (first argument is the API key — any text for local editions). A result window with the recognized plate confirms the runtime and datasets are working.
3
Make the DLLs reachable from your app
Your executable must be able to load nnAnpr.dll and its dependencies. Either run your EXE from bin\, copy the bin\ contents next to your EXE, or add the SDK bin\ folder to PATH. The data\ folder must sit next to nnAnpr.dll.
4Licensing & activation
The SDK is available in local (on-device) and cloud editions:
Local edition — processing runs on your machine. Licensing is file-based: use bin\License Manager.exe to view the machine identity and activate the license on the target computer. Error -18 NNANPR_LICENSE_ERROR indicates a missing or invalid license.
Trial builds — may require internet access for validation; without it the engine returns -17 NNANPR_TRIAL_VERSION_NO_INTERNET.
Cloud edition — recognition is performed by the vendor cloud (device nndIntertraffCloud). The cloud device applies only to the plate recognizer and the vehicle-model recognizer; the plate finder and vehicle finder always run locally (Intel CPU/GPU, Myriad, NVIDIA GPU or Hailo). Set your API key once at startup, before creating recognizers:
You can detect a cloud-only build at runtime with NNANPR_IsPlateRecognizerCloudVersion(). Server-side errors are reported as codes -11…-16, -19, -20 (section 15).
Note
Contact your supplier for license purchase, trial keys and cloud quotas. [Vendor: insert contact details, license terms and quota table here.]
5Quick start (C++)
The complete still-image workflow — initialize once, create an engine pair, find plates, read each one, free everything:
Still-image recognition · C++
#include "nnAnpr.h"
#include <iostream>
#pragma comment(lib, "nnAnpr.lib") // from Include\
int wmain()
{
if (NNANPR_Init() != NNANPR_RESULT_SUCCESS) // 1. initialize the engine once
return -1;
// NNANPR_SetStringParameter(L"APIKEY", L"..."); // cloud edition only
NNANPR_Image_ptr image = nullptr; // 2. load the input image
NNANPR_CreateImage(&image);
if (NNANPR_LoadImage(L"testdata\\EU2.jpg", image) != NNANPR_RESULT_SUCCESS)
return -2;
NNANPR_PlateFinder_ptr finder = nullptr; // 3. create finder + recognizer
NNANPR_PlateRecognizer_ptr recognizer = nullptr; // for your region and device
NNANPR_CreatePlateFinder (nngaEurope, nndIntelCPU, &finder);
NNANPR_CreatePlateRecognizer(nngaEurope, nndIntelCPU, &recognizer);
NNANPR_Object plates[30]; // 4. find plates
size_t count = 30; // in: capacity, out: found
if (NNANPR_FindPlates(finder, image, plates, &count) == NNANPR_RESULT_SUCCESS)
{
for (size_t i = 0; i < count; ++i) // 5. read each found plate
{
NNANPR_RecognizerResult rr{};
if (NNANPR_RecognizePlate(recognizer, image, plates[i], &rr) == NNANPR_RESULT_SUCCESS)
std::wcout << rr.Characters << L" country: " << rr.Country
<< L" confidence: " << rr.CharacterConfidences[0] << L"\n";
}
}
NNANPR_FreePlateRecognizer(recognizer); // 6. release in reverse order
NNANPR_FreePlateFinder(finder);
NNANPR_FreeImage(image);
return 0;
}
Build settings
Platform x64 · add Include\ to the include path · link Include\nnAnpr.lib · run the EXE from the SDK bin\ folder (or copy the bin\ contents beside your EXE) so nnAnpr.dll and data\ are found.
To recognize a plate on an already-cropped plate image, pass 0 as the plate object. The same quick start in C# and Delphi is shown in section 11. The complete runnable project for this example is Examples\c++\PlateRecognize\ (see section 12 for every sample's folder).
6Core concepts
Handles and objects
Every engine entity — plate finder, plate recognizer, image, capture, window, accumulator, vehicle finder, vehicle model recognizer — is an opaque handle created by a NNANPR_Create… function and released by its matching NNANPR_Free… function. A found plate or vehicle is returned as a lightweight NNANPR_Object value; pass it to NNANPR_GetPlate / NNANPR_GetVehicle to read its position and confidence, or hand it directly to the recognizer.
Always pair Create/Free correctly
Each handle type has its own free function. In particular, vehicle finder and vehicle model recognizer handles must be released with NNANPR_FreeVehicleFinder / NNANPR_FreeVehicleModelRecognizer — never with the plate free functions. Handles are untyped pointers, so the compiler cannot catch a mismatch.
The recognition pipeline
Processing is a two-stage pipeline. Stage one (finder) locates plates or vehicles in the frame; stage two (recognizer) reads one found object. This split lets you decide per frame which plates to read, run the stages on different devices, and scale them independently.
The two-stage pipeline
image ──► PlateFinder ──► NNANPR_Object[] ──► PlateRecognizer ──► NNANPR_RecognizerResult
(per object) text · confidences · country · type
Synchronous vs asynchronous
Every processing step exists in two forms. The synchronous form (FindPlates, RecognizePlate…) blocks until the result is ready — simplest for still images and batch jobs. The asynchronous form (FindPlatesAsync, RecognizePlateAsync…) queues the frame on an internal worker thread and returns immediately; the result arrives later through your callback. If a new frame arrives while the worker is busy, the older queued frame is skipped and the engine processes the latest one — live video never falls behind. Increase this queue with SetPlateFinderQueueSize / SetPlateRecognizerQueueSize (1–10, default 1) if you prefer throughput over latency.
Callback rules
The five threading rules
1. Callbacks are invoked on SDK worker threads, never on your UI thread — marshal results to the UI (PostMessage / Control.Invoke / TThread.Synchronize) instead of touching controls directly.
2. Keep callbacks short; heavy work in a callback stalls the pipeline.
3. Never free a handle that an asynchronous pipeline may still be using. Stop capture (NNANPR_StopCaptureFrameAsync) and let in-flight callbacks finish before calling any Free… — including when the user changes settings and you re-create engines.
4. In .NET, store every callback delegate in a field for the lifetime of the engine object. If only the garbage collector holds it, the native engine will eventually call a collected delegate and crash the process.
5. Do not call Free… functions from inside a callback.
Multi-frame accumulation
On live video the same plate is seen many times. The recognition accumulator merges consecutive reads of the same plate and reports the best-voted result, dramatically reducing single-frame misreads. Create one with NNANPR_CreateRecognitionAccumulator, configure it with NNANPR_SetupRecognitionAccumulator (maximum accumulation time and frame count, minimum plate size, and the character-difference threshold used to decide that two reads are the same plate), then replace RecognizePlateAsync with NNANPR_RecognizePlateAsyncWithAccumulator. Your recognition callback then receives consolidated results.
Return codes
Every function returns an NNANPR_RESULT: 1 success, 0 a valid "nothing found / cannot recognize" outcome, negative values are errors (full table). Convert any code to text with NNANPR_GetErrorString; for engine-level diagnostics read NNANPR_GetLastFindPlatesMessage / NNANPR_GetLastPlateRecognizerMessage.
7Video & cameras
Video files and IP streams
NNANPR_CreateVideoCapture opens a video file path or a stream URL (e.g. rtsp://user:pass@host/stream). Then either pull frames yourself with NNANPR_CaptureFrame in a loop, or let the SDK push them to you with NNANPR_StartCaptureFrameAsync and an OnFrame callback. Always call NNANPR_StopCaptureFrameAsync before freeing the capture.
Frame pacingNNANPR_GetVideoFPS reports the stream FPS when the container provides it. Live cameras and some RTSP streams report no FPS — check the return code and fall back to a default (e.g. 25) before dividing by it in your frame-timing code.
The typical live pipeline
Live pipeline overview
StartCaptureFrameAsync ─► OnFrame: draw frame; FindPlatesAsync(frame)
OnPlateFound: for the best plate → RecognizePlateAsyncWithAccumulator(...)
OnRecognition: post result to UI thread (plate text, country, image)
By default the plate-found callback fires only when at least one plate was detected. To receive a callback for every processed frame (useful for smooth overlay drawing), enable:
Callback on every frame
NNANPR_SetUIntParameter(L"CALL PF ASYNC CALLBACK EVEN NO RESULT", 1);
and check the result count inside your callback before using the array.
Industrial cameras (GigE / USB3 Vision)
Enumerate supported connected cameras with NNANPR_GetSpecialVideoCaptureList and open one by name with NNANPR_CreateSpecialVideoCapture. Camera features (exposure, gain, trigger mode…) are set and read with NNANPR_SetVideoCaptureParameterUInt / NNANPR_GetVideoCaptureParameterUInt / NNANPR_SetVideoCaptureParameterString; parameter names follow the camera vendor's feature naming. For cameras in software-trigger mode, request the next frame with NNANPR_PulseSoftwareTrigger. Runtimes for Basler (pylon), LUCID (Arena) and Daheng (Galaxy) cameras ship in bin\.
8Vehicle make & model (VMM)
The VMM API (nnVmm.h) mirrors the ANPR pattern without the geographic-area parameter:
Frames buffered per pipeline. 1 = lowest latency (skip old frames); higher = higher throughput.
Vehicle finder threshold
NNANPR_SetupVehicleFinder
0.0–1.0, default 0.5
Minimum vehicle-detection confidence.
VMM threshold
NNANPR_SetupVehicleModelRecognizer
0.0–1.0
Minimum make/model classification confidence.
Specific engine variants
Several finder/recognizer variants may be installed per region (optimised for different scenes or accelerators). List them with NNANPR_GetPlateFinderSpecificNames / NNANPR_GetPlateRecognizerSpecificNames — the buffer returns one name per line — and pass a name as the last argument of the corresponding Create… call, or nullptr for the default. Query the active variant with NNANPR_GetPlateFinderInfo / NNANPR_GetPlateRecognizerInfo.
Global engine parameters
Parameter
Set with
Values
Purpose
APIKEY
NNANPR_SetStringParameter
key string
Cloud-edition authentication. Set once at startup.
CALL PF ASYNC CALLBACK EVEN NO RESULT
NNANPR_SetUIntParameter
0 / 1
1 = plate/vehicle-found callback fires for every processed frame, even with zero results.
ENABLE LOG FILES
NNANPR_SetUIntParameter
0 / 1
Writes engine log files for support diagnostics.
Syntax templates — fixing confusable characters
Characters like O/0, I/1, Z/2, B/8, S/5 are visually ambiguous. A syntax template tells the recognizer the legal character pattern of a country's plates so ambiguous characters resolve to the right class. Load one with NNANPR_SetPlateRecognizerSyntaxTemplate — pass either the file content or a file path. Template format (see bin\data\UK.syntax):
UK.syntax
d=0123456789 ← define character class "d" (digits)
l=ABCDEFGHIJKLMNOPQRSTUVWXYZ ← define character class "l" (letters)
template=llddlll ← a legal plate pattern: 2 letters, 2 digits, 3 letters
template=ldddlll ← list every legal pattern on its own line
10Datasets & regions
The engine loads datasets only from the data\ folder next to nnAnpr.dll (that is, bin\data). Datasets\full\ is a complete master/backup copy of every model — nothing is loaded from it at runtime; use it only to restore or complete a bin\data deployment. Datasets are encrypted — do not rename, edit or substitute files. File families:
Files
Purpose
ww_pf1 … ww_pf4
Worldwide plate finder models (internal files behind the selectable finder variants).
eu_pr1, eu_pr2
Plate recognizer models — Europe (nngaEurope).
me_pr1, me_pr2
Plate recognizer models — Middle East (nngaMiddleEast).
us_pr1, us_pr2
Plate recognizer models — USA & South America (nngaUSA_SouthAmerica).
aa_pr1
Plate recognizer — Asia & Australasia (nngaAsia_Australasia).
af_pr1
Plate recognizer — Africa (nngaAfrica).
vmm, vmm8, vmm8_4, vmm8_cpu, vmmv4
Vehicle make & model models (per device variants).
These are internal, encrypted file names — you never pass them to the API. A build's region is chosen with the NNANPR_GeographicalArea enum. To pick a specific finder or recognizer variant, call NNANPR_GetPlateFinderSpecificNames / NNANPR_GetPlateRecognizerSpecificNames to obtain the friendly display names the SDK exposes (for example "ME OV V7") and pass one as the pSpecificName argument to NNANPR_CreatePlateFinder / NNANPR_CreatePlateRecognizer (null = default). An unknown name returns -8 NNANPR_INCORRECT_SPECIFIC_NAME.
Each model ships in several runtime formats and the engine picks the one matching your selected device: .xml/.bin (OpenVINO — Intel CPU/GPU/Myriad), .hef (Hailo), .cfg/.weights/.classes (NVIDIA GPU builds), with .json metadata. Missing dataset files for the chosen region/device produce error -10 NNANPR_DATASET_IS_NOT_INSTALLED.
11Language bindings
C / C++
Include Include\nnAnpr.h (and nnVmm.h for VMM), link Include\nnAnpr.lib, build x64. Works from console, Win32, MFC and service applications — see the quick start.
C# (.NET)
Add Include\nnAnpr.cs (and nnVmm.cs) to your project. The bindings are P/Invoke declarations over nnAnpr.dll; no COM registration is needed.
Set the project platform to x64 (not AnyCPU) — the native DLL is 64-bit.
Make sure nnAnpr.dll and its bin\ dependencies are next to your EXE or on PATH.
Root your callback delegates. Store each delegate in a field that lives as long as the pipeline; passing a method group or lambda directly leaves the delegate to the garbage collector, and the native engine will eventually call a collected delegate and terminate the process.
Rooting callback delegates · C#
public sealed class AnprEngine
{
// Delegates kept in fields: the native engine calls them from its worker
// threads for as long as the pipeline exists. If only the garbage collector
// held them, a collected delegate would crash the process.
private readonly NNANPROnPlateFoundCallback _onPlates;
private readonly NNANPROnRecognitionCallback _onRead;
private IntPtr _finder, _recognizer; // handles are IntPtr, not ulong
public AnprEngine()
{
_onPlates = OnPlatesFound; // field assignment = GC-rooted
_onRead = OnPlateRead;
nnAnpr.NNANPR_Init();
nnAnpr.NNANPR_CreatePlateFinder(NNANPR_GeographicalArea.nngaEurope,
NNANPR_Device.nndIntelCPU, ref _finder);
nnAnpr.NNANPR_CreatePlateRecognizer(NNANPR_GeographicalArea.nngaEurope,
NNANPR_Device.nndIntelCPU, ref _recognizer);
}
public void ProcessFrame(IntPtr image) =>
nnAnpr.NNANPR_FindPlatesAsync(_finder, image, _onPlates, IntPtr.Zero);
private void OnPlatesFound(ulong[] plates, ulong count, IntPtr image, IntPtr ctx)
{
if (count > 0)
nnAnpr.NNANPR_RecognizePlateAsync(_recognizer, image, plates[0], _onRead, IntPtr.Zero);
}
private void OnPlateRead(ref NNANPR_RecognizerResult rr, IntPtr image, IntPtr ctx)
{
string plate = new string(rr.Characters).TrimEnd('\0');
// marshal to the UI thread before touching controls:
// form.BeginInvoke((Action)(() => label.Text = plate));
}
}
Delphi
Add Include\nnAnpr.pas (and nnVmm.pas) to your project. The bindings target Win64 only — keep the project platform on Win64; do not add a Win32 target. Callbacks arrive on engine threads: hand results to the VCL thread with TThread.Synchronize/TThread.Queue or PostMessage, exactly as the shipped Delphi samples do. Sample projects are provided for Delphi 12.
12Sample projects
All samples live in Examples\; ready-built demos are in bin\. Build samples as x64 and run them from bin\ (or copy the bin\ contents beside the built EXE).
Sample
Language
Shows
Run
PlateRecognize
C++ console
Still-image recognition, synchronous pipeline, result drawing.
PlateRecognize.exe <apikey> <image>
VehicleModelRecognize
C++ console
Vehicle make & model on a still image; optional vehicle detection first.
The same three scenarios in Delphi/VCL, including correct thread marshaling.
GUI / console
Test images for all regions are provided in bin\testdata\.
Where each project lives
Every sample sits under Examples\ in a subfolder that matches its Sample name above — C / C++ in Examples\c++\, C# in Examples\c#\, Delphi in Examples\Delphi 12\. Per scenario:
• Plate from image:Examples\c++\PlateRecognize\ · Examples\c#\VideoSampleCS\ (Open-image button) · Examples\Delphi 12\VideoSample\ • Live ANPR on video / RTSP:Examples\c++\VideoAsync\ · Examples\c#\VideoAsyncCS\ · Examples\Delphi 12\VideoAsync\ • Vehicle make & model:Examples\c++\VehicleModelRecognize\ (and live VideoAsyncVMM\) · Examples\c#\VehicleModelRecognizerCS\ · Examples\Delphi 12\VehicleModelRecognizer\
13Deployment checklist
Build your application x64.
Ship the contents of bin\ (in particular nnAnpr.dll, all dependent DLLs and the data\ folder) next to your executable, or install the SDK bin\ to a fixed path and add it to PATH. data\ must stay next to nnAnpr.dll.
Install the Microsoft Visual C++ x64 redistributable on the target machine.
Device drivers where used: NVIDIA driver for nndNvidiaGPU, Intel GPU driver for nndIntelGPU, Hailo driver for nndHailo, camera vendor drivers for GigE/USB3 cameras.
Activate the license on the target with License Manager.exe (local editions), or configure the APIKEY (cloud edition).
Verify on the target by running a bin\ demo against testdata\ before starting your application.
For support cases, enable engine logs: NNANPR_SetUIntParameter(L"ENABLE LOG FILES", 1).
14API reference
All functions return NNANPR_RESULT (1 success, 0 valid "no result", negative = error). Strings are wide (wchar_t* / UTF-16). The tables list the parameters only.
Engine & global
Function
Parameters
Description
NNANPR_Init
(void)
Initializes the engine. Call once, before any other SDK call.
Creates a plate finder for a region on a local device (not nndIntertraffCloud); optional specific variant name from NNANPR_GetPlateFinderSpecificNames.
nndIntelCPU 0 · nndIntelGPU 1 · nndMyriad 2 · nndIntertraffCloud 3 · nndNvidiaGPU 4 · nndHailo 5. nndIntertraffCloud is valid only for the plate and vehicle-model recognizers; finders use a local device.
First call loads and initializes datasets on the device — warm up at startup with one dummy image.
FAQ
Local or cloud?
Local editions process on-device (license file); the cloud edition sends images to the vendor service (nndIntertraffCloud + APIKEY). NNANPR_IsPlateRecognizerCloudVersion() tells you which build you have.
Several cameras?
Create one capture per stream. You can share one finder/recognizer (frames queue), or create one pair per stream for full parallelism — see the Multicameras sample.
32-bit application?
Not supported — the SDK is 64-bit only.
Which regions are covered?
Europe, Middle East, Africa, Asia & Australasia, USA & South America — one recognizer per NNANPR_GeographicalArea.
Data protection
In local editions images never leave the machine. In cloud mode frames are transmitted to the recognition service — review your data-protection obligations (e.g. GDPR) for your deployment.
17Code samples
Complete, copy-paste programs for the most common scenarios. Each is self-contained and built exactly against the shipped bindings. Build x64 and run from the SDK bin\ folder (or with bin\ on PATH). The C++ still-image quick start is in section 5. Every snippet below also ships as a full, runnable project — see section 12 for the exact Examples\ folder of each language and scenario.
C# — still-image recognition (console)
The C# equivalent of the quick start. Add Include\nnAnpr.cs to the project and build x64.
Still-image recognition · C#
using System;
class Program
{
static int Main()
{
if (nnAnpr.NNANPR_Init() != nnAnpr.NNANPR_RESULT_SUCCESS) // 1. initialize once
return -1;
// nnAnpr.NNANPR_SetStringParameter("APIKEY", "..."); // cloud edition only
IntPtr image = IntPtr.Zero; // 2. load the input image
nnAnpr.NNANPR_CreateImage(ref image);
if (nnAnpr.NNANPR_LoadImage(@"testdata\EU2.jpg", image) != nnAnpr.NNANPR_RESULT_SUCCESS)
return -2;
IntPtr finder = IntPtr.Zero, recognizer = IntPtr.Zero; // 3. create finder + recognizer
nnAnpr.NNANPR_CreatePlateFinder(NNANPR_GeographicalArea.nngaEurope,
NNANPR_Device.nndIntelCPU, ref finder);
nnAnpr.NNANPR_CreatePlateRecognizer(NNANPR_GeographicalArea.nngaEurope,
NNANPR_Device.nndIntelCPU, ref recognizer);
ulong[] plates = new ulong[30]; // 4. find plates
ulong count = (ulong)plates.Length; // in: capacity, out: found
if (nnAnpr.NNANPR_FindPlates(finder, image, ref plates[0], ref count) == nnAnpr.NNANPR_RESULT_SUCCESS)
{
for (ulong i = 0; i < count; i++) // 5. read each found plate
{
var rr = new NNANPR_RecognizerResult();
if (nnAnpr.NNANPR_RecognizePlate(recognizer, image, plates[i], ref rr) == nnAnpr.NNANPR_RESULT_SUCCESS)
Console.WriteLine(PlateText(rr.Characters) + " country: " + PlateText(rr.Country));
}
}
nnAnpr.NNANPR_FreePlateRecognizer(recognizer); // 6. release in reverse order
nnAnpr.NNANPR_FreePlateFinder(finder);
nnAnpr.NNANPR_FreeImage(image);
return 0;
}
// The result fields are fixed 50-char buffers; trim at the first NUL.
static string PlateText(char[] a) => new string(a).TrimEnd('\0');
}
Binding note
Handles are IntPtr; the found-plate buffer is passed as ref plates[0]; the per-character confidence field in the C# struct is named CharacterConfinedces (a spelling kept for binding compatibility).
Delphi — still-image recognition (console)
Add Include\nnAnpr.pas to the project; keep the platform on Win64.
Still-image recognition · Delphi
program PlateQuickStart;
{$APPTYPE CONSOLE}
uses
System.SysUtils, nnAnpr;
var
image : NNANPR_Image_ptr;
finder : NNANPR_PlateFinder_ptr;
recognizer : NNANPR_PlateRecognizer_ptr;
objs : PNNANPR_Object;
count, i : UInt64;
rr : NNANPR_RecognizerResult;
plateStr, countryStr : string;
begin
if NNANPR_Init <> NNANPR_RESULT_SUCCESS then // 1. initialize once
Halt(1);
// NNANPR_SetStringParameter('APIKEY', '...'); // cloud edition only
NNANPR_CreateImage(image); // 2. load the input image
if NNANPR_LoadImage('testdata\EU2.jpg', image) <> NNANPR_RESULT_SUCCESS then
Halt(2);
NNANPR_CreatePlateFinder(nngaEurope, nndIntelCPU, finder); // 3. create finder + recognizer
NNANPR_CreatePlateRecognizer(nngaEurope, nndIntelCPU, recognizer);
count := 30; // 4. find plates (in: capacity, out: found)
if NNANPR_FindPlates(finder, image, objs, count) = NNANPR_RESULT_SUCCESS then
for i := 0 to count - 1 do // 5. read each found plate
if NNANPR_RecognizePlate(recognizer, image, objs[i], rr) = NNANPR_RESULT_SUCCESS then
begin
plateStr := PChar(@rr.Characters[1]); // fixed buffers are NUL-terminated
countryStr := PChar(@rr.Country[1]);
Writeln(plateStr, ' country: ', countryStr);
end;
NNANPR_FreePlateRecognizer(recognizer); // 6. release in reverse order
NNANPR_FreePlateFinder(finder);
NNANPR_FreeImage(image);
end.
C++ — live ANPR on video (asynchronous + accumulator)
The real-world pipeline: capture frames from a stream, detect and read plates on SDK worker threads, and consolidate repeated reads of the same plate with the accumulator. Note the correct teardown order — stop capture before freeing anything — and the result-count guard.
Live ANPR on video · C++
#include "nnAnpr.h"
#include <iostream>
struct App {
NNANPR_PlateFinder_ptr finder = nullptr;
NNANPR_PlateRecognizer_ptr recognizer = nullptr;
NNANPR_RecognitionAccumulator_ptr accumulator = nullptr;
NNANPR_ImageWindow_ptr window = nullptr;
NNANPR_ImageCapture_ptr capture = nullptr;
};
// Runs on an SDK worker thread once a plate has been read (consolidated).
void OnRecognition(const NNANPR_RecognizerResult* rr, const NNANPR_Image_ptr image, void* ctx) {
if (rr->ErrorCode != NNANPR_RESULT_SUCCESS) return;
std::wcout << L"Plate: " << rr->Characters << L" (" << rr->Country << L")\n";
// In a GUI app, marshal to the UI thread here (PostMessage / Invoke) before touching controls.
}
// Runs on the finder thread once detection completes.
void OnPlateFound(const NNANPR_Object* plates, size_t count, const NNANPR_Image_ptr image, void* ctx) {
App* a = static_cast<App*>(ctx);
if (count == 0) return; // guard on the COUNT, never the pointer
NNANPR_RecognizePlateAsyncWithAccumulator(a->recognizer, a->accumulator, image, plates[0],
OnRecognition, ctx);
}
// Runs on the capture thread for every frame.
void OnFrame(const NNANPR_Image_ptr image, void* ctx) {
App* a = static_cast<App*>(ctx);
NNANPR_DrawImage(a->window, image); // show the live frame
NNANPR_FindPlatesAsync(a->finder, image, OnPlateFound, ctx);
}
int wmain() {
if (NNANPR_Init() != NNANPR_RESULT_SUCCESS) return -1;
App app;
NNANPR_CreatePlateFinder (nngaEurope, nndIntelCPU, &app.finder);
NNANPR_CreatePlateRecognizer(nngaEurope, nndIntelCPU, &app.recognizer);
NNANPR_CreateRecognitionAccumulator(&app.accumulator);
NNANPR_SetupRecognitionAccumulator(app.accumulator, 2000, 5, 20, 3); // 2 s / 5 frames / min 20 px / diff 3
NNANPR_CreateImageWindow(nullptr, &app.window);
if (NNANPR_CreateVideoCapture(&app.capture, L"rtsp://user:pass@192.168.1.10/stream")
!= NNANPR_RESULT_SUCCESS)
return -2;
NNANPR_StartCaptureFrameAsync(app.capture, OnFrame, &app); // callbacks now run on SDK threads
NNANPR_WaitImageWindow(app.window, 0, true); // block until the window is closed
NNANPR_StopCaptureFrameAsync(app.capture); // 1. stop capture FIRST
NNANPR_FreeVideoCapture(app.capture); // 2. then free, in reverse order
NNANPR_FreeRecognitionAccumulator(app.accumulator);
NNANPR_FreePlateRecognizer(app.recognizer);
NNANPR_FreePlateFinder(app.finder);
NNANPR_FreeImageWindow(app.window);
return 0;
}
C++ — batch-process a folder of images (CSV report)
Reads every image in a folder, writes one CSV row per plate. Reads all plates per image, not just the first.
Batch folder → CSV · C++
#include "nnAnpr.h"
#include <filesystem>
#include <fstream>
#include <iostream>
namespace fs = std::filesystem;
int wmain(int argc, wchar_t* argv[]) {
if (argc < 2) { std::wcout << L"usage: batch <folder>\n"; return -1; }
if (NNANPR_Init() != NNANPR_RESULT_SUCCESS) return -1;
NNANPR_PlateFinder_ptr finder = nullptr;
NNANPR_PlateRecognizer_ptr recognizer = nullptr;
NNANPR_CreatePlateFinder (nngaEurope, nndIntelCPU, &finder);
NNANPR_CreatePlateRecognizer(nngaEurope, nndIntelCPU, &recognizer);
NNANPR_SetupPlateFinder(finder, 0.5, 0);
NNANPR_Image_ptr image = nullptr;
NNANPR_CreateImage(&image);
std::wofstream report(L"results.csv");
report << L"file,plate,country\n";
for (auto& entry : fs::directory_iterator(argv[1])) {
auto ext = entry.path().extension().wstring();
if (ext != L".jpg" && ext != L".png" && ext != L".bmp") continue;
if (NNANPR_LoadImage(entry.path().c_str(), image) != NNANPR_RESULT_SUCCESS) continue;
NNANPR_Object plates[30]; size_t count = 30;
if (NNANPR_FindPlates(finder, image, plates, &count) != NNANPR_RESULT_SUCCESS) continue;
for (size_t i = 0; i < count; ++i) { // every plate, not just the first
NNANPR_RecognizerResult rr{};
if (NNANPR_RecognizePlate(recognizer, image, plates[i], &rr) == NNANPR_RESULT_SUCCESS)
report << entry.path().filename().wstring() << L',' << rr.Characters
<< L',' << rr.Country << L'\n';
}
}
NNANPR_FreeImage(image);
NNANPR_FreePlateRecognizer(recognizer);
NNANPR_FreePlateFinder(finder);
std::wcout << L"Done. Results in results.csv\n";
return 0;
}
Tip
For make & model instead of plates, swap the finder/recognizer for NNANPR_CreateVehicleFinder / NNANPR_CreateVehicleModelRecognizer and call NNANPR_FindVehicles / NNANPR_RecognizeVehicleModel — the structure is identical (see section 8).
C# — live ANPR on video (RTSP)
Open an rtsp:// stream and read plates asynchronously with the accumulator. The callback delegates are stored in fields so the garbage collector cannot collect them while the engine is still calling them.
Root the delegates
Storing each callback in a field (as above) is mandatory. Passing a method group or lambda directly lets the GC collect the thunk, and the native engine will later crash calling a dead delegate. In a WinForms app, marshal to the UI thread with Control.BeginInvoke before touching controls.
C# — vehicle make & model
Detect vehicles and classify make & model with the nnVmm binding.
Vehicle make & model · C#
using System;
class Vmm
{
static int Main()
{
if (nnAnpr.NNANPR_Init() != nnAnpr.NNANPR_RESULT_SUCCESS) return -1;
IntPtr image = IntPtr.Zero;
nnAnpr.NNANPR_CreateImage(ref image);
if (nnAnpr.NNANPR_LoadImage(@"testdata\Traker1.jpg", image) != nnAnpr.NNANPR_RESULT_SUCCESS) return -2;
IntPtr vf = IntPtr.Zero, vmr = IntPtr.Zero;
nnVmm.NNANPR_CreateVehicleFinder(NNANPR_Device.nndIntelCPU, ref vf);
nnVmm.NNANPR_SetupVehicleFinder(vf, 0.5);
nnVmm.NNANPR_CreateVehicleModelRecognizer(NNANPR_Device.nndIntelCPU, ref vmr);
ulong[] vehicles = new ulong[30];
ulong count = (ulong)vehicles.Length;
if (nnVmm.NNANPR_FindVehicles(vf, image, ref vehicles[0], ref count) == nnAnpr.NNANPR_RESULT_SUCCESS)
for (ulong i = 0; i < count; i++) {
var mr = new NNANPR_ModelRecognizerResult();
if (nnVmm.NNANPR_RecognizeVehicleModel(vmr, image, vehicles[i], ref mr) == nnAnpr.NNANPR_RESULT_SUCCESS)
Console.WriteLine($"{new string(mr.ModelName).TrimEnd('\0')} {mr.Confidence:0.00}");
}
nnVmm.NNANPR_FreeVehicleModelRecognizer(vmr);
nnVmm.NNANPR_FreeVehicleFinder(vf);
nnAnpr.NNANPR_FreeImage(image);
return 0;
}
}
Note
The C# model struct field is Confidence (spelled correctly in the C# binding). Pass 0 instead of a vehicle object to classify a whole image. For live vehicle recognition, use NNANPR_FindVehiclesAsync + NNANPR_RecognizeVehicleModelAsync with rooted delegates, exactly as the ANPR RTSP sample above.
Delphi — live ANPR on video (RTSP)
Open an rtsp:// stream and read plates continuously using global cdecl callbacks.
Live ANPR on video · Delphi
program LiveAnpr;
{$APPTYPE CONSOLE}
uses System.SysUtils, nnAnpr;
var
Finder : NNANPR_PlateFinder_ptr;
Recognizer : NNANPR_PlateRecognizer_ptr;
Accum : NNANPR_RecognitionAccumulator_ptr;
Window : NNANPR_ImageWindow_ptr;
Capture : NNANPR_ImageCapture_ptr;
procedure OnRecognition(rr: PNNANPR_RecognizerResult; image: NNANPR_Image_ptr; ctx: Pointer); cdecl;
begin
if rr^.ErrorCode = NNANPR_RESULT_SUCCESS then
Writeln(string(PChar(@rr^.Characters[1])), ' (', string(PChar(@rr^.Country[1])), ')');
end;
procedure OnPlateFound(plates: PNNANPR_Object; count: UInt64; image: NNANPR_Image_ptr; ctx: Pointer); cdecl;
begin
if count = 0 then Exit; // guard on the count
NNANPR_RecognizePlateAsyncWithAccumulator(Recognizer, Accum, image, plates[0], OnRecognition, nil);
end;
procedure OnFrame(image: NNANPR_Image_ptr; ctx: Pointer); cdecl;
begin
NNANPR_DrawImage(Window, image);
NNANPR_FindPlatesAsync(Finder, image, OnPlateFound, nil);
end;
begin
if NNANPR_Init <> NNANPR_RESULT_SUCCESS then Halt(1);
NNANPR_CreatePlateFinder(nngaEurope, nndIntelCPU, Finder);
NNANPR_CreatePlateRecognizer(nngaEurope, nndIntelCPU, Recognizer);
NNANPR_CreateRecognitionAccumulator(Accum);
NNANPR_SetupRecognitionAccumulator(Accum, 2000, 5, 20, 3);
NNANPR_CreateImageWindow(0, Window);
if NNANPR_CreateVideoCapture(Capture, 'rtsp://user:pass@192.168.1.10/stream') <> NNANPR_RESULT_SUCCESS then Halt(2);
NNANPR_StartCaptureFrameAsync(Capture, OnFrame, nil); // callbacks run on SDK threads
NNANPR_WaitImageWindow(Window, 0, True); // block until the window is closed
NNANPR_StopCaptureFrameAsync(Capture); // stop FIRST, then free
NNANPR_FreeVideoCapture(Capture);
NNANPR_FreeRecognitionAccumulator(Accum);
NNANPR_FreePlateRecognizer(Recognizer);
NNANPR_FreePlateFinder(Finder);
NNANPR_FreeImageWindow(Window);
end.
VCL apps
These callbacks run on SDK threads — never touch VCL controls from them. Marshal to the UI with TThread.Queue/TThread.Synchronize or PostMessage, as the shipped VideoAsync sample does.
Delphi — vehicle make & model
Detect vehicles and classify make & model with the nnVmm unit.
Vehicle make & model · Delphi
program VehicleModel;
{$APPTYPE CONSOLE}
uses System.SysUtils, nnAnpr, nnVmm;
var
image : NNANPR_Image_ptr;
vf : NNANPR_VehicleFinder_ptr;
vmr : NNANPR_VehicleModelRecognizer_ptr;
vehicles : PNNANPR_Object;
count, i : UInt64;
mr : NNANPR_ModelRecognizerResult;
begin
if NNANPR_Init <> NNANPR_RESULT_SUCCESS then Halt(1);
NNANPR_CreateImage(image);
if NNANPR_LoadImage('testdata\Traker1.jpg', image) <> NNANPR_RESULT_SUCCESS then Halt(2);
NNANPR_CreateVehicleFinder(nndIntelCPU, vf);
NNANPR_SetupVehicleFinder(vf, 0.5);
NNANPR_CreateVehicleModelRecognizer(nndIntelCPU, vmr);
count := 30;
if NNANPR_FindVehicles(vf, image, vehicles, count) = NNANPR_RESULT_SUCCESS then
for i := 0 to count - 1 do
if NNANPR_RecognizeVehicleModel(vmr, image, vehicles[i], mr) = NNANPR_RESULT_SUCCESS then
Writeln(string(PChar(@mr.ModelName[1])), ' ', FormatFloat('0.00', mr.Confinedce));
NNANPR_FreeVehicleModelRecognizer(vmr);
NNANPR_FreeVehicleFinder(vf);
NNANPR_FreeImage(image);
end.
Field name
The Delphi model struct field is spelled Confinedce in nnVmm.pas. Free VMM handles with the VMM free functions; pass 0 to classify a whole image.
18Support
[Vendor: insert support email, phone, ticket portal and SLA here.] When contacting support, include: SDK build date, Windows version, selected device (NNANPR_Device), the error code and the text from NNANPR_GetErrorString, plus engine logs (ENABLE LOG FILES = 1, see section 9).
Need a hand integrating the SDK? Contact support and a real engineer will get back to you.