×
Namespaces

Variants
Actions

Mirror Custom Effect (Nokia Imaging SDK)

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how to create a Mirror Effect similar to the MirrorFilter in the Nokia Imaging SDK. It is provided for educational purposes rather than developer re-use.

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

Contents

Introduction

The MirrorEffect creates a mirrored image that is very similar to the Nokia Imaging SDK's inbuilt MirrorFilter. The main differences are that it is implemented as a custom effect, it is less efficient, and a vertical effect can be achieved without requiring a combination of multiple filters as is needed with the SDK.

Warning.pngWarning: The MirrorEffect should not be used in place of the inbuilt MirrorFilter. 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 MirrorEffect custom effect is provided below (toggle "Expand") and also in a ready-to-use file here: MirrorEffect.cs.

MirrorEffect.cs (03 Feb 2014)

  1. // ============================================================================
  2. // DATE        AUTHOR                   DESCRIPTION
  3. // ----------  -----------------------  ---------------------------------------
  4. // 2014.01.15  Rob.Kachmar              Initial creation
  5. // ============================================================================
  6.  
  7. using Nokia.Graphics.Imaging;
  8. using System;
  9.  
  10. namespace NISDKExtendedEffects.ImageEffects
  11. {
  12.     public class MirrorEffect : CustomEffectBase
  13.     {
  14.         public enum MirrorType
  15.         {
  16.             Horizontal = 0,
  17.             Vertical = 1
  18.         }
  19.  
  20.         MirrorType m_MirrorType = MirrorType.Horizontal;
  21.  
  22.         public MirrorEffect(IImageProvider source, MirrorType mirrorType = MirrorType.Horizontal) : base(source)
  23.         {
  24.             m_MirrorType = mirrorType;
  25.         }
  26.  
  27.         protected override void OnProcess(PixelRegion sourcePixelRegion, PixelRegion targetPixelRegion)
  28.         {
  29.             var sourcePixels = sourcePixelRegion.ImagePixels;
  30.             var targetPixels = targetPixelRegion.ImagePixels;
  31.  
  32.             int rowIndex = 0;
  33.             sourcePixelRegion.ForEachRow((index, width, position) =>
  34.             {
  35.                 // get the vertical midpoint >>> L = (P / W) >>> V = (L / 2)
  36.                 int verticalMidPoint = (sourcePixels.Length / width) / 2;
  37.                 // get the horizontal midpoint >>> M = (W / 2)
  38.                 int horizontalMidPoint = width / 2;
  39.  
  40.                 if (m_MirrorType.Equals(MirrorType.Vertical))
  41.                 {
  42.                     for (int x = 0; x < width; ++x, ++index)
  43.                     {
  44.                         if (rowIndex < verticalMidPoint)
  45.                         {
  46.                             // Just keep the first half of the column as is
  47.                             targetPixels[index] = sourcePixels[index];
  48.                         }
  49.                         else
  50.                         {
  51.                             // Now we start repeating the mirror image from the first half of the column
  52.                             // index - (((i - V) * 2 * W) - 1) 
  53.                             int sourceIndex = index - ((rowIndex - verticalMidPoint) * 2 * width) - 1;
  54.                             if (sourceIndex > 0)
  55.                             {
  56.                                 targetPixels[index] = sourcePixels[sourceIndex];
  57.                             }
  58.                         }
  59.                     }
  60.                 }
  61.                 else
  62.                 {
  63.                     for (int x = 0; x < width; ++x, ++index)
  64.                     {
  65.                         if (x < horizontalMidPoint)
  66.                         {
  67.                             // Just keep the first half of the row as is
  68.                             targetPixels[index] = sourcePixels[index];
  69.                         }
  70.                         else
  71.                         {
  72.                             // Now we start repeating the mirror image from the first half of the row
  73.                             // index - (((x - H) * 2) - 1) 
  74.                             int sourceIndex = index - ((x - horizontalMidPoint) * 2) - 1;
  75.                             if (sourceIndex > 0)
  76.                             {
  77.                                 targetPixels[index] = sourcePixels[sourceIndex];
  78.                             }                  
  79.                         }
  80.                     }
  81.                 }
  82.                 rowIndex++;
  83.             });
  84.         }
  85.     }
  86. }

Testing

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

Device MirrorEffect (Horizontal/Vertical) MirrorFilter (Horizontal) MirrorFilter + Rotation (Horizontal)
Lumia 925 16 FPS 21-22 FPS 20-21 FPS

The MirrorEffect class runs at about 16FPS (frames per second) in both vertical and horizontal mode. The inbuilt MirrorFilter runs at around 21FPS; while it does not support vertical mode, adding a rotation filter to create the same effect reduces the performance by only 1 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

Note.pngNote: This code walkthrough is borrowed from Custom Filter QuickStart for Nokia Imaging SDK

We'll begin with a visual representation of the pixel indexes that we will be traversing.

CustomFilters MirrorPixelMap.PNG

As we can see, the left half of the image is exactly the same. It's not until we reach the center that we have to start making changes, so you can probably guess that we'll need to make note of the row midpoint value. Additionally, we can see that as we move further to the right of the row, past the midpoint, we are decrementing from the index. We continue this pattern until the end of the row when we are finally back down to our original index from the start of the row. Let's continue on to the formulas we'll need to code up this solution.

  • H >>> Horizontal midpoint of a row
  • W >>> Row width
  • x >>> Column index
  • c >>> Column indexes back (offset) from the current index
  • H = (W / 2)
  • c = ((x - H) * 2) - 1


Finally, let's see what the code looks like with our formulas. Remember, we only need to do our special manipulation after the midpoint, so we'll use a simple if statement to determine when we've crossed that point in the row.

  1. sourcePixelRegion.ForEachRow((index, width, position) =>
  2. {
  3.     int horizontalMidPoint = width / 2; // get the horizontal midpoint >>> H = (W / 2)
  4.  
  5.     for (int x = 0; x < width; ++x, ++index)
  6.     {
  7.         if (x < horizontalMidPoint)
  8.         {
  9.             // Just keep the first half of the row as is
  10.             targetPixels[index] = sourcePixels[index];
  11.         }
  12.         else
  13.         {
  14.             // Now we start repeating the mirror image from the first half of the row >>> index - (((x - H) * 2) - 1) 
  15.             targetPixels[index] = sourcePixels[index - ((x - horizontalMidPoint) * 2) - 1];               
  16.         }
  17.     }  
  18. });

Okay, this is cool, but let's take it a step further, and go beyond the out-of-the-box filter in the SDK. Let's do a vertical mirror image. As before, let's work through the formulas first. The approach is very similar to the horizontal method, but the key difference is that each new row increases the index count by the row width; we do not reset to zero with each new row, like we do when iterating the columns.

  • P >>> Total pixels in our image array
  • W >>> Row width
  • L >>> Column length
  • V >>> Vertical midpoint of a column
  • i >>> Row index
  • r >>> Row indexes back (offset) from the current index
  • L = (P / W)
  • V = (L / 2)
  • r >>> ((i - V) * 2 * W) - 1


Finally, let's put it all together in the code below. Note that this is optimized for "understanding", and may differ slightly from the production code.

  1. var sourcePixels = sourcePixelRegion.ImagePixels;
  2. var targetPixels = targetPixelRegion.ImagePixels;
  3.  
  4. int rowIndex = 0;
  5. sourcePixelRegion.ForEachRow((index, width, position) =>
  6. {
  7.     int verticalMidPoint = (sourcePixels.Length / width) / 2; // get the vertical midpoint >>> L = (P / W) >>> V = (L / 2)
  8.  
  9.     for (int x = 0; x < width; ++x, ++index)
  10.     {
  11.         if (rowIndex < verticalMidPoint)
  12.         {
  13.             // Just keep the first half of the column as is
  14.             targetPixels[index] = sourcePixels[index];
  15.         }
  16.         else
  17.         {
  18.             // Now we start repeating the mirror image from the first half of the column >>> index - (((i - V) * 2 * W) - 1) 
  19.             targetPixels[index] = sourcePixels[index - ((rowIndex - verticalMidPoint) * 2 * width) - 1];
  20.         }
  21.     }
  22.     rowIndex++;
  23. });


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 MirrorEffect(imageStream, MirrorEffect.MirrorType.Horizontal))
  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 23 July 2014, at 16:04.
114 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.

×