How to Build Windows Desktop Barcode and QR Code Scanner in .NET 6
Tags: dotnet, qrcode, winform, camera
Published: 2022-05-13 06:21:43

A few weeks ago, I released a .NET 6 [BarcodeQRCodeSDK](https://www.nuget.org/packages/BarcodeQRCodeSDK/) package implemented with [Dynamsoft C/C++ Barcode SDK](https://www.dynamsoft.com/barcode-reader/sdk-desktop-server/). This article demonstrates how to build a Windows desktop barcode and QR code scanner using .NET WinForms, OpenCvSharp, and BarcodeQRCodeSDK.


## Install Dependencies
* [OpenCvSharp4.Windows](https://www.nuget.org/packages/OpenCvSharp4.Windows/)

    Used to capture video stream from webcam.

    ```bash
    dotnet add package OpenCvSharp4.Windows
    ```
* [OpenCvSharp4.Extensions](https://www.nuget.org/packages/OpenCvSharp4.Extensions/)
    
    Used to convert OpenCvSharp Mat to Bitmap.

    ```bash
    dotnet add package OpenCvSharp4.Extensions
    ```

* [BarcodeQRCodeSDK](https://www.nuget.org/packages/BarcodeQRCodeSDK/)
    
    Used to decode barcode and QR code from `Mat` or `Bitmap`.    

    ```bash
    dotnet add package BarcodeQRCodeSDK
    ```


## Steps to Develop Windows Desktop Barcode and QR Code Scanner in .NET 6
Let's build the application from scratch.

### Step 1: Create Windows Forms App:
We can quickly create a new project and install dependencies via the following commands in terminal:

```bash
dotnet new winforms -o desktop-barcode-qrcode-scanner
cd desktop-barcode-qrcode-scanner
dotnet add package OpenCvSharp4.Windows
dotnet add package OpenCvSharp4.Extensions
dotnet add package BarcodeQRCodeSDK
```

Then import relevant namespaces and classes in `Form1.cs` file:
    
```csharp
using Dynamsoft;
using Result = Dynamsoft.BarcodeQRCodeReader.Result;
using OpenCvSharp;
using OpenCvSharp.Extensions;
```

In constructor, we initialize `BarcodeQRCodeReader` and `VideoCapture` instances. You can apply for a [30-day trial license](https://www.dynamsoft.com/customer/license/trialLicense?product=dbr) to activate the barcode SDK.
    
```csharp
public Form1()
{
    InitializeComponent();
    BarcodeQRCodeReader.InitLicense("DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="); // Get a license key from https://www.dynamsoft.com/customer/license/trialLicense?product=dbr
    reader = BarcodeQRCodeReader.Create();
    capture = new VideoCapture(0);
}
```

### Step 2: Add UI Components
Add a `PictureBox` and a `Button` to the form designer. The `PictureBox` is used to display the camera video stream, and the `Button` is used to start and stop scanning barcode and QR code from video frames. If you use `Visual Studio Code`, you need to manually add the components in `Form1.Designer.cs`. Whereas if you use `Visual Studio`, you can drag the components from toolbox to the form. 

```csharp
this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pictureBox1.Location = new System.Drawing.Point(13, 12);
this.pictureBox1.Margin = new System.Windows.Forms.Padding(4);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(640, 640);
this.pictureBox1.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;

this.button1.AllowDrop = true;
this.button1.AutoEllipsis = true;
this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.button1.Location = new System.Drawing.Point(667, 12);
this.button1.Margin = new System.Windows.Forms.Padding(4);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(333, 79);
this.button1.TabIndex = 1;
this.button1.Text = "Camera Scan";
this.button1.UseVisualStyleBackColor = true;
this.button1.Click += new System.EventHandler(this.button_Click);
```

The button click event handler is implemented as follows:

```csharp
private void button_Click(object sender, EventArgs e)
{
    if (!capture.IsOpened())
    {
        MessageBox.Show("Failed to open video stream or file");
        return;
    }

    if (button2.Text == "Camera Scan")
    {
        StartScan();
    }
    else {
        StopScan();
    }
}
```

### Step3: Display Camera Video Stream in PictureBox
To avoid blocking the UI thread, we capture video stream in a background thread. The type of the captured image is `Mat`, which cannot be passed to `PictureBox` directly. So we use `BitmapConverter` to convert `Mat` to `Bitmap`.
    
```csharp
private void StartScan() {
    button2.Text = "Stop";
    isCapturing = true;
    thread = new Thread(new ThreadStart(FrameCallback));
    thread.Start();
}

private void StopScan() {
    button2.Text = "Camera Scan";
    isCapturing = false;
    if (thread != null) thread.Join();
}

private void FrameCallback() {
    while (isCapturing) {
        Mat frame = new Mat();
        capture.Read(frame);
        pictureBox1.Image = BitmapConverter.ToBitmap(frame);
    }
}
```

### Step4: Decode Barcode and QR Code from Camera Video Stream
Here is the code used for decoding barcode and QR code from `Mat`:

```csharp
Result[]? results = reader.DecodeBuffer(mat.Data, mat.Cols, mat.Rows, (int)mat.Step(), BarcodeQRCodeReader.ImagePixelFormat.IPF_RGB_888);
```

As we get the results, we draw text and contours on the image:

```csharp
private Bitmap DecodeMat(Mat mat)
{
    Result[]? results = reader.DecodeBuffer(mat.Data, mat.Cols, mat.Rows, (int)mat.Step(), BarcodeQRCodeReader.ImagePixelFormat.IPF_RGB_888);
    if (results != null)
    {
        foreach (Result result in results)
        {
            string output = "Text: " + result.Text + Environment.NewLine + "Format: " + result.Format1 + Environment.NewLine;
            textBox1.AppendText(output);
            textBox1.AppendText(Environment.NewLine);
            int[]? points = result.Points;
            if (points != null)
            {
                OpenCvSharp.Point[] all = new OpenCvSharp.Point[4];
                int xMin = points[0], yMax = points[1];
                all[0] = new OpenCvSharp.Point(xMin, yMax);
                for (int i = 2; i < 7; i += 2)
                {
                    int x = points[i];
                    int y = points[i + 1];
                    OpenCvSharp.Point p = new OpenCvSharp.Point(x, y);
                    xMin = x < xMin ? x : xMin;
                    yMax = y > yMax ? y : yMax;
                    all[i / 2] = p;
                }
                OpenCvSharp.Point[][] contours = new OpenCvSharp.Point[][] { all };
                Cv2.DrawContours(mat, contours, 0, new Scalar(0, 0, 255), 2);
                if (result.Text != null) Cv2.PutText(mat, result.Text, new OpenCvSharp.Point(xMin, yMax), HersheyFonts.HersheySimplex, 1, new Scalar(0, 0, 255), 2);
            }
        }
    }
    else
    {
        textBox1.AppendText("No barcode detected!" + Environment.NewLine);
    }

    Bitmap bitmap = BitmapConverter.ToBitmap(mat);
    return bitmap;
}
```

Update the following line to show barcode and QR code results:

```diff
- pictureBox1.Image = BitmapConverter.ToBitmap(frame);
+ pictureBox1.Image = DecodeMat(mat);
```

Now, we can run the program to scan barcode and QR code.

```bash
dotnet run
```

![command-line .NET barcode and QR code reader](https://www.dynamsoft.com/codepool/img/2022/05/dotnet-windows-desktop-qr-code-scanner.png)

## Source Code
[https://github.com/yushulx/dotnet-barcode-qr-code-sdk/tree/main/example/desktop-gui](https://github.com/yushulx/dotnet-barcode-qr-code-sdk/tree/main/example/desktop-gui)

Download the source code with Subversion:

```bash
svn checkout https://github.com/yushulx/dotnet-barcode-qr-code-sdk/trunk/example/desktop-gui
```