Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

Grayscale Negative Custom Effect (Nokia Imaging SDK)

From Wiki
Jump to: navigation, search

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

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

Contents

Introduction

The GrayscaleNegativeEffect allows you to change an image into a negative black and white image, very similar to the Nokia Imaging SDK's inbuilt GrayscaleNegativeFilter. 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 negative look provided by the SDK, the GrayscaleNegativeEffect should not be used in place of the inbuilt GrayscaleNegativeFilter. 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 GrayscaleNegativeEffect custom effect is provided below (toggle "Expand") and also in a ready-to-use file here: GrayscaleNegativeEffect.cs.

GrayscaleNegativeEffect.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. //                                      and handling static images
  7. // ============================================================================
  8.  
  9. using Nokia.Graphics.Imaging;
  10. using System;
  11.  
  12. namespace NISDKExtendedEffects.ImageEffects
  13. {
  14.     public class GrayscaleNegativeEffect : CustomEffectBase
  15.     {
  16.         private double m_RedPercentage = 0.2126;
  17.         private double m_GreenPercentage = 0.7152;
  18.         private double m_BluePercentage = 0.0722;
  19.  
  20.         public GrayscaleNegativeEffect(IImageProvider source, double red = 0.2126, double green = 0.7152, double blue = 0.0722)
  21.             : base(source)
  22.         {
  23.             // Self correct negative percentages to just zero them out and ensure no percentage goes above 1.0 (100%)
  24.             m_RedPercentage = (red < 0.00) ? 0.00 : (red > 1.00) ? 1.00 : red;
  25.             m_GreenPercentage = (green < 0.00) ? 0.00 : (green > 1.00) ? 1.00 : green;
  26.             m_BluePercentage = (blue < 0.00) ? 0.00 : (blue > 1.00) ? 1.00 : blue;
  27.         }
  28.  
  29.         protected override void OnProcess(PixelRegion sourcePixelRegion, PixelRegion targetPixelRegion)
  30.         {
  31.             var sourcePixels = sourcePixelRegion.ImagePixels;
  32.             var targetPixels = targetPixelRegion.ImagePixels;
  33.  
  34.             sourcePixelRegion.ForEachRow((index, width, position) =>
  35.             {
  36.                 for (int x = 0; x < width; ++x, ++index)
  37.                 {
  38.                     uint pixel = sourcePixels[index]; // get the current pixel
  39.  
  40.                     if (pixel > 0) // Only manipulate pixels that have color
  41.                     {
  42.                         uint red = (pixel & 0x00ff0000) >> 16; // red color component
  43.                         uint green = (pixel & 0x0000ff00) >> 8; // green color component
  44.                         uint blue = pixel & 0x000000ff; // blue color component
  45.  
  46.                         // Calculate the weighted average of all the color components
  47.                         // REFERENCE: http://en.wikipedia.org/wiki/Grayscale
  48.                         uint grayscaleAverage = (uint)Math.Max(0, Math.Min(255, (
  49.                             m_RedPercentage * red +
  50.                             m_GreenPercentage * green +
  51.                             m_BluePercentage * blue)));
  52.  
  53.                         // Assign the result to each component
  54.                         red = green = blue = grayscaleAverage;
  55.  
  56.                         // Reassembling each component back into a pixel
  57.                         pixel = 0xff000000 | (red << 16) | (green << 8) | blue;
  58.  
  59.                         // Flip the bits of the pixel to create the negative effect
  60.                         pixel = ~pixel;
  61.  
  62.                         // Assign the pixels back by each component so we can ensure the alpha channel 
  63.                         // is at 255. Otherwise the image will not be visible if it is static.
  64.                         targetPixels[index] = 0xff000000 | (pixel & 0x00ff0000) | (pixel & 0x0000ff00) | pixel & 0x000000ff;
  65.                     }
  66.                 }
  67.             });
  68.         }
  69.     }
  70. }

Testing

The Test Apps for Viewing Custom Filters allow you to cycle through a number of different custom effects (including GrayscaleNegativeEffect) 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 GrayscaleNegativeEffect is less efficient than the Nokia Imaging SDK's inbuilt GrayscaleNegativeFilter.

Device GrayscaleNegativeEffect GrayscaleNegativeFilter
Lumia 920 8-9 FPS 17-18 FPS

The GrayscaleNegativeEffect class runs at about 8-9 FPS (frames per second). The inbuilt GrayscaleNegativeFilter 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

This filter technique is a mash up of the GrayscaleEffect and the NegativeEffect. First you calculate the grayscale of the 3 color components with a weighted average algorithm. Then the result is assigned to all 3 colors, and they are reassembled back into a pixel. Finally you flip the bits for the negative effect and ensure the alpha channel is at 255 when creating the final pixel.

  1. // Calculate the weighted average 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;
  5.  
  6. // Reassembling each component back into a pixel
  7. pixel = 0xff000000 | (red << 16) | (green << 8) | blue;
  8.  
  9. // Flip the bits of the pixel to create the negative effect
  10. pixel = ~pixel;
  11.  
  12. // Assign the pixels back by each component so we can ensure the alpha channel 
  13. // is at 255. Otherwise the image will not be visible if it is static.
  14. targetPixels[index] = 0xff000000 | (pixel & 0x00ff0000) | (pixel & 0x0000ff00) | pixel & 0x000000ff;

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 negative 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 GrayscaleNegativeEffect(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 GrayscaleNegativeEffect(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 06:36.
215 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.

×