Wide Angle Lens Effect
Note: This is an entry in the Nokia Original Imaging Effect Wiki Challenge 2014Q2
This article explains how to implement a Wide Angle Lens effect.
Windows Phone 8
Contents |
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.
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. | |
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. |
U_{n+1} = U_{n} + 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 width
const 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 right
int startLeft = (int)(halfwidth - halfwidth * CenterDistance);
int startRight = (int)(halfwidth + halfwidth * CenterDistance);
// apply the distortion for the left side of the image
for (int x = startLeft-1; x >= 0 ; x--)
{...
widthDist = widthDist + WidthDistortion;
heightDist = heightDist + HeightDistortion;
}
// leave the center unchanged
for (int x = startLeft; x < startRight; x++, index++)
{
targetPixels[index] = sourcePixels[index];
}
//apply the distortion to the right side
for (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 |
---|---|
(no comments yet)