Otsu Threshold

Introduction

The Otsu Threshold is one of binarization algorithms, named after its inventor Nobuyuki Otsu. This method involves iterating through all the possible threshold values and calculating a measure of spread for the pixel levels each side of the threshold, i.e. the pixels that either fall in foreground or background. The aim is to find the threshold value where the sum of foreground and background spreads is at its minimum.

Original

Otsu Threshold Effect

Source code

Full source code for the OtsuThresholdEffect custom effect is provided in a ready-to-use file here: OtsuThresholdEffect.cs.

Testing

The Test Apps for Viewing Custom Filters allow you to cycle through a number of different custom effects (including OtsuThresholdEffect) and apply them to the real-time camera preview or a static image.

In addition, the custom effect file can be dropped into your own project, and used as described in the section Using the filter.

Pre-requisites

Performance

Device : Lumia 920 OtsuThresholdEffect : 9-10 FPS

The OtsuThresholdEffect class runs 9-10 FPS (Frames Per Second).

Code walkthrough

First, Otsu Threshold computes histogram data of a grayscale image. When computing histogram data, we don't need to iterate every pixel. Iterating a pixel by three gives enough data and higher performance. Also following code uses luminance formula to convert iterated pixels to grayscale.

// simply computes the grayscale image histogram
sourcePixelRegion.ForEachRow((index, width, pos) =>
{
    for (int x = 0; x < width; x += 3, index += 3)
    {
        uint currentPixel = sourcePixelRegion.ImagePixels[index];

        uint red = (currentPixel & 0x00ff0000) >> 16; // red color component
        uint green = (currentPixel & 0x0000ff00) >> 8; // green color component
        uint blue = currentPixel & 0x000000ff; // blue color component

        //luminance formula
        var p = (byte)(0.21 * red + 0.71 * green + 0.07 * blue);
        hist[p]++;
    }
});

Then, Otsu's method computes all possible threshold values and calculating a measure of spread for the pixel levels each side of the threshold. For more information about algorithm

// loop through all possible t values and maximize between class variance
for (k = 1; k != 255; k++)
{
    p1 = Px(0, k, hist);
    p2 = Px(k + 1, 255, hist);
    p12 = p1 * p2;
    if (p12 == 0)
        p12 = 1;
    float diff = (Mx(0, k, hist) * p2) - (Mx(k + 1, 255, hist) * p1);
    vet[k] = (float)diff * diff / p12;
}

_threshold = (byte)findMax(vet, 256);

Finally, simple binarization method binarize image by using calculated threshold value.

// simple routine for thresholding
sourcePixelRegion.ForEachRow((index, width, pos) =>
{
    for (int x = 0; x < width; ++x, ++index)
    {
        uint currentPixel = sourcePixelRegion.ImagePixels[index];

        uint red = (currentPixel & 0x00ff0000) >> 16; // red color component
        uint green = (currentPixel & 0x0000ff00) >> 8; // green color component
        uint blue = currentPixel & 0x000000ff; // blue color component

        if ((byte)(0.21 * red + 0.71 * green + 0.07 * blue) < _threshold)
            sourcePixelRegion.ImagePixels[index] = black;
        else
            sourcePixelRegion.ImagePixels[index] = white;
    }
});

Using the filter

Drop an image control into your XAML.

Use this code to apply the filter effect to your chosen image and assign it to the XAML image control.

// Initialize a WriteableBitmap with the dimensions of the XAML image control
WriteableBitmap writeableBitmap = new WriteableBitmap((int)FilterEffectImage.Width, (int)FilterEffectImage.Height);

// Example: Accessing an image stream within a standard photo chooser task callback
// http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394019(v=vs.105).aspx
//using (var imageStream = new StreamImageSource(e.ChosenPhoto))

// Example: Accessing an image stream from a sample picture loaded with the project in a folder called "Pictures"
var resource = App.GetResourceStream(new Uri(string.Concat("Pictures/", "sample_photo_08.jpg"), UriKind.Relative));
using (var imageStream = new StreamImageSource(resource.Stream))
{
    // Applying the custom filter effect to the image stream
    using (var customEffect = new OtsuThresholdEffect(imageStream))
    {
        // Rendering the resulting image to a WriteableBitmap
        using (var renderer = new WriteableBitmapRenderer(customEffect, writeableBitmap))
        {
            // Applying the WriteableBitmap to our xaml image control
            FilterEffectImage.Source = await renderer.RenderAsync();

            // while debugging, it writes calculated threshold value to Output Window
            System.Diagnostics.Debug.WriteLine(customEffect.Threshold);
        }
    }
}

License

The code has been released with the standard MIT License, and can be viewed in the Github project here.

Summary

Hopefully you've enjoyed seeing how to binarize an image using Otsu's method. This is just one of many things you can do with the amazing Nokia Imaging SDK.

As with all articles on the wiki, you are welcome to contribute any changes to this code that would improve the quality, whether it be additional features or improving its efficiency.

Comments