According to Wikipedia, vignetting in photography is any process by which there is loss of clarity towards the corners and sides of an image. This, in other words, is a reduction of an image's brightness or saturation at the periphery compared to the center of the image. For purposes of this article, a vignette is a soft border around an image, and the shape of the border is the vignette shape. Many commercial packages provide the vignette effect as one of their features. Though I have no access to popular packages like Photoshop, I have an idea of its vignette feature by looking at tutorials on the Internet. In this article, I present a simple program which 'draws' a vignette border around your favorite image. Hopefully this program also creates some vignette effects which Photoshop is yet to provide.
2. Features of the Program
This software application allows you to:
- Select the shape of the vignette from one of five - Circle, Ellipse, Diamond, Rectangle, and Square.
- Modify the orientation of the vignette between 0 and 180 degrees (except for the Circle).
- Modify the coverage area of the vignette, from a small area of the image to almost encompassing the entire image.
- Modify the width of the 'blend zone' (described later in this article) of the vignette, from narrow to wide.
- Modify the smoothness of blend from coarse to fine.
- Modify the x and y coordinates of the centre of the vignette so that it can be moved across the image.
- Modify the color of the border zone.
- Preview the effect of modifying each of the above parameters.
- Save the vignetted image.
We will examine each of these features individually and see their implementation. Since the elliptical shape is perhaps the most complicated of all our shapes, I will explain the elliptical vignette in detail, and briefly look at the other shapes.
3. Basics of an Elliptical Vignette
The figure below shows the basic geometry of an elliptical vignette. I have drawn this figure somewhat exaggerated for purposes of clarity. Given an image, called as the 'source image', and a border color, the task is to create a vignetted image called the 'target image'. The rectangle OABC represents the image, with OA as its width and OC as its height; and the Ox and Oy axes represent the global coordinate system. It is desired to create an elliptical vignette with the following characteristics:
- Orientation of the ellipse, shown by angle theta in the figure.
- Coverage represented, in an average sense, by the ellipse E3 in the figure.
- Width of the 'blend zone' represented by the difference between the major axes of ellipses E5 and E1 in the figure. This is also equal to the difference between the minor axes of these ellipses.
- Smoothness (or coarseness) of the blend represented by the number of steps between the outer and inner ellipses. Difference in major axes between, say, ellipses E5 and E4 represents the step size. Since there are five ellipses from outer to inner - E5, E4, E3, E2, and E1 - the number of steps is four. Ignore the dotted-line ellipse E34 for now.
- X and Y coordinates of the center of the ellipse (Point O1) represented by x-offset and y-offset. The center point of the image itself is shown by the + sign in the middle of the image.
- Color of the border represented by the color outside ellipse E5.
3.1 Problem Statement in Greater Detail
Create three zones in the image such that:
- In Zone A, represented by the area inside the innermost ellipse E1 in the figure, the pixels in the target image are taken directly from the corresponding pixels in the source image. The ellipse E1 surrounding Zone A has its major and minor axes calculated as follows:
Major Axis of Ellipse E1 = Major Axis of Ellipse E3 - Half of the width of blend zone
Minor Axis of Ellipse E1 = Minor Axis of Ellipse E3 - Half of the width of blend zone
- In Zone C, represented by the area outside the outermost ellipse E5 in the figure, the pixels in the target image correspond directly to the border color. The ellipse E5 surrounded by Zone C has its major and minor axes determined by:
Major Axis of Ellipse E5 = Major Axis of Ellipse E3 + Half of the width of blend zone
Minor Axis of Ellipse E5 = Minor Axis of Ellipse E3 + Half of the width of blend zone
- Zone B can be termed as the blend zone. In Zone B, represented by the area between ellipses E5 and E1 in the figure, the pixels in the target image are computed as weighted sums from both the source image and border color. The choice of weighting functions determines the smoothness of the blend. Along a directed line PQRST drawn from the innermost ellipse to the outermost one, the influence of the source image gradually diminishes and that of the border color gradually increases. At the inner end P of this line, the source image completely dictates the target pixel; whereas at the outer end T of this line, the border color completely dictates the target pixel. At points in-between, the target pixel value is a weighted sum of the two, as mentioned earlier.
Let's now take a look at the equations and their implementation, feature by feature.
4. Location, Orientation, and Coverage (Size) of Each Ellipse
From the figure above, it is seen that five parameters determine the location, orientation, and size of each ellipse:
- Two parameters - X-Coordinate and Y-Coordinate of the 'center' of the ellipse, shown by x-offset and y-offset in the above figure.
- Orientation 'theta' of the major axis of the ellipse, shown in the figure above.
- Two parameters - Size of Major and Minor axes of the ellipse, for example the Major and Minor axes of the Ellipse E3 in the figure above.
It is straightforward to draw a 'straight' ellipse - one which is aligned with the global x and y axes. Not so for an inclined one. However, if you have heard of the term 'rotation matrix', you'll note that this is not difficult. The following formulas give the equation of an ellipse with the above parameters - including both rotation and translation. Implementation of this equation in code is straightforward.
Another thing to note regarding an ellipse is whether a point lies within an ellipse, or outside it. This is determined as follows:
Coverage of the ellipse is quite straightforward. If the values of the major and minor axes of an ellipse are greater than the respective values for another ellipse, then the former ellipse is larger in area than the latter.
In the application, a slider is provided to modify the orientation of the ellipse. A slider (termed Coverage) is provided to increase the major and minor axes (together, in proportion) of the ellipse. Two sliders modify the x-offset and y-offset values.
5. Width of the Blend Zone
Zone B represents the width of the blend. This is the zone in between the ellipses E5 and E1. Ellipses E5 and E1 are characterized by their major and minor axes. The width of the blend zone is modified by the user, via its corresponding slider.
6. Smoothness of the Blend
Getting this right was not easy. It was a struggle to arrive at the right equations.
Consider two images I1 and I2 to be blended over a region I12. In the second figure above, Zone B represents the blend zone. In this zone, the target pixel value is determined both by the source image and the border color; here the border colour can also be thought of as another image, termed as the border image. The variation in contribution of the different images along the line PQRST is to be determined. The target pixel value can be taken as the sum of weighted functions:
Target Pixel Value = I1 * w1 + I2 * w2
where the weights w1 = w1(s) and w2 = w2(s) are the contributions of images I1 and I2, respectively. The parameter s is the linear distance measured from point P along line PQRST. These weighting functions are to be chosen such that their sum at any point is unity. Choosing the correct functions w1(s) and w2(s) is critical to getting the right visually pleasing effect. Let's define d as the distance PT (this is equal to the width of the blend zone). (Note that the weights are defined only in the blend zone, and not outside it.)
I first tried linear weights (blending functions), given by the equations:
For some image-border color combinations, this worked well; but for others, this was awful. This set me thinking about a new equation for blending. I started searching on the Internet for suitable blending functions, and came across the paper by Burt and Adelson [Peter J Burt and Edward H Adelson, A Multiresolution Spline With Application to Image Mosaics, ACM Transactions on Graphics, Vol. 2. No. 4, October 1983, Pages 217-236]. Hardly had I read the first three pages, when I came across a figure in that paper. This set me thinking into using the Cosine function for blending. Upon deriving the equations, the following blending functions emerged (I wanted to include the gory details of the derivation here, but found that it is too cumbersome on Microsoft Word 2003 to key them in!):
The mathematically inclined reader may note that the linear blending functions are C0 continuous at the boundary, and not C1 continuous at the end points: s = 0 and s = d. The cosine-based blending functions, however, are both C0 and C1 continuous at these end points, and this gives the pleasing blend effect. Another such function is the sigmoid function, and I'll try it for a later version.
Now comes an important question. How to draw line PQRST. Ideally, it should be normal to all the ellipses it intersects with. This would involve the computation of the derivative of an ellipse, and may become computationally difficult. Upon pondering further, I decided to split the blend zone into a number of elliptical zones - for example, the zone between ellipses E4 and E3 is one such elliptical zone. Within each zone, I consider the value of s corresponding to the mid-point U for the mid-ellipse, shown as E34 (ellipse drawn with a dotted line) in the figure. And so on with all the elliptical zones. When the number of elliptical zones is less, discrete steps are seen in the resulting image. However, when the number of elliptical zones is increased, a smooth blending effect is perceived. The Smoothness slider on the screen is therefore used to vary the number of steps in the blend.
The above-referenced paper talks about multi-resolution, but given the nice results I got without using it, I decided to go with just 'single-resolution'.
7. On the Other Shapes - Circle, Diamond, Rectangle, and Square
Once the elliptical vignette is fully understood, it becomes easy to follow the other vignette shapes:
- Circle: The circle is a special case of ellipse, with identical values for the major and minor axes.
- Diamond: The diamond is a figure bounded by straight lines, and the concept of major and minor axes is not of relevance. Considering the 'two-intercept' form of a straight line, the equation of the diamond becomes (where a and b are equivalent to the major and minor axes):
A method similar to the ellipse can be used to determine whether a point lies within the diamond or not.
- Rectangle: Working with a rectangular shape is quite straightforward. The fact that .NET offers a method to determine whether a point lies inside a rectangle, or outside it, makes it a lot easy.
- Square: A square is a special case of rectangle, with identical length and breadth dimensions.
8. On the Code
The vignette code is organized into a class
VignetteEffect, which has the following important methods:
SetupParameters: In an attempt to improve performance, I compute the important parameters initially and then apply these parameters to the vignette. This method first computes the major and minor axes of the different ellipses (or equivalent dimensions for the diamonds and rectangles); and in addition, computes the weights used in the blend zone. The weight computation is computationally intensive (since it involves computation of trigonometric functions), and is best done at setup time.
ApplyEffectCircleEllipseDiamond: It is possible to combine the computations for a Circle, Ellipse, or Diamond into a single function, since the formulas differ only slightly. This method loops over all the pixels of the target image. Firstly, the zone (Zone A, Zone B, or Zone C) to which a pixel belongs is determined. Secondly, the target pixel value is computed. For a pixel in Zone A, the source pixel is just copied to the target image. For a pixel in Zone C, the border color determines the target image pixel value. For a Zone B pixel, the blend formula is used to determine the target pixel value. The following is the relevant code extract:
int k, el, w1, w2;
byte r, g, b;
double wb2 = width * 0.5 + Xcentre * width * geometryFactor;
double hb2 = height * 0.5 + Ycentre * height * geometryFactor;
double thetaRadians = Angle * Math.PI / 180.0;
double cos = Math.Cos(thetaRadians);
double sin = Math.Sin(thetaRadians);
double xprime, yprime, potential1, potential2, potential;
double factor1, factor2, factor3, factor4;
byte redBorder = BorderColour.R;
byte greenBorder = BorderColour.G;
byte blueBorder = BorderColour.B;
for (el = 0; el < height; ++el)
w2 = width * el;
for (k = 0; k < width; ++k)
xprime = (k - wb2) * cos + (el - hb2) * sin;
yprime = -(k - wb2) * sin + (el - hb2) * cos;
factor1 = 1.0 * Math.Abs(xprime) / aVals;
factor2 = 1.0 * Math.Abs(yprime) / bVals;
factor3 = 1.0 * Math.Abs(xprime) / aVals[NumberSteps];
factor4 = 1.0 * Math.Abs(yprime) / bVals[NumberSteps];
if (Shape == VignetteShape.Circle || Shape == VignetteShape.Ellipse)
potential1 = factor1 * factor1 + factor2 * factor2 - 1.0;
potential2 = factor3 * factor3 + factor4 * factor4 - 1.0;
potential1 = factor1 + factor2 - 1.0;
potential2 = factor3 + factor4 - 1.0;
w1 = w2 + k;
if (potential1 <= 0.0)
r = pixRedOrig[w1];
g = pixGreenOrig[w1];
b = pixBlueOrig[w1];
else if (potential2 >= 0.0)
r = redBorder;
g = greenBorder;
b = blueBorder;
int j, j1;
for (j = 1; j < NumberSteps; ++j)
factor1 = Math.Abs(xprime) / aVals[j];
factor2 = Math.Abs(yprime) / bVals[j];
if (Shape == VignetteShape.Circle ||
Shape == VignetteShape.Ellipse)
potential = factor1 * factor1 + factor2 * factor2 - 1.0;
potential = factor1 + factor2 - 1.0;
if (potential < 0.0) break;
j1 = j - 1;
r = (byte)(pixRedOrig[w1] * weight1[j1] + redBorder * weight2[j1]);
g = (byte)(pixGreenOrig[w1] * weight1[j1] + greenBorder * weight2[j1]);
b = (byte)(pixBlueOrig[w1] * weight1[j1] + blueBorder * weight2[j1]);
pixRedModified[w1] = r;
pixGreenModified[w1] = g;
pixBlueModified[w1] = b;
ApplyEffectRectangleSquare: This is similar to the above method, but specialized for rectangles and squares. The .NET
Rect structure has a method called
Contains(Point pt), which returns
true if a point lies within the rectangle, and
false otherwise. This is used to identify the zone, and hence compute the target pixel value.
The main window contains all the UI elements. The code-behind class performs the functions of image reading, populating the pixel lists, transferring these pixels to the
VignetteEffect object, applying the effect, and forming the image from the modified pixels returned by it. All of these are put together into a Visual Studio 2008 solution.
9. Vignette Gallery
Since the user has several ways to modify the vignette parameters, there is possibly an infinity of vignette patterns possible through this application. Here, I present a gallery of some of the vignettes created using this program. The source image is the Tulips image available with Windows 7.
10. Points of Interest
Performance: My focus here has been functionality as against performance. However, to give a responsive UI, I've scaled the original image to fit into a 600 x 600 area, taking the aspect ratio into consideration. All user actions are done on this scaled image. This is one way to improve performance. However, this doesn't seem to be enough. You may notice that some of the sliders don't move easily, and this can cause annoyance. While I keep thinking of ways to improve performance, please send in your suggestions. (Multithreading is an option to try, but that's for a later version).
WPF or Windows Forms: When I showed this application to a friend, he said that it looks more like a Windows Forms application. Perhaps it does. Again, I've not used any pattern like MVVM. One more thing to note is that I've not used WPF features like paths and geometries to create the mask on the image; nor have I used image brushes. However, I doubt whether we can use these to achieve the blend zone effects achievable through brute-force pixel manipulation. In essence, I've perhaps not used WPF to its fullest potential. Do send in your suggestions here as well.
Anti-aliasing: I've not tried to implement any anti-aliasing (either bilinear interpolation or sub-pixel calculations) in the algorithm. I find the results quite acceptable without anti-aliasing.
In this article, a method to create a vignette was shown. Perhaps I have taken the concept of a vignette to its extreme, giving the user all possible provisions to modify its attributes. I hope you enjoy the application despite its somewhat slow nature, and use it to create the vignetted images you desire. If you feel that more features are to be added, do write back. Even otherwise, your feedback is deeply appreciated.
- Version 1.0: 17 April 2011.