Category Archives: programming

Object detection with a Yolov5 computer vision model in a .NET application

Introduction

In the previous post I showed how I trained a Yolov5 computer vision model to recognize Starfleet combadges, and how Yolo’s own command line tool can be used to recognize them in a video.
That is nice for testing the capabilities of the model, however has no practical use.

We can of course use ultralytics python library to use the model in a Python application, but there are times when we’d like to use it some other environment. Maybe we have a legacy application that we have to maintain, and we’d like to add object detection features to it. Whatever the reason fortunately there’s a solution.

This post will show how to do this in C# .NET with an example Windows Forms application.
The reason for this is because I had to create such an application for a course.

Exporting the model

First and foremost we need to export the model to a format that can be used in another environment.

Fortunately there’s an industry standard format for this: ONNX
which stands for Open Neural Network eXchange, and is supported by many libraries.

Yolo also comes with a built-in tool that can export our model in this format, with a simple command:

python export.py --weights D:\projects\python\yolov5\yolov5\runs\train\exp2\weights\best.pt --include onnx

We can visualize our exported ONNX with a web based tool at https://netron.app

Setting up the project

Our little example project is going to use .NET 6 as the dependencies require this.
Because of this and because of Windows Forms we need (or at least strongly suggested to use) Visual Studio 2022.

In the New Project dialog we need to select Windows Forms App (the one that does NOT say .NET framework), and select .NET 6 as a framework.

Installing dependencies

Next we’re going to install the libraries that we need.

The library we’re going to use the model with is Yolov5Net.
It is built on ML.NET which makes that a dependency too. It has a CPU and a GPU version, but we’re going to stick with the CPU version for this example.
Yolov5Net also depends on ImageSharp, and we’re going to use ImageSharp Draw to manipulate images.

Fortunately we can easily install all this with NuGet commands in the Package Manager Console ( Tools – NuGet Package Manager – Package Manager Console in VS2022 ).

Install-Package Yolov5Net -Version 1.1.0
Install-Package Microsoft.ML.OnnxRuntime -Version 1.14.1
Install-Package SixLabors.ImageSharp.Drawing -Version 2.1.2

Building the application

First we need to create a model definition record for our Yolo based model.
This record contains hyperparameters like the labels we use, the image size used by the model, etc.

For this we need to create a new record (it is named ComBadgeModel in my example app) that inherits from YoloModel base record.
If we used the default parameters for training and only changed the labels then
we can just copy YoloCocoP5Model and then just change these 3 parameters:

  • dimensions
  • labels
  • output

The labels we defined before training the model.
The dimensions is exactly the number of labels + 5.
As for the name of the output we can get it by examining the model with the netron.app web based tool.

We also need a form, with a browse button and a PictureBox.
The browse button will trigger a file selection dialog where we can select the image file we’d like to recognize objects on.
The PictureBox will show this image with bounding boxes around the recognized objects.

In the form’s class we have some setting up to do:

Load a font that we want to use:

Font font = new Font(new FontCollection().Add("C:/Windows/Fonts/consola.ttf"), 12);

Load our computer vision model:

YoloScorer scorer = new YoloScorer("best.onnx");

Once this is done we implement the rest of the application logic in the click event handler of the browse button:

Get the filename from the user through a dialog:

OpenFileDialog ofd = new OpenFileDialog();ofd.Filter = "JPG (.JPG)|.jpg";
DialogResult result = ofd.ShowDialog();
if( result != DialogResult.OK )
{
return;
}

Then we can load the image:

Image img = await Image.LoadAsync(fileName);

Once we have the image, we can detect objects on the loaded image:

List predictions = scorer.Predict(img);

Then we can label the detected objects by drawing bounding boxes and drawing the label and the confidence value:

foreach (YoloPrediction prediction in predictions)
{
 double score = Math.Round(prediction.Score, 2);
 
 float x = prediction.Rectangle.Left - 3;
 float y = prediction.Rectangle.Top - 23;

 img.Mutate(a => a.DrawPolygon(
     new SolidPen(prediction.Label.Color, 1),
     new PointF(prediction.Rectangle.Left, prediction.Rectangle.Top),
     new PointF(prediction.Rectangle.Right, prediction.Rectangle.Top),
     new PointF(prediction.Rectangle.Right, prediction.Rectangle.Bottom),
     new PointF(prediction.Rectangle.Left, prediction.Rectangle.Bottom)
     )
 );

 img.Mutate(
    a => a.DrawText($"{prediction.Label.Name} ({score})",
    font, prediction.Label.Color, new PointF(x, y)));
}

Finally we need to display the labeled image in the PictureBox.
In order to do that we have to convert the ImageSharp Image to System.Drawing.Image.
For that we have to extend the ImageSharp image class with some extensions methods that can be found here and should be added to a static class.

Once this is done we can actually convert and display the image in the PictureBox:

pictureBox1.BackgroundImage = img.ToArray().ToDrawingImage();

The result

The application will look something like this in action:

You can find a full example on Github.

Troubleshooting

If you get a System.InvalidOperationException: Sequence contains no matching element exception then you should check that your model parameters are correct.

Implemented Hardcore Mode in Arcemu

I read an article on wccftech about Blizzard planning on implementing Hardcore Mode.

Basically this means that when a player dies the character is deleted.

I thought to myself, sounds easy to implement, so I’ve done it!

It is available now as an optional feature.

Yet another Arcemu Python Engine video

This video shows a scripted NPC (Guard Roberts) during the human quest Garments of the Light.

You get quest credit for the following:

  • Heal Guard Roberts with Lesser Heal rank 2
  • Buff Guard Roberts with Power Word: Fortitude rank 1

When healing he stands up, turns towards you, bows, and thanks you.

After buffing him, he cheers, thanks you again and goes on his way saying his farewells. Then despawns and respawns.

Up until now this wasn’t working in Arcemu, making the quest unfinishable.

The script can be found here: https://github.com/arcemu/arcemu/blob/master/src/scripts/pythonscripts/prod/guard_roberts.py

…and here’s a video:

Arcemu Python Engine showoff video #1

I’ve recorded and uploaded a video to show off some of what can be done with Python NPC scripts in Arcemu.

I think it’s quite fun.

The script itself can be found here:
https://github.com/arcemu/arcemu/blob/master/src/scripts/pythonscripts/dev/mohawk.py

Arcemu Python Engine (APE)

I’ve just pushed a new Arcemu scripting engine’s code (195 commits) to Arcemu’s main code repository.

Lately I’ve had to deal a lot with Python at work, and I thought it would be a nice experiment to add Python scripting support to Arcemu, so during last year’s Christmas I started implementing it.
To make this work first I had to get and compile Python with CMake and both Visual Studio 2010 as Arcemu still supports that.
I made my version of Python 3.4.10 separately available on my Github page as well, so if anyone would like to play around with that they can find it here:

https://github.com/dfighter1985/python34

After that I had to learn to both extend and embed Python. You can find some details about those in the official documentation of Python:
https://docs.python.org/3/extending/extending.html
https://docs.python.org/3/extending/embedding.html

While this new Arcemu scripting engine is still experimental and not at all complete it can still be fun for others too. That’s why I decided to push it today.

The “release” if you will includes some sample Python scripts that I used for testing functionality that I implemented. You can find these here:
https://github.com/arcemu/arcemu/tree/master/src/scripts/pythonscripts

If you’d like to try make sure to set BUILD_PYTHONENGINE and INSTALL_EXAMPLE_PYTHON_SCRIPTS to True in CMake when building the code.
I will continue to extend it and will probably write about it more at a later time.

Until then have fun!

 

undefined reference to `getentropy’

I was building software for an older target platform (Ubuntu 16.04 Xenial), when I got this one.

After some research I’ve figured that as the name suggests this call simply returns some random bytes, and the error occurs because the target platform has an older GLIBC version (2.23) than necessary (2.25) for this system call to be available.

The solution in my case was simply modifying the header of the software before re-building as it had alternative implementations for the call that was using getentropy. Had this not been the case I could have just emulated the call…

CMake Error: CMake can not determine linker language for target

This is yet another error message that can be quite misleading. In my case it was caused by a simple typo.

I wrote:

ADD_LIBRARY( yadayada STATIC ${yadayada_source} )

…instead of:

ADD_LIBRARY( yadayada STATIC ${yadayada_sources} )

Which means that accidentally I was trying to get CMake to generate a build solution for linking… nothing?

error MSB8013: This project doesn’t contain the Configuration and Platform combination of xxxx

Visual Studio error messages can be quite misleading.

I got this error message while trying to build a C project with Visual Studio, and despite what the message suggests I had the right configuration / platform combination set up.

As it turns out however the project had some pre-build and pre-link steps set up, which failed because I removed the binaries (on purpose) that it wanted to run.

So for me the solution was just to remove those pre-build and pre-link steps as I didn’t need them anymore.

How to handle Heritrix stale file handle exception

Heritrix is the Internet Archive-s web archival software, essentially a web crawling bot that takes a list of web sites, and saves them as ARC/WARC files in order to create a web archive like the one at archive.org.

Sometimes, like every other piece of software, it can produce error messages that might not be trivial.

One of them is the following:

Caused by: java.nio.file.FileSystemException: /path/to/file: Stale file handle

Other than the exception, you might face the following problems:

  • The REST API returns empty responses for certain jobs, instead of their status.
  • The web UI shows a long chain of exception (including Stale file handle FileSystemException as the root cause) when navigating to the job’s status page

Cause:
One possible cause this issue is that Heritrix has a file open that is on a remote filesystem, and during Heritrix’s run the connection to that filesystem broke due to a network outage for example.

Solution:

  • Safely shut down Heritrix’s other jobs ( pause, checkpoint )
  • Restart Heritrix

After the restart if you continue the jobs they will be fine, and the error is gone.

Arcemu Dungeon Finder

Arcemu has been a pet project of mine for a long time.

Lately whenever I could find a little time, I’ve been working on implementing a feature that has been missing from Arcemu for a very long time: Dungeon Finder

While it’s far from done, today I enabled the periodic automatic LFG queue update, which means if the LFG is enabled in the config, you no longer need to use a GM command to update the queue and assemble groups, it will be done automagically.

Here’s a list of use cases that I’ve mostly implemented:

  • Joining / leaving queues for both specific and random dungeons
  • Assembling groups for those dungeons
  • Group ready check when a group is ready
  • Requeing the group if someone fails the ready check
  • Teleporting in and out of dungeons

There’s still tons of work (new features, more checks, constant refactoring) to be done, but it’s good for a hobby.

Either way, here’s a little video about queuing, ready checking, and teleporting: