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. Thanks for all your past and future contributions.

Wide Angle Lens Effect

18 Aug
2014

Code ExampleTested with
SDK: Windows Phone 8.0, 8.1
Devices(s): Nokia Lumia 920, 625 , 620
Compatibility
Platform(s):
Windows Phone 8
Article
Created: Loukt (21 Jun 2014)
Last edited: croozeus (18 Aug 2014)

Introduction

A wide-angle lens refers to a lens with a focal length substantially smaller than the normal lens focal length, this lens allows more of the scene to be included in the picture.

This lens is usually used to emphasize the difference in size or distance between objects in the foreground and the background, nearby objects appear very large and objects at a moderate distance would appear small and far away.

Wide Angle Effect

Basically, to go from a normal picture to a Wide-Angle is clearly impossible as we need to retrieve more data around the image, in this article we'll try to simulate the Effect giving an illusion of a Wide Angle Effect.

 normal image without distortion Like the FishEye Lens, the Wide Angle also applies some distortions on the image, we'll try to reproduce those distortions and try to bring in the emphasis done by the lens on the image. image with distortion applied We can easily define these distortions as a gradual stretching in both the width and height, having as a start points the center vertical line of the image.

Creating the effect

Before creating a custom effect, we recommend you read Custom Filter QuickStart for Nokia Imaging SDK.

How to calculate the numerical distortion values

 as stated before, there is a gradual stretching in both the width and the height, starting from the central vertical line points to the edges of the image, we can perform this "gradual" distortions effect mathematically using Numerical Series, widening the distortions at each iteration.
 Un+1 = Un + Distortion

Before we start the calculating the distortions, to make the computing easy we start by normalizing all the pixels to a range of [-1 1] for both height and width, and then Starting from the center of the first row we start with left side, then the right side.

 There is also a case where we could leave a portion of the center untouched in which we simply specify the percentage to ignore. In the normal case, the distortion will start from the center (blue vertical line) and will increase gradually till the edges of the image (orange vertical lines), as for the second case, a rectangle equals to the specified percentage will be ignored (blue rectangle) and will not be affected, the distortion will start at the edges of this rectangle to the orange vertical lines.

we set two constants for the Height and Width distortion and then we divide the image into 3 parts, the left side, the center (which could be one line) and the right side

`// two constant values to controle the distortion for the height and widthconst double HeightDistortion = 0.0005;const double WidthDistortion = 0.0001;//we calculate according the CenterDistance to be ignored, the starting //points from the left and the rightint startLeft = (int)(halfwidth - halfwidth * CenterDistance);int startRight = (int)(halfwidth + halfwidth * CenterDistance); // apply the distortion for the left side of the imagefor (int x = startLeft-1; x >= 0 ; x--){...widthDist = widthDist + WidthDistortion;heightDist = heightDist + HeightDistortion;}// leave the center unchangedfor (int x = startLeft; x < startRight; x++, index++){                    targetPixels[index] = sourcePixels[index];}//apply the distortion to the right sidefor (int x = startRight; x < width; x++){...widthDist = widthDist + WidthDistortion;heightDist = heightDist + HeightDistortion;}`

The code

`const double HeightDistortion = 0.0005;const double WidthDistortion = 0.0001; protected override void OnProcess(PixelRegion sourcePixelRegion, PixelRegion targetPixelRegion)        {            var sourcePixels = sourcePixelRegion.ImagePixels;            var targetPixels = targetPixelRegion.ImagePixels;            int rowindex = 0;            int halfwidth = (int)targetPixelRegion.ImageSize.Width / 2;             int startLeft = (int)(halfwidth - halfwidth * CenterDistance);            int startRight = (int)(halfwidth + halfwidth * CenterDistance);             double widthDist = 0;            double heightDist = 0;            double resizeH = 0;            sourcePixelRegion.ForEachRow((index, width, position) =>            {                double ny = ((2 * rowindex) / sourcePixelRegion.ImageSize.Height) - 1;                  widthDist = heightDist = 0;                for (int x = startLeft-1; x >= 0 && index < width * sourcePixelRegion.ImageSize.Height; x--)                {                    resizeH = heightDist / startLeft;                    double newSize = resizeH * (startLeft - x) + 1;                    double nx = ((2 * x) / sourcePixelRegion.ImageSize.Width) - 1;                    //compute new x,y                    double nyn = ny / newSize;                    double nxn = nx + widthDist;                     //                    if (nxn <= 1 && nxn >= -1 && nyn >= -1 && nyn <= 1)                    {                        int x2 = (int)(((nxn + 1) * sourcePixelRegion.ImageSize.Width) / 2);                        int y2 = (int)(((nyn + 1) * sourcePixelRegion.ImageSize.Height) / 2);                        // find (x2,y2) position from source pixels                         int srcpos = (int)(y2 * sourcePixelRegion.ImageSize.Width + x2);                        // make sure that position stays within arrays                         if (srcpos >= 0 &                            srcpos < sourcePixelRegion.ImageSize.Width * sourcePixelRegion.ImageSize.Height)                        {                            targetPixels[index + x] = sourcePixels[srcpos];                        }                    }                    widthDist = widthDist + WidthDistortion;                    heightDist = heightDist + HeightDistortion;                }                 index = rowindex * width + startLeft;                 // if we are in the Center, don't distort                for (int x = startLeft; x < startRight; x++, index++)                {                    targetPixels[index] = sourcePixels[index];                }                 widthDist = heightDist = 0;                for (int x = startRight; x < width; x++)                {                    resizeH = heightDist / startLeft;                    double newSize = resizeH * (startRight - x) + 1;                    double nyn = ny * newSize;                    double nx = ((2 * x) / sourcePixelRegion.ImageSize.Width) - 1;                    double nxn = nx - widthDist;                     if (nxn <= 1 && nxn >= -1 && nyn >= -1 && nyn <= 1)                    {                        int x2 = (int)(((nxn + 1) * sourcePixelRegion.ImageSize.Width) / 2);                        int y2 = (int)(((nyn + 1) * sourcePixelRegion.ImageSize.Height) / 2);                        int srcpos = (int)(y2 * sourcePixelRegion.ImageSize.Width + x2);                        // make sure that position stays within arrays                         if (srcpos >= 0 &                            srcpos < sourcePixelRegion.ImageSize.Width * sourcePixelRegion.ImageSize.Height)                        {                            targetPixels[index + x - startRight] = sourcePixels[srcpos];                        }                     }                    else break;                    widthDist = widthDist + WidthDistortion;                    heightDist = heightDist + HeightDistortion;                }                  rowindex++;            });        }`

Performance

Device WideAngleEffect
Lumia 925 4-5 FPS
Lumia 620 2-3 FPS

Using the FishEye Effect

There are multiple ways of using the filter, but here is a quick and easy way to see an example. Download the RealTimeFilterDemo solution available here.

It's recommended to read the walk-through of the solution available here too).

`case xx: //replace xx with a number according the effectCount in your list  {  EffectName = String.Format(nameFormat, (_effectIndex + 1), "Wide Angle");  _customEffect = new WideAngleEffect(m_StreamImageSource, 0); //the second parameter is the portion from center to ignore, the range is [0,1]//0 means nothing is ignored. //1 means all the image is ignored  }  break;`

Results

Original image Result
Original image
Wide Angle
Original image
Wide Angle
Original image
Wide Angle