Gamma correction is a non-linear adjustment to individual pixel values. While in image normalization we carried out linear operations on individual pixels, such as scalar multiplication and addition/subtraction, gamma correction carries out a non-linear operation on the source image pixels, and can cause saturation of the image being altered. Furthermore, it can also lead to poor contrast if the gamma value is too large or too small. Nonetheless, it is still an important operation to cover.
Many people have likely encountered this term before while adjusting their new television set or playing around with advanced video settings in PC games. At first glance, gamma correction appears to either darken or brighten an image, but this is a gross oversimplification. We can already adjust the average brightness of an image by some modified normalization algorithm, the simplest of which would be simply adding a constant value to each pixel intensity value, effectively “shifting” the mean pixel intensity values across the entire image. Gamma correction is an operation that effectively carries out an exponential function on individual pixel values.
Before continuing, let’s consider the case of a “black and white” television set. Assuming it acts as a simple output device, we’d expect it to display, say, a black pixel wherever we specify a pixel intensity value of zero (0), and a white pixel wherever we specify an intensity value of one (1). All values inbetween 0 and 1 are shades of gray. With many (if not all) graphical display devices, this is not the case. Although we have specified that we want a specific color at a certain pixel location, due to imperfections in the display device, the actual color we see on-screen is not the exact color we desire. The diagram below shows a grayscale spectrum, along with the “transfer function” of an ideal display device to its right. For the transfer function plot to the right, consider the x-axis to be the color we want to appear on-screen, while the y-axis represents the color intensity value that is actually displayed. For the grayscale spectrum on the left, this shows an approximation of how “rapidly” output intensity values shift from black to white. In the case of the ideal display device below, the desired output intensity value is exactly the one we specified.
Now consider an old television set that is on its last legs. Due to worn out components, everything appears darker than it should, as demonstrated below.
Finally, consider the case of a new TV that is far too bright. The Output spectrum and transfer function are shown below.
The MATLAB code shown below was used to generate the images shown above.
%Normal, gamma=1.0 i=double(ones(100,100)); for j=1:100 for k = 1:100 i(j,k)=round(k/10)./10; end end subplot(1,2,1);imagesc(i);colormap gray; subplot(1,2,2);plot(0:0.01:1);axis tight; %Brighter, gamma=0.5 i=double(ones(100,100)); for j=1:100 for k = 1:100 i(j,k)=(round(k/10)./10).^0.5; end end subplot(1,2,1);imagesc(i);colormap gray; subplot(1,2,2);plot((0:0.01:1).^0.5);axis tight; %Darker, gamma=2.0 i=double(ones(100,100)); for j=1:100 for k = 1:100 i(j,k)=(round(k/10)./10).^2.0; end end subplot(1,2,1);imagesc(i);colormap gray; subplot(1,2,2);plot((0:0.01:1).^2.0);axis tight;
For a more mathematical analysis, consider any arbitrary pixel on a display device. The grayscale intensity value we actually see is defined as , and the intensity value we want to appear on the screen (in our code) is defined as . Finally, the gamma value is defined as . The relationship between the input and output intensity values can be described by the equation below. A table of figures representing the transfer function of gamma correction for varying values of gamma is shown below for emphasis.
In the ideal case, with , our input and output intensity values match perfectly, as in the case of the ideal display described at the top of the page. As the gamma value deviates from unity, we begin to notice a significant difference in the input and output intensity values. As gamma approaches zero, the output pixels become brighter, while as gamma approaches infinity, the pixels become darker. Gamma correction essentially carries out the inverse function of the exponential operation caused by imperfections in the display device.
For an example, consider we are using a display which, by observation, we assume is introducing gamma distortion corresponding to a gamma value of . This means that when we specify a pixel value to appear on-screen, it shows up with a (distorted) pixel value of . If we carry out the inverse of this opertion in advance, prior to displaying the pixel on-screen, we can compensate for the gamma adjustment due to the display. This is equivalent to .
This is a very simple and straightforward algorithm to implement, and is demonstrated below, complete with sample images. The real trick with gamma correction is determining the gamma value to use in practise. You can either design algorithms to compute the ideal gamma value mathematically, or you can simply “guess and test” values and evaluate the results yourself. This subject extends beyond the scope of this subsection, and will be covered in future tutorials.
function output_image = gammac(input_image,gamma_value) % gammac - Normalizes image intensity values over [0,1] and % performs gamma correction on it % output_image - The finished image % input_image - The source image data % gamma_value - The gamma value to use % (C) 2010 Matthew Giassa, <email@example.com> www.giassa.net %========================================================================== %Make a grayscale copy of our input image I = double(rgb2gray(input_image)); %Determine input image dimensions [j k] = size(I); %Determine the extreme intensity values for our input image in_min = double(min(min(I))); in_max = double(max(max(I))); %Determine the extreme intensity values for the output image out_min = double(1); out_max = double(1); %Determine the amount to "shift/move" pixel intensity values by shift_val = in_min - out_min; %Determine the value to "scale" pixel intensity values by scale_val = (out_max)/(in_max-in_min); %Perform the shift and scale (in that order) for counter1 = 1:j for counter2 = 1:k I(counter1,counter2)=(I(counter1,counter2)-double(shift_val))*double(scale_val); end end %Perform the gamma correction operation for counter1 = 1:j for counter2 = 1:k I(counter1,counter2)=(I(counter1,counter2)).^gamma_value; end end output_image = I;