How to implement license plate recognition in C#

License plate recognition is a mass surveillance technique used for identifying registered vehicle plates. This guide shows you all the information needed for using the function. Follow the instructions and descriptions written below and you will be able to implement the license plate recognition successfully. For the implemention use the help of your C# camera application. To succeed, Ozeki Camera SDK has to be installed and a reference to OzekiSDK.dll has to be added to your Visual Studio project.

detected license plate
Figure 1 - Detected license plate

Important: you should study this article in order to find out how to setup your Windows Forms Application correctly.

Getting started

To get started it is recomended to Download and Install the latest version of Ozeki Camera SDK. After installation you can find the example code discussed in this page with full source code in the following location on your harddisk:

Download Ozeki Camera SDK:
Windows forms version: C:\Program Files\Ozeki\Ozeki SDK\examples\04_ComputerVision\09_LicensePlateDetection

To compile this example you will need Microsoft Visual Studio installed on your computer.

Corporate use of license plate recognition

License plate recognition is an extremely popular function nowadays. You can benefit from it if you wish to analyze and detect license plates in a given area where a camera is placed. This function of the Ozeki Camera SDK can be an effective help for those who would like to create monitoring or surveillance systems based on license plate recognition.

There are a lot of fields where the license plate recognition function of the Ozeki Camera SDK can be used. With the help of the license plate recognition you can develop an intelligent transportation system or help the work of border control systems. What is more, you can analyze the traffic, the parking habits or improve the security of a parking area.

We can mention other situations where license plate recognition can be used: in security monitoring, vehicle surveillance, theft prevention, controlling traffic rules. What is more, with the help of this function you can draw up a complex database of traffic.

Implement license plate recognition in C#

Some computer vision possibilities which are supplied by the SDK require an extra ToolKit. This ToolKit is the OzekiComputerVision.dll to which a reference has to be added in the project, just like in the case of the OzekiSDK.dll. This dll is responsible for the algorithms and tools which are necessary for the CV (Computer Vision) actions.

License plate recognition

For detecting edges we can use the ILicensePlateRecognizer object of the Ozeki Camera SDK. After an instance has been created with the help of the static ImageProcesserFactory class we can detect on frames and on video as well.

In the case of frames the output image can be created by the Process() method of the instance. In the case of videos we have to use the ImageProcesserHandler mediahandler.


This is a mediahandler from a VideoHandler class (so it is VideoReceiver and VideoSender at the same time) which means that the input can be a VideoSender (for example WebCamera) and the output can also be created for a VideoReceiver. These connections can be built with the help of an instance of the MediaConnector class in the SDK.

No actions are executed on the input frames they are simply forwarded by default. However, the object which implement the IImageProcesser interface (for example the ILicensePlateRecognizer) can be added with the help of the AddProcesser() method. More instance can be added which implement IImageProcesser, they will run one after the other using the image which is before them in the list.

The example uses the FrameCapture mediahandler as well which examines only every fifth frame. You can find the descripition of Frame capture here.

Global Variables

  • WebCamera _webCamera;
  • Webcamera instance, with the help of this we can get the images.

  • MediaConnector _connector;
  • Using this we can connect the mediahandlers.

  • ImageProcesserHandler _imageProcesserHandler;
  • This is a Mediahandler, which runs the IImageProcesser interface (this processes the images) on the incoming video.

  • ILicensePlateRecognizer _licensePlateRecognizer;
  • This is an image processer interface which can detect license plates, this implements the IImageProcesser interface.

  • FrameCapture _frameCapture;
  • With the help of mediahandler we can determine the frequency of processing on it.

  • VideoViewerWF instances
  • This is a GUI tool which is responsible for displaying the video for Windows Forms applications.

  • DrawingImageProvider instances
  • Mediahandler which prepares the image which is sent by the mediahandlers from the VideoSender class for the VideoViewerWF instances.


  • Init()
  • The initialization of the global variables is the task of the Init() method. The instance of the FrameCapture mediahandler are set here and the ILicensePlateRecognizer instance is also created here with the help of the ImageProcesserFactory. This also can be added to the ImageProcesserHandler instance. The processing of all images is indicated by the DetectionOccuredevent of the ILincesePlateRecognizer instance. Here we can subscribe to this event.

  • SetVideoViewers()
  • The SetVideoViewer() creates and initialize the objects which are responsible for the displaying of the video. It defines the VideoViewerWF instances, configures their properties and assigns the appropiate DrawingImageProvider instances. Furthermore, the SetVideoViewers() adds these instances to the GUI.

  • InvokeGUIThread()
  • This method handles the GUI thread. It performs a specific method on the GUI thread in asynchronous mode.

  • InitDetectorFields()
  • This method fills the TextBox-es which can be found on the GUI with the actual settings of the ILicensePlateRecognizer instance using the InvokeGUIThread() helper method.

  • ConnectWebcam()
  • This method connects the appropiate mediahandler instances with the help of the MediaConnector instance. One of the ImageProvider object recives the original image of the webcamera and the other object receives the processed image.

  • Start()
  • After the initializations the mediahandlers can start to operate which is the responsibility of the Start()method.


There are two main steps during the license plate recognition: the detection of the license plate itself (detection) and the recognition of the number on the plate (recognition). Most of the configurations refer to the first step.

  • ApertureSize: The Aperture value which is used for the edge detector algorithm.
  • CannyThreshold: It is used for detecting the edges during the whole detection. The value of the thresholding is determined by the CannyThreshold.
  • CannyThresholdLinking: It is used for detecting the edges during the whole detection.
  • MinArea: The minimal size of the license plate.
  • MaxArea: The maximum size of the license plate (If the provided value is 0 then it will be ignored).
  • MinWidthHeightRatio: The minimal width-height ratio of the license plate which you wish to detect (here a minimum should be provided).
  • MaxWidthHeightRatio: The maximum width-height ratio of the license plate which you wish to detect.
  • PlateLeftPadding: We can increase/restrict the top and the left side of the already detected license plate. In this way we can modify the image part on which the license plate detection will take place.
  • PlateRightPadding: We can increase/restrict the bottom and the right side of the already detected license plate. In this way we can modify the image part on which the license plate detection will take place.
  • DetectableCharacters: Provides the characters as a string which can be detected.
  • AddSchemeRefex(): If it is set to default, every detected text will be given back by the detector on every found rectangle. You can provide the pattern of the license plate by using regular expressions.
  • GetSchemeRegexes(): Returns with the added list of regular expressions.
  • RemoveSchemeRegex(): Picks the regular expression with a determined prefix from the list.
  • SetDetectableChars(): The characters which should be determined can be provided as a string. By default the capital letters of the English ABC and the dash is set.

PostProcess configurations

After the detection we can configure the behavior of the output image and what changes are required. Examples for this kind of configurations:

  • ShowImage: Declares whether the original image should be shown or only the detected shapes should be seen with black background. It is important if there are more image processer algorithms are running after each other.
  • DrawColor: The color of the selection of the detected object can be provided here.
  • DrawThickness: The thickness of the selection of the detected object can be provided here.


After every license plate recognition the DetectionOccured event is triggered. In the arguments of this event you can find the detected registration number and the characters from which it was generated by using the regular expressions.

C# code example for license plate recognition

Windows Form  

Windows forms version


using System;
using System.Windows.Forms;
using Ozeki.Camera;
using Ozeki.Media;
using Ozeki.Vision;

namespace LicensePlateDetection_WF
    public partial class MainForm : Form
         private OzekiCamera _camera;
        private MediaConnector _connector;
        private CameraURLBuilderWF _myCameraUrlBuilder;
        private ImageProcesserHandler _imageProcesserHandler;
        private ILicensePlateRecognizer _licensePlateRecognizer;
        private ILineDetector _lineDetector;
        private DrawingImageProvider _originalImageProvider;
        private DrawingImageProvider _processedImageProvider;

        public MainForm()

        void MainForm_Load(object sender, EventArgs e)



        void Init()
            _myCameraUrlBuilder=new CameraURLBuilderWF();
            _connector = new MediaConnector();
            _originalImageProvider = new DrawingImageProvider();
            _processedImageProvider = new DrawingImageProvider();

            _licensePlateRecognizer = ImageProcesserFactory.CreateLicensePlateRecognizer();
            _licensePlateRecognizer.DetectionOccurred += _licensePlateRecognizer_DetectionOccurred;

            _imageProcesserHandler = new ImageProcesserHandler();


        void SetVideoViewers()

        void InitDetectorFields()
            InvokeGuiThread(() =>
                tb_CannyThreshold.Text = _licensePlateRecognizer.CannyThreshold.ToString();
                tb_CannyThresholdLinking.Text = _licensePlateRecognizer.CannyThresholdLinking.ToString();

        void ConnectCam()
            _connector.Connect(_camera.VideoChannel, _originalImageProvider);

            _connector.Connect(_camera.VideoChannel, _imageProcesserHandler);
            _connector.Connect(_imageProcesserHandler, _processedImageProvider);

        void Start()


        void btn_HighlightSet_Click(object sender, EventArgs e)
            InvokeGuiThread(() =>
                _licensePlateRecognizer.CannyThreshold = Double.Parse(tb_CannyThreshold.Text);
                _licensePlateRecognizer.CannyThresholdLinking = Double.Parse(tb_CannyThresholdLinking.Text);

        void _licensePlateRecognizer_DetectionOccurred(object sender, DetectedLicensePlate e)
            InvokeGuiThread(() =>

        void InvokeGuiThread(Action action)

        private void button_Compose_Click(object sender, EventArgs e)
            var result = _myCameraUrlBuilder.ShowDialog();

            if (result != DialogResult.OK) return;

            tb_cameraUrl.Text = _myCameraUrlBuilder.CameraURL;

            button_Connect.Enabled = true;

        private void button_Connect_Click(object sender, EventArgs e)
            if (_camera != null)
                _camera.CameraStateChanged -= _camera_CameraStateChanged;
                _connector.Disconnect(_camera.VideoChannel, _processedImageProvider);
                _connector.Disconnect(_camera.VideoChannel, _originalImageProvider);
                _camera = null;

            _camera = new OzekiCamera(_myCameraUrlBuilder.CameraURL);
            _camera.CameraStateChanged += _camera_CameraStateChanged;

            button_Connect.Enabled = false;



        private void _camera_CameraStateChanged(object sender, CameraStateEventArgs e)
            InvokeGuiThread(() =>
                if (e.State == CameraState.Connecting)
                    button_Connect.Enabled = false;
                if (e.State == CameraState.Streaming)
                    button_Disconnect.Enabled = true;
                if (e.State == CameraState.Disconnected)
                    button_Disconnect.Enabled = false;
                    button_Connect.Enabled = true;

            InvokeGuiThread(() =>
                stateLabel.Text = e.State.ToString();

        private void button_Disconnect_Click(object sender, EventArgs e)
            if (_camera == null) return;

            _connector.Disconnect(_camera.VideoChannel, _processedImageProvider);
            _connector.Disconnect(_camera.VideoChannel, _originalImageProvider);
            _camera = null;


Code 1 - License plate recognition example code in C#


graphical user interface
Figure 2 - The graphical user interface of your application

After you have downloaded the Ozeki Camera SDK software you can find the GUI code in the Example folder.


namespace LicensePlateDetection_WF
    partial class MainForm
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
            if (disposing && (components != null))

        #region Windows Form Designer generated code

        private void InitializeComponent()
            this.label1 = new System.Windows.Forms.Label();
            this.label2 = new System.Windows.Forms.Label();
            this.tb_CannyThreshold = new System.Windows.Forms.TextBox();
            this.tb_CannyThresholdLinking = new System.Windows.Forms.TextBox();
            this.label5 = new System.Windows.Forms.Label();
            this.label4 = new System.Windows.Forms.Label();
            this.groupBox2 = new System.Windows.Forms.GroupBox();
            this.btn_HighlightSet = new System.Windows.Forms.Button();
            this.lb_Detection = new System.Windows.Forms.ListBox();
            this.label12 = new System.Windows.Forms.Label();
            this.OriginalViewer = new Ozeki.Media.VideoViewerWF();
            this.ProcessedViewer = new Ozeki.Media.VideoViewerWF();
            this.groupBox5 = new System.Windows.Forms.GroupBox();
            this.stateLabel = new System.Windows.Forms.Label();
            this.label14 = new System.Windows.Forms.Label();
            this.button_Connect = new System.Windows.Forms.Button();
            this.button_Disconnect = new System.Windows.Forms.Button();
            this.tb_cameraUrl = new System.Windows.Forms.TextBox();
            this.label13 = new System.Windows.Forms.Label();
            this.button_Compose = new System.Windows.Forms.Button();
            // label1
            this.label1.AutoSize = true;
            this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
            this.label1.Location = new System.Drawing.Point(7, 357);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(87, 13);
            this.label1.TabIndex = 0;
            this.label1.Text = "Original image";
            // label2
            this.label2.AutoSize = true;
            this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
            this.label2.Location = new System.Drawing.Point(348, 357);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(103, 13);
            this.label2.TabIndex = 1;
            this.label2.Text = "Processed image";
            // tb_CannyThreshold
            this.tb_CannyThreshold.Location = new System.Drawing.Point(140, 23);
            this.tb_CannyThreshold.Name = "tb_CannyThreshold";
            this.tb_CannyThreshold.Size = new System.Drawing.Size(87, 20);
            this.tb_CannyThreshold.TabIndex = 4;
            // tb_CannyThresholdLinking
            this.tb_CannyThresholdLinking.Location = new System.Drawing.Point(140, 57);
            this.tb_CannyThresholdLinking.Name = "tb_CannyThresholdLinking";
            this.tb_CannyThresholdLinking.Size = new System.Drawing.Size(87, 20);
            this.tb_CannyThresholdLinking.TabIndex = 5;
            // label5
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(6, 60);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(121, 13);
            this.label5.TabIndex = 12;
            this.label5.Text = "CannyThresholdLinking:";
            // label4
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(6, 26);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(87, 13);
            this.label4.TabIndex = 11;
            this.label4.Text = "CannyThreshold:";
            // groupBox2
            this.groupBox2.Location = new System.Drawing.Point(351, 12);
            this.groupBox2.Name = "groupBox2";
            this.groupBox2.Size = new System.Drawing.Size(322, 89);
            this.groupBox2.TabIndex = 13;
            this.groupBox2.TabStop = false;
            this.groupBox2.Text = "Setting";
            // btn_HighlightSet
            this.btn_HighlightSet.Location = new System.Drawing.Point(251, 55);
            this.btn_HighlightSet.Name = "btn_HighlightSet";
            this.btn_HighlightSet.Size = new System.Drawing.Size(58, 23);
            this.btn_HighlightSet.TabIndex = 19;
            this.btn_HighlightSet.Text = "Set";
            this.btn_HighlightSet.UseVisualStyleBackColor = true;
            this.btn_HighlightSet.Click += new System.EventHandler(this.btn_HighlightSet_Click);
            // lb_Detection
            this.lb_Detection.FormattingEnabled = true;
            this.lb_Detection.Location = new System.Drawing.Point(10, 390);
            this.lb_Detection.Name = "lb_Detection";
            this.lb_Detection.Size = new System.Drawing.Size(661, 121);
            this.lb_Detection.TabIndex = 14;
            // label12
            this.label12.AutoSize = true;
            this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
            this.label12.Location = new System.Drawing.Point(7, 374);
            this.label12.Name = "label12";
            this.label12.Size = new System.Drawing.Size(91, 13);
            this.label12.TabIndex = 15;
            this.label12.Text = "Detection info:";
            // OriginalViewer
            this.OriginalViewer.BackColor = System.Drawing.Color.Black;
            this.OriginalViewer.FlipMode = Ozeki.Media.FlipMode.None;
            this.OriginalViewer.FrameStretch = Ozeki.Media.FrameStretch.Uniform;
            this.OriginalViewer.FullScreenEnabled = true;
            this.OriginalViewer.Location = new System.Drawing.Point(10, 110);
            this.OriginalViewer.Name = "OriginalViewer";
            this.OriginalViewer.RotateAngle = 0;
            this.OriginalViewer.Size = new System.Drawing.Size(335, 240);
            this.OriginalViewer.TabIndex = 17;
            this.OriginalViewer.Text = "videoViewerWF1";
            // ProcessedViewer
            this.ProcessedViewer.BackColor = System.Drawing.Color.Black;
            this.ProcessedViewer.FlipMode = Ozeki.Media.FlipMode.None;
            this.ProcessedViewer.FrameStretch = Ozeki.Media.FrameStretch.Uniform;
            this.ProcessedViewer.FullScreenEnabled = true;
            this.ProcessedViewer.Location = new System.Drawing.Point(351, 110);
            this.ProcessedViewer.Name = "ProcessedViewer";
            this.ProcessedViewer.RotateAngle = 0;
            this.ProcessedViewer.Size = new System.Drawing.Size(320, 240);
            this.ProcessedViewer.TabIndex = 18;
            this.ProcessedViewer.Text = "videoViewerWF1";
            // groupBox5
            this.groupBox5.Location = new System.Drawing.Point(12, 12);
            this.groupBox5.Name = "groupBox5";
            this.groupBox5.Size = new System.Drawing.Size(333, 89);
            this.groupBox5.TabIndex = 21;
            this.groupBox5.TabStop = false;
            this.groupBox5.Text = "Connect";
            // stateLabel
            this.stateLabel.AutoSize = true;
            this.stateLabel.Location = new System.Drawing.Point(80, 72);
            this.stateLabel.Name = "stateLabel";
            this.stateLabel.Size = new System.Drawing.Size(0, 13);
            this.stateLabel.TabIndex = 24;
            // label14
            this.label14.AutoSize = true;
            this.label14.Location = new System.Drawing.Point(39, 72);
            this.label14.Name = "label14";
            this.label14.Size = new System.Drawing.Size(35, 13);
            this.label14.TabIndex = 23;
            this.label14.Text = "State:";
            // button_Connect
            this.button_Connect.Enabled = false;
            this.button_Connect.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.5F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238)));
            this.button_Connect.ForeColor = System.Drawing.Color.Black;
            this.button_Connect.Location = new System.Drawing.Point(83, 39);
            this.button_Connect.Name = "button_Connect";
            this.button_Connect.Size = new System.Drawing.Size(71, 23);
            this.button_Connect.TabIndex = 18;
            this.button_Connect.Text = "Connect";
            this.button_Connect.UseVisualStyleBackColor = true;
            this.button_Connect.Click += new System.EventHandler(this.button_Connect_Click);
            // button_Disconnect
            this.button_Disconnect.Enabled = false;
            this.button_Disconnect.Location = new System.Drawing.Point(180, 39);
            this.button_Disconnect.Name = "button_Disconnect";
            this.button_Disconnect.Size = new System.Drawing.Size(69, 23);
            this.button_Disconnect.TabIndex = 22;
            this.button_Disconnect.Text = "Disconnect";
            this.button_Disconnect.UseVisualStyleBackColor = true;
            this.button_Disconnect.Click += new System.EventHandler(this.button_Disconnect_Click);
            // tb_cameraUrl
            this.tb_cameraUrl.Location = new System.Drawing.Point(83, 13);
            this.tb_cameraUrl.Name = "tb_cameraUrl";
            this.tb_cameraUrl.ReadOnly = true;
            this.tb_cameraUrl.Size = new System.Drawing.Size(166, 20);
            this.tb_cameraUrl.TabIndex = 21;
            // label13
            this.label13.AutoSize = true;
            this.label13.Location = new System.Drawing.Point(6, 16);
            this.label13.Name = "label13";
            this.label13.Size = new System.Drawing.Size(71, 13);
            this.label13.TabIndex = 20;
            this.label13.Text = "Camera URL:";
            // button_Compose
            this.button_Compose.Location = new System.Drawing.Point(255, 13);
            this.button_Compose.Name = "button_Compose";
            this.button_Compose.Size = new System.Drawing.Size(69, 23);
            this.button_Compose.TabIndex = 19;
            this.button_Compose.Text = "Compose";
            this.button_Compose.UseVisualStyleBackColor = true;
            this.button_Compose.Click += new System.EventHandler(this.button_Compose_Click);
            // MainForm
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(679, 522);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.Name = "MainForm";
            this.Text = "license Plate Detection";
            this.Load += new System.EventHandler(this.MainForm_Load);



        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.TextBox tb_CannyThreshold;
        private System.Windows.Forms.TextBox tb_CannyThresholdLinking;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.GroupBox groupBox2;
        private System.Windows.Forms.ListBox lb_Detection;
        private System.Windows.Forms.Button btn_HighlightSet;
        private System.Windows.Forms.Label label12;
        private Ozeki.Media.VideoViewerWF OriginalViewer;
        private Ozeki.Media.VideoViewerWF ProcessedViewer;
        private System.Windows.Forms.GroupBox groupBox5;
        private System.Windows.Forms.Button button_Connect;
        private System.Windows.Forms.Button button_Disconnect;
        private System.Windows.Forms.TextBox tb_cameraUrl;
        private System.Windows.Forms.Label label13;
        private System.Windows.Forms.Button button_Compose;
        private System.Windows.Forms.Label stateLabel;
        private System.Windows.Forms.Label label14;


Code 2 - GUI example in C#


By reading through this tutorial you will be able to successfully implement license plate recognition with your C# camera application using the Ozeki Camera SDK. On this webpage you will find a free source code example which is a great help for your implementation. Moreover, this webpage contains helpful documentations to help the understanding.

Related pages


Below you can find the answers for the most frequently asked questions related to this topic:

  1. What kind of developer environment is needed?

    • Microsoft Visual Studio 2010
    • Microsoft .Net Framework 4.0
    • Internet connection
  2. How can I get the URL of the camera?

    You can get the URL from the producer of the camera.

  3. I have not managed to build the solution. How to solve it?

    • Please set the Target framework property of the project to .NET 4.0.
    • You should add the OzekiSDK.dll to the references of the solution.
    • Please import the missing classes.

More information