15 November, 2014

A tutorial on intensity transforms of images

To open the application about the topic, open the following post: Interactive demonstration of intensity transforms

Image processing often requires transforming the intensity values for example to turn the image easier to process or to highlight certain objects. In this post an effective way of some simple transforms are explained and an interactive demonstration is also available.

Basic intensity transforms

Intensity transforms – according to the name – change the intensity values, most often in the range of [0, 255]. A basic t transform is a function, mapping all the possible input values to output values also in the range of [0, 255]. The simplest t transform is the identity, which maps all input values to themselves:

$$t_{identity}(x) = x$$

The function looks like this:

Inverting the image is very similar:

$$t_{invert}(x) = 255 - x$$

The transform – marked by red – looks like this:

Brightness

Changing the brightness of an image is as simple as shifting the values by a given a constant.

$$t_{brightness}(x) = x + c$$

If c is positive, the image gets brighter, otherwise it will be darker. Of course the output of t is limited into the range [0, 255], so the exceeding values will be saturated. Also we have to take into consideration, that this may cause some loss of information, as you can see on the following picture:

Here we increased the brightness by a constant of 64, the transform is marked by red. On the upper part more input values get the output value of 255: here we loose information, since the transform can not be inverted.

Here you can see the result of the transform on the classic cameraman picture, the image has been brightened.

Threshold

Thresholding is a binary operation, where we set an l limit: all intensities below l will be false and true otherwise.

$$t_{threshold}(x) = \begin{cases} 0: & \text{n < l} \\ 1: & \text{n} \ge \text{l} \end{cases}$$

In the following transform we set l to 128, and the true value is 255, because we are dealing in the [0, 255] range.

Here is the output for the cameraman image:

Gamma correction

Gamma-correction transforms intensities in a nonlinear way:

$$t_{gamma}(x) = A\cdot x^\gamma, \quad x \in [0, 1]$$

A often equals to 1. Before applying gamma correction, we have to normalize our intensities into the [0, 1] range, then transform the values, and then scale again into the [0, 255] range. Here you can see the transform for γ = ⅓:

As you can see, only the very dark regions remain dark, other intensities get brighter, as the sample output for the cameraman image shows. This transform turns some regions of the image more visible, for example have a look at the pants of the man:

Contrast

Increasing the contrast can be achieved in many ways, for example sigmoid-like function is great for this task. Basically increasing the contrast means making the dark regions darker and bright regions brighter, as you can see in the following transform:

By shifting the sigmoid function left or right, the tone of the resulting image can be modified. If we want to decrease the contrast, the amplitude of the sigmoid can be decreased. Here you can see an example of the increased contrast:

Applying transforms in MATLAB

Most of the transforms above can be achieved by a single line of MATLAB code. For example if we want to invert the image or brighten it, we write:

% read the image

% invert it
invert = 255 - image;

% brighten it
bright = image + 64;


Even thresholding is very easy:

% set the threshold at 128
binary = image > 128;


Applying gamma correction is much harder, if we want to use the classic equation:

% normalize first
gamma = image / 255;

% gamma correction of gamma = 1/3
gamma = gamma .^ (1/3);

% get back to the [0, 255] range
gamma = gamma * 255;

% convert the values to uint8
gamma = uint8(gamma);


Of course this also could be written in one single line, but it would make much harder to understand the code. You can imagine how difficult it would be to apply contrast-transform this way. So let us take another point of view.

Instead of calculating the transformed value for each pixels individually – as we did it in the examples before – , now calculate the output values for all possible inputs, and store them in an array. Some examples:

% all possible inputs
x = 0 : 255;

% invert
y_i = 255 - x;

% brightness: we do the saturation manually
y_b = min(255, 64 + x);

% gamma
y_g = uint8(255 * (x / 255) .^ (1/3));

% contrast: generate a sigmoid function
y_c = uint8(255 ./ (1 + exp(-12 * (x ./ 255 - 0.5))));

% plot them
plot(x, y_i, 'b', 'linewidth', 2); % blue
axis('square');
hold on;
plot(x, y_b, 'g', 'linewidth', 2); % green
plot(x, y_g, 'r', 'linewidth', 2); % red
plot(x, y_c, 'k', 'linewidth', 2); % black


Here you can see the result and have a look at our mapping functions:

Here comes the trick: use 1-dimensional interpolation to transform the image! As the MATLAB documentation of interp1 says, it is basically a table lookup, having the following form:

vq = interp1(x, v, xq)


Where x and v contains point pairs of the function, xq holds the query points and the results are returned in vq. Have a look at the following simple example:

% describes three points of an invert-like function
% in the range of [0, 255]
x = [0, 127, 255];
v = [255, 127, 0];

% display the function
plot(x, v);

% interpolate for some points
xq = [0, 64, 8, 200, 210, 1];
vq = uint8(interp1(x, v, xq))

% display the results
plot(xq, vq, '+');


The output is:

vq =
255  190  247   55   45  254


It works fast and great. We can use it to transform our image in the same way:

% invert an image using interpolationresult = interp1(x, y_i, image);


The advantage of this method is, that we have only to calculate the mapping function, and then simply apply a transform. In addition, we can describe our transform with a few points, because interpolation calculates us all the values. This way we can work efficiently even with any complex transforms, since this approach is a generalized one.