×
Namespaces

Variants
Actions

Quad Transformation Custom Effect (Nokia Imaging SDK)

From Nokia Developer Wiki
Jump to: navigation, search

This article explains how to create an Quad Transformation custom effect.

SignpostIcon XAML 40.png
Article Metadata
Tested with
Devices(s): Nokia Lumia 920
Dependencies: Nokia Imaging SDK 1.0
Article
Created: Engin Kırmacı (19 Feb 2014)
Last edited: Engin Kırmacı (01 Mar 2014)

Contents

Introduction

The QuadTransformation allows to transform any quadrilateral from a given source image to a rectangular image.

Source code

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

QuadTransformation.cs (28 Feb 2014)

  1. // ============================================================================
  2. // DATE        AUTHOR                   DESCRIPTION
  3. // ----------  -----------------------  ---------------------------------------
  4. // 2014.02.13  Engin.Kırmacı            Initial creation
  5. // ============================================================================
  6.  
  7. using NISDKExtendedEffects.Entities;
  8. using Nokia.Graphics.Imaging;
  9. using System.Windows;
  10.  
  11. namespace NISDKExtendedEffects.ImageEffects
  12. {
  13.     public class QuadTransformation : CustomEffectBase
  14.     {
  15.         private QuadDirection Direction;
  16.         private Size Size;
  17.  
  18.         public EdgePoints EdgePoints { get; set; }
  19.  
  20.         public QuadTransformation(IImageProvider source, Size size, QuadDirection direction, EdgePoints edgePoints)
  21.             : base(source)
  22.         {
  23.             Direction = direction;
  24.             Size = size;
  25.  
  26.             EdgePoints = edgePoints;
  27.         }
  28.  
  29.         protected override void OnProcess(PixelRegion sourcePixelRegion, PixelRegion targetPixelRegion)
  30.         {
  31.             double xs0, ys0, xs1, ys1, xs2, ys2, xs3, ys3;  // Four corners of the source (a quadrilateral area)
  32.             double xt0, yt0, xt1, yt1, xt2, yt2, xt3, yt3;  // Four corners of the target (a rectangle)
  33.             int view_org_xs, view_org_ys, view_org_xt, view_org_yt;
  34.  
  35.             // Four corners of the source
  36.             xs0 = EdgePoints.TopLeft.X; ys0 = EdgePoints.TopLeft.Y;
  37.             xs1 = EdgePoints.TopRight.X; ys1 = EdgePoints.TopRight.Y;
  38.             xs2 = EdgePoints.BottomRight.X; ys2 = EdgePoints.BottomRight.Y;
  39.             xs3 = EdgePoints.BottomLeft.X; ys3 = EdgePoints.BottomLeft.Y;
  40.  
  41.             // Margins (the source will be displayed to the left)
  42.             view_org_xs = 0;
  43.             view_org_ys = 0;
  44.  
  45.             if (Size.Width > sourcePixelRegion.ImageSize.Width)
  46.                 Size.Width = sourcePixelRegion.ImageSize.Width;
  47.  
  48.             if (Size.Height > sourcePixelRegion.ImageSize.Height)
  49.                 Size.Height = sourcePixelRegion.ImageSize.Height;
  50.  
  51.             // Four corners of the target
  52.             xt0 = 0; yt0 = 0;
  53.             xt1 = Size.Width; yt1 = 0;
  54.             xt2 = Size.Width; yt2 = Size.Height;
  55.             xt3 = 0; yt3 = Size.Height;
  56.  
  57.             // Margins (the target will be displayed to the right)
  58.             view_org_xt = 0;
  59.             view_org_yt = 0;
  60.  
  61.             int width = (int)sourcePixelRegion.Bounds.Width;
  62.             int height = (int)sourcePixelRegion.Bounds.Height;
  63.  
  64.             double xt, yt; // Target (rectangle)
  65.             double xs, ys = 0; // Source (quadrilatreal area)
  66.             uint color;    // Pixel
  67.  
  68.             // Formula f(x) = ax + b for the edges and diagonals
  69.             double a_top, b_top, a_bottom, b_bottom, a_left, b_left, a_right, b_right;
  70.  
  71.             // Data for any line between the top and the bottom edge and more or less parallel to them
  72.             double xs_horiz_left, ys_horiz_left, xs_horiz_right, ys_horiz_right;
  73.             double a_horiz, b_horiz;
  74.  
  75.             // Data for any line between the left and the right edge and more or less parallel to them
  76.             double xs_verti_top, ys_verti_top, xs_verti_bottom, ys_verti_bottom;
  77.             double a_verti, b_verti;
  78.  
  79.             // Data for perspective
  80.             double perspv_a, perspv_b, perspv_4d05;
  81.             double persph_a, persph_b, persph_4d05;
  82.             double tmp_01;
  83.  
  84.             // -----------------------------------------------------------------------------
  85.             // Get the equations, f(x) = ax + b, of the four edges of the quadrilateral area
  86.             // (for vertical lines, a = 0 means a = infinity for x = b)
  87.             // -----------------------------------------------------------------------------
  88.             // Top edge
  89.             if (xs1 == xs0) { a_top = 0; b_top = xs1; } // special case of vertical line
  90.             else { a_top = (ys1 - ys0) / (xs1 - xs0); b_top = ys0 - a_top * xs0; }
  91.  
  92.             // Bottom edge
  93.             if (xs2 == xs3) { a_bottom = 0; b_bottom = xs2; }
  94.             else { a_bottom = (ys2 - ys3) / (xs2 - xs3); b_bottom = ys3 - a_bottom * xs3; }
  95.  
  96.             // Left edge
  97.             if (xs3 == xs0) { a_left = 0; b_left = xs3; }
  98.             else { a_left = (ys3 - ys0) / (xs3 - xs0); b_left = ys0 - a_left * xs0; }
  99.  
  100.             // Right edge
  101.             if (xs2 == xs1) { a_right = 0; b_right = xs2; }
  102.             else { a_right = (ys2 - ys1) / (xs2 - xs1); b_right = ys1 - a_right * xs1; }
  103.  
  104.             // Data for perspective
  105.             perspv_4d05 = ((xs1 - xs0) / (xs2 - xs3)) * 2;
  106.             perspv_a = 2 - perspv_4d05;
  107.             perspv_b = perspv_4d05 - 1;
  108.  
  109.             persph_4d05 = ((ys3 - ys0) / (ys2 - ys1)) * 2;
  110.             persph_a = 2 - persph_4d05;
  111.             persph_b = persph_4d05 - 1;
  112.  
  113.             // Loop for each horizontal line
  114.             for (yt = yt0; yt < yt3; yt++)
  115.             {
  116.                 // Find the corresponding y on the left edge of the quadrilateral area
  117.                 //   - adjust according to the lengths
  118.                 ys_horiz_left = (yt * (ys3 - ys0) / (yt3 - yt0));
  119.  
  120.                 if (a_left != a_right)
  121.                 { // left edge not parallel to the right edge
  122.                     //   - adjust according to the perspective
  123.                     tmp_01 = (ys_horiz_left) / (ys3 - ys0);
  124.                     ys_horiz_left = (ys3 - ys0) * (tmp_01 * tmp_01 * perspv_a + tmp_01 * perspv_b);
  125.                 }
  126.  
  127.                 ys_horiz_left += ys0;
  128.  
  129.                 // Find the corresponding x on the left edge of the quadrilateral area
  130.                 if (a_left == 0) xs_horiz_left = b_left;
  131.                 else xs_horiz_left = (ys_horiz_left - b_left) / a_left;
  132.  
  133.                 // Find the corresponding of y on the right edge of the quadrilateral area
  134.                 //   - adjust according to the lengths
  135.                 ys_horiz_right = (yt * (ys2 - ys1) / (yt2 - yt1));
  136.  
  137.                 if (a_left != a_right)
  138.                 { // left edge not parallel to the right edge
  139.                     //   - adjust according to the perspective
  140.                     tmp_01 = (ys_horiz_right) / (ys2 - ys1);
  141.                     ys_horiz_right = (ys2 - ys1) * (tmp_01 * tmp_01 * perspv_a + tmp_01 * perspv_b);
  142.                 }
  143.  
  144.                 ys_horiz_right += ys1;
  145.  
  146.                 // Find the corresponding x on the left edge of the quadrilateral area
  147.                 if (a_right == 0) xs_horiz_right = b_right;
  148.                 else xs_horiz_right = (ys_horiz_right - b_right) / a_right;
  149.  
  150.                 // Find the equation of the line joining the points on the left and the right edges
  151.                 if (xs_horiz_right == xs_horiz_left) { a_horiz = 0; b_horiz = xs_horiz_right; }
  152.                 else
  153.                 {
  154.                     a_horiz = (ys_horiz_right - ys_horiz_left) / (xs_horiz_right - xs_horiz_left);
  155.                     b_horiz = ys_horiz_left - a_horiz * xs_horiz_left;
  156.                 }
  157.  
  158.                 // Loop for each point in an horizontal line
  159.                 for (xt = xt0; xt < xt1; xt++)
  160.                 {
  161.                     // Find the corresponding x
  162.                     //   - adjust according to the lengths
  163.                     xs = (xt * (xs_horiz_right - xs_horiz_left) / (xt1 - xt0));
  164.                     xs += xs_horiz_left;
  165.  
  166.                     // - adjust for perspective
  167.  
  168.                     // Find the corresponding point on the top edge of the quadrilateral area
  169.                     xs_verti_top = (xs - xs_horiz_left) * (xs1 - xs0) / (xs_horiz_right - xs_horiz_left);
  170.  
  171.                     if (a_top != a_bottom)
  172.                     { // top edge not parallel to the bottom edge
  173.                         tmp_01 = (xs_verti_top) / (xs1 - xs0);
  174.                         xs_verti_top = (xs1 - xs0) * (tmp_01 * tmp_01 * persph_a + tmp_01 * persph_b);
  175.                     }
  176.  
  177.                     xs_verti_top += xs0;
  178.                     ys_verti_top = a_top * xs_verti_top + b_top;
  179.  
  180.                     // Find the corresponding of x on the bottom edge of the quadrilateral area
  181.                     xs_verti_bottom = (xs - xs_horiz_left) * (xs2 - xs3) / (xs_horiz_right - xs_horiz_left);
  182.  
  183.                     if (a_top != a_bottom)
  184.                     { // top edge not parallel to the bottom edge
  185.                         tmp_01 = (xs_verti_bottom) / (xs2 - xs3);
  186.                         xs_verti_bottom = (xs2 - xs3) * (tmp_01 * tmp_01 * persph_a + tmp_01 * persph_b);
  187.                     }
  188.  
  189.                     xs_verti_bottom += xs3;
  190.                     ys_verti_bottom = a_bottom * xs_verti_bottom + b_bottom;
  191.  
  192.                     // Find the equation of the line joining the points on the top and the bottom edges
  193.                     if (xs_verti_top != xs_verti_bottom)
  194.                     {
  195.                         a_verti = (ys_verti_bottom - ys_verti_top) / (xs_verti_bottom - xs_verti_top);
  196.                         b_verti = ys_verti_top - a_verti * xs_verti_top;
  197.  
  198.                         xs = (ys - b_verti) / a_verti; // new xs
  199.                         // ys = a_horiz * xs + b_horiz;   // adjust ys
  200.                     }
  201.  
  202.                     // Find the corresponding y with the equation of the line
  203.                     ys = a_horiz * xs + b_horiz;
  204.  
  205.                     // Copy a pixel
  206.                     switch (Direction)
  207.                     {
  208.                         case QuadDirection.QuadToRect:
  209.                             color = sourcePixelRegion.ImagePixels[((int)(ys) + view_org_ys) * width + ((int)(xs) + view_org_xs)];
  210.                             targetPixelRegion.ImagePixels[((int)(yt + 0.5) + view_org_yt) * width + ((int)(xt + 0.5) + view_org_xt)] = color;
  211.                             break;
  212.  
  213.                         case QuadDirection.RectToQuad:
  214.                             color = sourcePixelRegion.ImagePixels[((int)(yt) + view_org_yt) * width + ((int)(xt) + view_org_xt)];
  215.                             targetPixelRegion.ImagePixels[((int)(ys + 0.5) + view_org_ys) * width + ((int)(xs + 0.5) + view_org_xs)] = color;
  216.                             break;
  217.                     }
  218.                 }
  219.             }
  220.         }
  221.     }
  222. }

Testing

The Test Apps for Viewing Custom Filters allow you to cycle through a number of different custom effects (including QuadTransformation) 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 QuadTransformation Small Area QuadTransformation Medium Area QuadTransformation Big Area
Lumia 920 4-6 FPS 8-9 FPS 17-18 FPS

The QuadTransformation class runs 4-18 FPS (Frames Per Second) depending on area you want transform.

Code walkthrough

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.     //Estimates rectangle size by given edge points
  13.     var estimatedSize = points.EstimatedRectangleSize();
  14.  
  15.     // Applying the custom filter effect to the image stream
  16.     using (var customEffect = new QuadTransformation(imageStream, estimatedSize, NISDKExtendedEffects.Entities.QuadDirection.QuadToRect, points))
  17.     {
  18.         // Applying ReframingFilter to change image size to estimated size
  19.         var reframingFilter = new ReframingFilter(new Windows.Foundation.Rect(0, 0, estimatedSize.Width, estimatedSize.Height), 0);
  20.  
  21.         _filterEffect = new FilterEffect(customEffect)
  22.         {
  23.             Filters = new IFilter[] { reframingFilter }
  24.         };
  25.  
  26.         // Rendering the resulting image to a WriteableBitmap
  27.         using (var renderer = new WriteableBitmapRenderer(_filterEffect, writeableBitmap))
  28.         {
  29.             // Applying the WriteableBitmap to our xaml image control
  30.             FilterEffectImage.Source = await renderer.RenderAsync();
  31.         }
  32.     }
  33. }

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 transform an image using Quad Transformation. 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.

This page was last modified on 1 March 2014, at 02:31.
89 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.

×