NN ANPR & VMM SDK

Quick Start

Get results in minutes. Pick your language, then a scenario — read a plate from an image, process a live RTSP stream, or classify a vehicle's make & model. Each scenario is one complete, copy-paste program that runs locally against nnAnpr.dll.

1 · Choose your language

about 5 minutes

Build with C / C++

Setup — once
  1. Prerequisites: Windows x64, Visual C++ 2015–2022 runtime, Visual Studio. Keep bin\ intact (nnAnpr.dll + data\).
  2. Add to project: add Include\ to the include path, #include "nnAnpr.h" (and "nnVmm.h" for vehicles), link Include\nnAnpr.lib.
  3. Platform: build x64, and run from the SDK bin\ folder.

2 · Choose a scenario

Plate · from image

Load an image, find every plate, read each one.

main.cpp
#include "nnAnpr.h"
#include <iostream>
#pragma comment(lib, "nnAnpr.lib")

int wmain()
{
    if (NNANPR_Init() != NNANPR_RESULT_SUCCESS) return -1;        // init once

    NNANPR_Image_ptr image = nullptr;                            // load an image
    NNANPR_CreateImage(&image);
    if (NNANPR_LoadImage(L"testdata\\EU2.jpg", image) != NNANPR_RESULT_SUCCESS) return -2;

    NNANPR_PlateFinder_ptr     finder     = nullptr;             // create engines
    NNANPR_PlateRecognizer_ptr recognizer = nullptr;
    NNANPR_CreatePlateFinder    (nngaEurope, nndIntelCPU, &finder);
    NNANPR_CreatePlateRecognizer(nngaEurope, nndIntelCPU, &recognizer);

    NNANPR_Object plates[30]; size_t count = 30;                 // find plates
    if (NNANPR_FindPlates(finder, image, plates, &count) == NNANPR_RESULT_SUCCESS)
        for (size_t i = 0; i < count; ++i) {                     // read each plate
            NNANPR_RecognizerResult rr{};
            if (NNANPR_RecognizePlate(recognizer, image, plates[i], &rr) == NNANPR_RESULT_SUCCESS)
                std::wcout << rr.Characters << L"  (" << rr.Country << L")\n";
        }

    NNANPR_FreePlateRecognizer(recognizer);                      // clean up (reverse order)
    NNANPR_FreePlateFinder(finder);
    NNANPR_FreeImage(image);
    return 0;
}
You should see each recognized plate and its country, e.g. S AB 1234 (Germany).

RTSP · live video

Open an rtsp:// stream (or camera) and read plates continuously. Detection and recognition run on SDK worker threads; the accumulator merges repeated reads of the same plate.

live.cpp
#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;
};

void OnRecognition(const NNANPR_RecognizerResult* rr, const NNANPR_Image_ptr image, void* ctx) {
    if (rr->ErrorCode == NNANPR_RESULT_SUCCESS)
        std::wcout << rr->Characters << L"  (" << rr->Country << L")\n";
}
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, not the pointer
    NNANPR_RecognizePlateAsyncWithAccumulator(a->recognizer, a->accumulator, image, plates[0], OnRecognition, ctx);
}
void OnFrame(const NNANPR_Image_ptr image, void* ctx) {
    App* a = static_cast<App*>(ctx);
    NNANPR_DrawImage(a->window, image);
    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);
    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 run on SDK threads
    NNANPR_WaitImageWindow(app.window, 0, true);                 // block until the window is closed

    NNANPR_StopCaptureFrameAsync(app.capture);          // stop FIRST, then free
    NNANPR_FreeVideoCapture(app.capture);
    NNANPR_FreeRecognitionAccumulator(app.accumulator);
    NNANPR_FreePlateRecognizer(app.recognizer);
    NNANPR_FreePlateFinder(app.finder);
    NNANPR_FreeImageWindow(app.window);
    return 0;
}
Key point Always NNANPR_StopCaptureFrameAsync before freeing any engine, and guard the callback on the result count, not the array pointer.
You should see a live video window, with each recognized plate printed as vehicles pass.

Make & model

Detect vehicles and classify make & model. The VMM API lives in nnVmm.h and mirrors the plate flow (no region parameter).

vmm.cpp
#include "nnVmm.h"       // includes nnAnpr.h
#include <iostream>

int wmain()
{
    if (NNANPR_Init() != NNANPR_RESULT_SUCCESS) return -1;

    NNANPR_Image_ptr image = nullptr;
    NNANPR_CreateImage(&image);
    if (NNANPR_LoadImage(L"testdata\\Traker1.jpg", image) != NNANPR_RESULT_SUCCESS) return -2;

    NNANPR_VehicleFinder_ptr          vf  = nullptr;             // create vehicle engines
    NNANPR_VehicleModelRecognizer_ptr vmr = nullptr;
    NNANPR_CreateVehicleFinder(nndIntelCPU, &vf);
    NNANPR_SetupVehicleFinder(vf, 0.5);
    NNANPR_CreateVehicleModelRecognizer(nndIntelCPU, &vmr);

    NNANPR_Object vehicles[30]; size_t count = 30;              // find vehicles
    if (NNANPR_FindVehicles(vf, image, vehicles, &count) == NNANPR_RESULT_SUCCESS)
        for (size_t i = 0; i < count; ++i) {                    // classify each
            NNANPR_ModelRecognizerResult mr{};
            if (NNANPR_RecognizeVehicleModel(vmr, image, vehicles[i], &mr) == NNANPR_RESULT_SUCCESS)
                std::wcout << mr.ModelName << L"  " << mr.Confidence << L"\n";
        }

    NNANPR_FreeVehicleModelRecognizer(vmr);                     // VMM-specific free functions
    NNANPR_FreeVehicleFinder(vf);
    NNANPR_FreeImage(image);
    return 0;
}
Key point Free VMM handles with NNANPR_FreeVehicleModelRecognizer / NNANPR_FreeVehicleFinder — not the plate free functions. Pass 0 instead of a vehicle object to classify a whole image.
You should see the make & model and a confidence score for each detected vehicle.
Full sample projects — C / C++ (in the SDK's Examples\c++\)
  • PlateRecognize\ — plate from image (console)
  • VideoAsync\ — live ANPR on video / RTSP (async + accumulator)
  • VideoSample\ — live ANPR (synchronous) with open-image and camera list
  • VehicleModelRecognize\ — vehicle make & model (console)
  • VideoAsyncVMM\ — live make & model on video
  • Multicameras\ — several streams in parallel
about 5 minutes

Build with C#

Setup — once
  1. Prerequisites: Windows x64, Visual C++ 2015–2022 runtime. Keep the SDK bin\ (with data\) reachable — run from it or add it to PATH.
  2. Add the binding: add Include\nnAnpr.cs to your project (and nnVmm.cs for vehicles).
  3. Platform: set Platform target to x64 (not AnyCPU).

2 · Choose a scenario

Plate · from image

Load an image, find every plate, read each one.

Program.cs
using System;

class Program
{
    static int Main()
    {
        if (nnAnpr.NNANPR_Init() != nnAnpr.NNANPR_RESULT_SUCCESS) return -1;   // init once

        IntPtr image = IntPtr.Zero;                                           // load an image (handles are IntPtr)
        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;               // create engines
        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];                                      // find plates
        ulong   count  = (ulong)plates.Length;
        if (nnAnpr.NNANPR_FindPlates(finder, image, ref plates[0], ref count) == nnAnpr.NNANPR_RESULT_SUCCESS)
            for (ulong i = 0; i < count; i++) {                              // read each plate
                var rr = new NNANPR_RecognizerResult();
                if (nnAnpr.NNANPR_RecognizePlate(recognizer, image, plates[i], ref rr) == nnAnpr.NNANPR_RESULT_SUCCESS)
                    Console.WriteLine($"{Text(rr.Characters)}  ({Text(rr.Country)})");
            }

        nnAnpr.NNANPR_FreePlateRecognizer(recognizer);                       // clean up
        nnAnpr.NNANPR_FreePlateFinder(finder);
        nnAnpr.NNANPR_FreeImage(image);
        return 0;
    }
    static string Text(char[] a) => new string(a).TrimEnd('\0');            // fixed 50-char buffer
}
You should see each recognized plate and its country, e.g. S AB 1234 (Germany).

RTSP · live video

Open an rtsp:// stream and read plates continuously. The callback delegates are stored in fields so the garbage collector can't collect them while the engine still calls them.

LiveAnpr.cs
using System;

class LiveAnpr
{
    // keep delegates alive for the whole run (GC-rooted)
    readonly NNANPROnFrameCallback       _onFrame;
    readonly NNANPROnPlateFoundCallback  _onPlates;
    readonly NNANPROnRecognitionCallback _onRead;
    IntPtr _finder, _recognizer, _accum, _window, _capture;

    LiveAnpr() { _onFrame = OnFrame; _onPlates = OnPlatesFound; _onRead = OnPlateRead; }

    void OnFrame(IntPtr image, IntPtr ctx) {
        nnAnpr.NNANPR_DrawImage(_window, image);
        nnAnpr.NNANPR_FindPlatesAsync(_finder, image, _onPlates, IntPtr.Zero);
    }
    void OnPlatesFound(ulong[] plates, ulong count, IntPtr image, IntPtr ctx) {
        if (count > 0)
            nnAnpr.NNANPR_RecognizePlateAsyncWithAccumulator(_recognizer, _accum, image, plates[0], _onRead, IntPtr.Zero);
    }
    void OnPlateRead(ref NNANPR_RecognizerResult rr, IntPtr image, IntPtr ctx) {
        if (rr.ErrorCode == nnAnpr.NNANPR_RESULT_SUCCESS)
            Console.WriteLine(new string(rr.Characters).TrimEnd('\0'));
    }

    static void Main() {
        if (nnAnpr.NNANPR_Init() != nnAnpr.NNANPR_RESULT_SUCCESS) return;
        var app = new LiveAnpr();
        nnAnpr.NNANPR_CreatePlateFinder(NNANPR_GeographicalArea.nngaEurope, NNANPR_Device.nndIntelCPU, ref app._finder);
        nnAnpr.NNANPR_CreatePlateRecognizer(NNANPR_GeographicalArea.nngaEurope, NNANPR_Device.nndIntelCPU, ref app._recognizer);
        nnAnpr.NNANPR_CreateRecognitionAccumulator(ref app._accum);
        nnAnpr.NNANPR_SetupRecognitionAccumulator(app._accum, 2000, 5, 20, 3);
        nnAnpr.NNANPR_CreateImageWindow(IntPtr.Zero, ref app._window);
        if (nnAnpr.NNANPR_CreateVideoCapture(ref app._capture, "rtsp://user:pass@192.168.1.10/stream") != nnAnpr.NNANPR_RESULT_SUCCESS) return;

        nnAnpr.NNANPR_StartCaptureFrameAsync(app._capture, app._onFrame, IntPtr.Zero);
        nnAnpr.NNANPR_WaitImageWindow(app._window, 0, true);          // block until closed

        nnAnpr.NNANPR_StopCaptureFrameAsync(app._capture);            // stop FIRST, then free
        nnAnpr.NNANPR_FreeVideoCapture(app._capture);
        nnAnpr.NNANPR_FreeRecognitionAccumulator(app._accum);
        nnAnpr.NNANPR_FreePlateRecognizer(app._recognizer);
        nnAnpr.NNANPR_FreePlateFinder(app._finder);
        nnAnpr.NNANPR_FreeImageWindow(app._window);
    }
}
Key point Store every callback delegate in a field (as above). Passing a method group directly lets the GC collect it, and the native engine will later crash calling a dead delegate.
You should see a live video window and each recognized plate printed as vehicles pass.

Make & model

Detect vehicles and classify make & model with the nnVmm binding.

Vmm.cs
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;
    }
}
Key point The C# model struct field is Confidence (correctly spelled in the C# binding). Pass 0 instead of a vehicle to classify a whole image.
You should see the make & model and a confidence score for each detected vehicle.
Full sample projects — C# (in the SDK's Examples\c#\)
  • VideoSampleCS\ — live ANPR (synchronous) with an Open-image button
  • VideoAsyncCS\ — live ANPR on video / RTSP (async + accumulator)
  • VehicleModelRecognizerCS\ — vehicle make & model (console)
about 5 minutes

Build with Delphi

Setup — once
  1. Prerequisites: RAD Studio / Delphi, Windows x64, Visual C++ 2015–2022 runtime. Keep the SDK bin\ (with data\) reachable.
  2. Add the binding: add Include\nnAnpr.pas (and nnVmm.pas) to your project or search path; list them in uses.
  3. Platform: keep the target on Win64 (the binding is Win64-only).

2 · Choose a scenario

Plate · from image

Load an image, find every plate, read each one.

QuickStart.dpr
program QuickStart;
{$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;
begin
  if NNANPR_Init <> NNANPR_RESULT_SUCCESS then Halt(1);            // init once

  NNANPR_CreateImage(image);                                       // load an image
  if NNANPR_LoadImage('testdata\EU2.jpg', image) <> NNANPR_RESULT_SUCCESS then Halt(2);

  NNANPR_CreatePlateFinder(nngaEurope, nndIntelCPU, finder);       // create engines
  NNANPR_CreatePlateRecognizer(nngaEurope, nndIntelCPU, recognizer);

  count := 30;                                                     // find plates
  if NNANPR_FindPlates(finder, image, objs, count) = NNANPR_RESULT_SUCCESS then
    for i := 0 to count - 1 do                                     // read each plate
      if NNANPR_RecognizePlate(recognizer, image, objs[i], rr) = NNANPR_RESULT_SUCCESS then
        Writeln(string(PChar(@rr.Characters[1])), '  (', string(PChar(@rr.Country[1])), ')');

  NNANPR_FreePlateRecognizer(recognizer);                          // clean up
  NNANPR_FreePlateFinder(finder);
  NNANPR_FreeImage(image);
end.
You should see each recognized plate and its country, e.g. S AB 1234 (Germany).

RTSP · live video

Open an rtsp:// stream and read plates continuously using global cdecl callbacks.

LiveAnpr.dpr
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.
Key point In a VCL app, don't touch controls from these callbacks — they run on SDK threads. Marshal to the UI with TThread.Queue/Synchronize or PostMessage (see the shipped VideoAsync sample).
You should see a live video window and each recognized plate printed as vehicles pass.

Make & model

Detect vehicles and classify make & model with the nnVmm unit.

VehicleModel.dpr
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.
Key point The Delphi model struct field is spelled Confinedce in nnVmm.pas. Free with the VMM free functions; pass 0 to classify a whole image.
You should see the make & model and a confidence score for each detected vehicle.
Full sample projects — Delphi 12 (in the SDK's Examples\Delphi 12\)
  • VideoSample\ — live ANPR (synchronous) with open-image
  • VideoAsync\ — live ANPR on video / RTSP (async + accumulator)
  • VehicleModelRecognizer\ — vehicle make & model

Next steps

  • Switch region — replace nngaEurope with nngaMiddleEast, nngaAfrica, nngaAsia_Australasia, or nngaUSA_SouthAmerica.
  • Open an industrial camera (GigE/USB3) instead of a file/URL with NNANPR_GetSpecialVideoCaptureList + NNANPR_CreateSpecialVideoCapture.
  • Tune detection thresholds, restrict a detection zone, raise the async queue size, or load a country syntax template.
  • See the full Developer Guide for the API reference, error codes, deployment and troubleshooting.

NN ANPR & VMM SDK — Quick Start · Windows x64 · C/C++ · C# · Delphi. Samples are built against the shipped headers/bindings (current Recognize API); run all demos from the SDK bin\ folder.

Something went wrong. Reload ✕