Image processing operations manipulate the individual pixels of an image to yield another image. Point operations transform every single pixel in the input image into the resulting output pixel, and four of these operations have been described in an earlier article of ours. In this article, we implement a piecewise linear contrast stretch operation, with three linear segments. We focus on grayscale images, with bit depth of 8 bits per pixel, where the minimum and maximum pixel values are 0 and 255, respectively.
Our objective in publishing this article is to demonstrate a single image processing (contrast stretch) operation in an application which is simple and easy to understand. It is our endeavor to include a small number of simple classes (not to include more than ten classes), and not to have any external dependencies; thus making it a self-contained application with a specific purpose.
Piecewise Linear Contrast Stretching
The basic transformation is shown in the figure below. In the figure, the horizontal axis r represents the input pixel value, and the vertical axis s represents the output pixel value. As seen, there are three straight line segments used to transform an input pixel to its resulting output pixel value. Stated otherwise, the transformation from the input pixel value to the output pixel value is via the piecewise linear profile shown in the figure. The parameters specifying the contrast stretch mapping are the four values r2, s2, r3, s3, which determine the position of the intermediate straight line segment. Modifying any of these four values modifies the contrast stretch transformation. The values of r1, s1, r4, s4 are fixed.
The enclosed application allows the user to modify these values, and displays the resulting image accordingly.
Software Design and Code
The language used for the development of this application is C#, and the development tool is Visual Studio 2008 Express Edition.
The software functionality required is:
- To open an image and display it. Since 8-bit grayscale images may not be easily available, an additional functionality is to convert 24-bit or 32-bit colour images to grayscale. The formula used for this conversion is
grayscaleValue = 0.3 * red + 0.59 * green + 0.11 * blue.
- Plot the piecewise linear contrast stretch profile.
- Provide the user with the facility to modify the parameters of the profile.
- Update the image based on the profile, and display the updated image accordingly.
- Allow the user to view the LUT values corresponding to the currently displayed profile.
- Save the updated image.
To speed up the image updating, a lookup table is used. The number of entries in this lookup table is 256 irrespective of the size of the image.
The software is designed to use reusable graphical controls. Two reusable graphical controls are used here (one of them being developed afresh):
ImagePanelControl - to display an 8-bit grayscale image, reused from our earlier article referenced in the Introduction above.
ContrastStretchControl - to display the piecewise linear contrast stretch profile. This control is designed to display the linear profile, and allow the user to drag the end points of the intermediate line segment. Small red rectangles at the ends of the intermediate line segment can be moved around the rectangular area (on mouse-down and move) to change the position/orientation of the lines. Double-buffering is used to eliminate any flicker during display of the line segments while the mouse is being moved; for this, all drawing is done on a back buffer, and the contents of this back buffer are ultimately transferred to the main graphics of the control. This in addition to setting the control's
DoubleBuffered property to
true. Upon release of the mouse button, this control computes the lookup table corresponding to the profile. Computation of the lookup table is perhaps the most important function of this control, and its code is given below. In each of the three straight line segment regions, a different formula is used to determine the mapping. Special care is taken when any denominator value becomes zero.
int r2, r3, r4;
double s2, s3, s4;
byte lookUpTable = new byte;
public void ComputeLookUpTable()
if ((p4.X - p1.X) != 0)
double p41x = 255.0 / (p4.X - p1.X);
double p41y = 255.0 / (p4.Y - p1.Y);
byte b = 0;
r2 = Convert.ToInt32((p2.X - p1.X) * p41x);
r3 = Convert.ToInt32((p3.X - p1.X) * p41x);
s2 = (p2.Y - p1.Y) * p41y;
s3 = (p3.Y - p1.Y) * p41y;
int r32 = r3 - r2;
int r43 = r4 - r3;
double s32 = s3 - s2;
double s43 = s4 - s3;
double factor1 = 0.0, factor2 = 0.0;
if (r32 != 0) factor1 = s32 / r32;
if (r43 != 0) factor2 = s43 / r43;
for (i = 0; i < 256; ++i)
if (i <= r2)
if (r2 == 0)
b = Convert.ToByte(s2);
b = Convert.ToByte(i * s2 / r2);
else if ((r2 < i) && (i <= r3))
if (r32 == 0)
b = Convert.ToByte(s3);
b = Convert.ToByte(s2 + factor1 * (i - r2));
if (r43 == 0)
b = Convert.ToByte(s4);
b = Convert.ToByte(s3 + factor2 * (i - r3));
lookUpTable[i] = b;
The main form hooks up the different controls together and integrates them into a single application.
It is possible to get some interesting effects with this type of contrast stretching.
For example, with a profile as shown below, it is possible to threshold the image. Thresholding maps a range of input pixel values to 0 (black), and its complementary range to 255 (white), thereby creating a binary image. Changing the r-coordinate of the vertical line changes the value of the threshold.
Specifying a profile as shown below has the effect of negating (inverting) the image.
A special effect is obtained with the profile shown below, where some of the intensities are negated, whereas others are not. This has the effect of having both positive and negative parts within the same image.
Specifying the profile shown below causes an entire range of input pixel values to map to a single grayscale value, thus causing the image to appear more "uniform".
Closure and Further Work
A simple application to perform a piecewise linear contrast stretch operation on an image was described above. This application allows the user to open an image and modify the parameters of the contrast stretch operation, upon which the image is updated accordingly. The user can also view the values of the lookup table currently being applied. It is possible to play around with the contrast stretch parameters to see a variety of effects like thresholding, negating, etc. This application was inspired by the book: Digital Image Processing by Gonzalez and Woods.
Further extensions are possible to include a faster way of updating the image. Instead of using just three line segments, it is possible to program for a user-specified number of line segments. Updating the image during mouse-move, rather than during mouse-up, is a possible extension; however, this would require a near-real-time performance, and significantly faster implementation and image updating.
The authors would like to thank Harsha T for useful suggestions on the user interface.