×
Namespaces

Variants
Actions

Grayscale Custom Effect (Nokia Imaging SDK)

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how to create a Grayscale Effect similar to the GrayscaleFilter in the Nokia Imaging SDK. It is provided for educational purposes rather than developer re-use, if you want the grayscale look provided by the SDK.

SignpostIcon XAML 40.png
WP Metro Icon WP8.png
Article Metadata
Tested with
Devices(s): Nokia Lumia 920
Compatibility
Platform(s):
Windows Phone 8
Dependencies: Nokia Imaging SDK 1.0
Article
Created: Rob.Kachmar (01 Feb 2014)
Last edited: Rob.Kachmar (18 Feb 2014)

Contents

Introduction

The GrayscaleEffect allows you to change a color image into a black and white image, very similar to the Nokia Imaging SDK's inbuilt GrayscaleFilter. The main differences are that it is implemented as a custom effect, it is less efficient, and you can use your own grayscale weighted average algorithm.

Warning.pngWarning: If you want the grayscale look provided by the SDK, the GrayscaleEffect should not be used in place of the inbuilt GrayscaleFilter. It is provided for educational purposes - to make it easier to understand the techniques used so that more complex filters can be created.

The article provides links to the filter source code and test code, an explanation of how the filter works, and code snippets showing how it is used.

Source code

Full source code for the GrayscaleEffect custom effect is provided below (toggle "Expand") and also in a ready-to-use file here: GrayscaleEffect.cs.

GrayscaleEffect.cs (03 Feb 2014)

  1. // ============================================================================
  2. // DATE        AUTHOR                   DESCRIPTION
  3. // ----------  -----------------------  ---------------------------------------
  4. // 2014.01.15  Rob.Kachmar              Initial creation
  5. // 2014.02.03  Rob.Kachmar              Allow adjustments to weighted avg algo
  6. // ============================================================================
  7.  
  8. using Nokia.Graphics.Imaging;
  9. using System;
  10.  
  11. namespace NISDKExtendedEffects.ImageEffects
  12. {
  13.     public class GrayscaleEffect : CustomEffectBase
  14.     {
  15.         private double m_RedPercentage = 0.2126;
  16.         private double m_GreenPercentage = 0.7152;
  17.         private double m_BluePercentage = 0.0722;
  18.  
  19.         public GrayscaleEffect(IImageProvider source, double red = 0.2126, double green = 0.7152, double blue = 0.0722)
  20.             : base(source)
  21.         {
  22.             // Self correct negative percentages to just zero them out and ensure no percentage goes above 1.0 (100%)
  23.             m_RedPercentage = (red < 0.00) ? 0.00 : (red > 1.00) ? 1.00 : red;
  24.             m_GreenPercentage = (green < 0.00) ? 0.00 : (green > 1.00) ? 1.00 : green;
  25.             m_BluePercentage = (blue < 0.00) ? 0.00 : (blue > 1.00) ? 1.00 : blue;
  26.         }
  27.  
  28.         protected override void OnProcess(PixelRegion sourcePixelRegion, PixelRegion targetPixelRegion)
  29.         {
  30.             var sourcePixels = sourcePixelRegion.ImagePixels;
  31.             var targetPixels = targetPixelRegion.ImagePixels;
  32.  
  33.             sourcePixelRegion.ForEachRow((index, width, position) =>
  34.             {
  35.                 for (int x = 0; x < width; ++x, ++index)
  36.                 {
  37.                     // 16-17 FPS with built-in GrayscaleFilter() and 11 FPS with this technique on Lumia 920
  38.                     uint currentPixel = sourcePixels[index]; // get the current pixel
  39.                     uint red = (currentPixel & 0x00ff0000) >> 16; // red color component
  40.                     uint green = (currentPixel & 0x0000ff00) >> 8; // green color component
  41.                     uint blue = currentPixel & 0x000000ff; // blue color component
  42.  
  43.                     // Calculate the weighted average of all the color components
  44.                     // REFERENCE: http://en.wikipedia.org/wiki/Grayscale
  45.                     uint grayscaleAverage = (uint)Math.Max(0, Math.Min(255, (
  46.                         m_RedPercentage * red +
  47.                         m_GreenPercentage * green +
  48.                         m_BluePercentage * blue)));
  49.  
  50.                     // Assign the result to each component
  51.                     red = green = blue = grayscaleAverage;
  52.  
  53.                     uint newPixel = 0xff000000 | (red << 16) | (green << 8) | blue; // reassembling each component back into a pixel
  54.                     targetPixels[index] = newPixel; // assign the newPixel to the equivalent location in the output image
  55.                 }
  56.             });
  57.         }
  58.     }
  59. }

Testing

The Test Apps for Viewing Custom Filters allow you to cycle through a number of different custom effects (including GrayscaleEffect) 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

The GrayscaleEffect is less efficient than the Nokia Imaging SDK's inbuilt GrayscaleFilter.

Device GrayscaleEffect GrayscaleFilter
Lumia 920 8-9 FPS 17-18 FPS

The GrayscaleEffect class runs at about 8-9 FPS (frames per second). The inbuilt GrayscaleFilter runs at around 17-18 FPS.

The Real Time Filter Demo test project is configured so that the inbuilt and custom effect run side-by-side for easy comparison.


Code walkthrough

The technique for changing a color image into black and white involves calculating the average of each of the RGB color components. However, it is not a basic average calculation where you add all the colors together and divide by 3. Instead we need to apply a special weighted average algorithm to the individual color components, and then assign the result back to each component.

  1. // Calculate the weighted avearge of all the color components and assign the result to each component
  2. // REFERENCE: http://en.wikipedia.org/wiki/Grayscale
  3. uint grayscaleAverage = (uint)(0.2126 * red + 0.7152 * green + 0.0722 * blue);
  4. red = green = blue = grayscaleAverage;

Note.pngNote: The algorithm above actually produces a slightly different look than the SDK. As soon as we figure out an algorithm that matches the SDK, we'll update the default.

To go beyond the SDK, we simply added some parameters to the class to allow adjusting the intensity of each color. Now you can choose your own weighted average algorithm for a fully customized grayscale look.

  1. private double m_RedPercentage = 0.2126;
  2. private double m_GreenPercentage = 0.7152;
  3. private double m_BluePercentage = 0.0722;
  4.  
  5. public GrayscaleEffect(IImageProvider source, double red = 0.2126, double green = 0.7152, double blue = 0.0722)
  6.     : base(source)
  7. {
  8.     // Self correct negative percentages to just zero them out and ensure no percentage goes above 1.0 (100%)
  9.     m_RedPercentage = (red < 0.00) ? 0.00 : (red > 1.00) ? 1.00 : red;
  10.     m_GreenPercentage = (green < 0.00) ? 0.00 : (green > 1.00) ? 1.00 : green;
  11.     m_BluePercentage = (blue < 0.00) ? 0.00 : (blue > 1.00) ? 1.00 : blue;
  12. }
  13. ...
  14.     uint grayscaleAverage = (uint)Math.Max(0, Math.Min(255, (
  15.         m_RedPercentage * red +
  16.         m_GreenPercentage * green +
  17.         m_BluePercentage * blue)));
  18. ...


Using the filter

Drop an image control into your XAML.

  1. <Image x:Name="FilterEffectImage" Width="800" Height="480" Stretch="Fill" Grid.RowSpan="2" />

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

  1. // Initialize a WriteableBitmap with the dimensions of the XAML image control
  2. WriteableBitmap writeableBitmap = new WriteableBitmap((int)FilterEffectImage.Width, (int)FilterEffectImage.Height);
  3.  
  4. // Example: Accessing an image stream within a standard photo chooser task callback
  5. // http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh394019(v=vs.105).aspx
  6. //using (var imageStream = new StreamImageSource(e.ChosenPhoto))
  7.  
  8. // Example: Accessing an image stream from a sample picture loaded with the project in a folder called "Pictures"
  9. var resource = App.GetResourceStream(new Uri(string.Concat("Pictures/", "sample_photo_08.jpg"), UriKind.Relative));
  10. using (var imageStream = new StreamImageSource(resource.Stream))
  11. {
  12.     // Applying the custom filter effect to the image stream
  13.     using (var customEffect = new GrayscaleEffect(imageStream))
  14.     {
  15.         // Rendering the resulting image to a WriteableBitmap
  16.         using (var renderer = new WriteableBitmapRenderer(customEffect, writeableBitmap))
  17.         {
  18.             // Applying the WriteableBitmap to our xaml image control
  19.             FilterEffectImage.Source = await renderer.RenderAsync();
  20.         }
  21.     }
  22. }


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 this custom effect implementation unlocks some of the mystery behind 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.

This page was last modified on 18 February 2014, at 09:35.
77 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×