Skip to content

Tutorial and exercises

This practical starts from a basic idea: a digital image can be understood as a matrix of pixels. Each pixel has a position in the image and contains visual information, either one gray-level value or several values associated with color. For this reason, concepts such as spatial resolution, quantization and the color model are important before applying any image-processing technique.

Throughout the tutorial, the operations are simple, but each one has a clear conceptual meaning. Some operations modify only pixel values, while others change the size, position or apparent shape of the image. Understanding this difference helps connect this first practical with later topics such as filtering, segmentation and object description.

Basic image manipulation

We load the images

I = imread("Imagenes\lena_gray_512.tif");
F = imread("Imagenes\lena_color_512.tif");

We display the images

imshow(I)

figure_0.png

imshow(F)

figure_1.png

We calculate the spatial resolution of each image

Spatial resolution indicates the number of pixels that form the image in width and height. A higher resolution can represent more detail if the original capture contains that detail, but resizing an already digitized image does not add real information. When an image is enlarged or reduced, new values must be estimated from existing pixels, and this may smooth details or create artifacts.

size(I)
ans = 1x2
   512   512
size(F)
ans = 1x3
   512   512     3

We calculate the intensity resolution, or quantization, of each image

Intensity resolution, also called quantization, indicates how many different levels each pixel can represent. In an 8-bit image there are 256 possible values, from 0 to 255. If this number of levels is reduced, different scene intensities are represented by the same value, which may cause loss of fine contrast or visible banding in smooth regions.

max(max(I))
ans = uint8245
max(max(F))
ans = 1x1x3 uint8 array
ans(:,:,1) =

   255

ans(:,:,2) =

   248

ans(:,:,3) =

   225

We spatially rescale the image

Spatial rescaling means changing the number of pixels in the image. If the image is reduced, samples are removed and detail may be lost. If the image is enlarged, intermediate values must be estimated by interpolation; therefore, enlargement does not recover real detail that was not present in the original image.

I = imresize(I,[128,128]);
imshow(I)

figure_2.png

I = imresize(I,[64,64]);
imshow(I)

figure_3.png

I = imresize(I,[16,16]);
imshow(I)

figure_4.png

I = imread("Imagenes\lena_gray_512.tif");
I = imresize(I,[1028,1028]);
imshow(I)

figure_5.png

We rescale the intensity levels of the image

Rescaling intensity levels means changing the range of pixel values. This operation can make an image look brighter, darker or higher contrast, but it does not change the position of objects. It is useful for preparing images before applying thresholds or filters.

levels_gray = [128,32,4];

I_128 = imquantize(I, linspace(0, 255, levels_gray(1)));
I_32 = imquantize(I, linspace(0, 255, levels_gray(2)));
I_4 = imquantize(I, linspace(0, 255, levels_gray(3)));

imshow(I_128,[])

figure_6.png

imshow(I_32,[])

figure_7.png

imshow(I_4,[])

figure_8.png

We calculate the histogram

The histogram is a simple tool for studying the distribution of intensity levels in an image. It shows how many pixels have each value, but it does not preserve information about their position. Even with this limitation, it is very useful for detecting whether an image is dark, bright, low contrast, or whether it contains groups of intensities that could be separated with a threshold.

histograma = imhist(I);

We visualize the histogram

bar(histograma)

figure_9.png

We calculate each of the color channels of the image by "removing" the other two

Separating color channels helps show that the color of a pixel is a combination of several components. In the RGB model, each channel represents the contribution of red, green or blue. In the HSV model, color is described using hue, saturation and perceived intensity, which can be more suitable for some visual-analysis tasks.

F_rojo = F;
F_rojo(:,:,2)=0;
F_rojo(:,:,3)=0;

F_verde = F;
F_verde(:,:,1)=0;
F_verde(:,:,3)=0;

F_azul = F;
F_azul(:,:,1)=0;
F_azul(:,:,2)=0;

imshow(F_rojo)

figure_10.png

imshow(F_verde)

figure_11.png

imshow(F_azul)

figure_12.png

We convert the image from RGB to grayscale

Converting to grayscale reduces a three-channel image to a single intensity image. This is usually not a simple average of the channels, but a weighted combination that takes visual sensitivity to red, green and blue into account. Color information is lost, but analysis is simpler when color is not essential.

F_gris = rgb2gray(F);
imshow(F_gris)

figure_13.png

We convert the image from RGB to HSV

The HSV model separates color information into three more interpretable components: hue indicates the type of color, saturation indicates color purity, and value indicates brightness. This separation can help in tasks where color should be analyzed more independently from illumination.

F_hsi = rgb2hsv(F);
imshow(F_hsi)

figure_14.png

We calculate the H, S and V channels

imshow(F_hsi(:,:,1))

figure_15.png

imshow(F_hsi(:,:,2))

figure_16.png

imshow(F_hsi(:,:,3))

figure_17.png

Gaussian noise

Noise represents unwanted changes in pixel values. It may appear because of limitations in the sensor, illumination, transmission or digitization process. In Gaussian noise, many pixels undergo small changes around an average value, so the image keeps its general structure but obtains a more grainy appearance.

We define the Gaussian noise parameters, that is, the mean and variance

media = 0;
varianza = 0.01;

The imnoise function adds multiplicative Gaussian noise, which can be easily detected by its grainy appearance

In multiplicative noise, the perturbation depends on the pixel value. This means that brighter regions may be affected differently from darker regions. This model is useful for understanding noise that is not added equally across the whole image.

I_gaussian_mult = imnoise(I,"gaussian",media,varianza);

imshow(I_gaussian_mult)

figure_18.png

Whereas additive Gaussian noise, which is added directly to the image, only slightly blurs the details

In additive noise, each pixel receives a random variation that is independent of its original value. The image keeps its general structure, but small local differences may be contaminated. This is why many smoothing filters try to reduce this type of local variation.

ruido_gaussiano = uint8(randn(size(I)) * sqrt(varianza) + media);

I_gaussian_add = I + ruido_gaussiano;

imshow(I_gaussian_add)

figure_19.png

Salt and pepper noise

Salt-and-pepper noise is an example of impulsive noise. Unlike Gaussian noise, it does not affect all pixels smoothly, but replaces some values with extreme intensities, usually black or white. This behavior explains why, in later practicals, nonlinear filters such as the median can be more appropriate than a simple average.

We define the parameters of salt and pepper noise, namely, the probability that a given pixel becomes a salt or pepper grain

probabilidad = 0.05;

Generate a random image that we use to change the pixels of the original image to salt or pepper points (if a random pixel is above or below the defined probability, the corresponding pixel in the original image is changed)

ruido_sal_pimienta = rand(size(I));
I_ruido = I;

I_ruido(ruido_sal_pimienta < probabilidad/2) = 0; % "salt" noise
I_ruido(ruido_sal_pimienta > 1 - probabilidad/2) = 255; % "pepper" noise

imshow(I_ruido)

figure_20.png

Arithmetic operations

Arithmetic operations act directly on the numerical values of pixels. Adding or subtracting can modify brightness, while multiplying or dividing can change global contrast. It is important to remember that digital images have a limited value range; if an operation produces values outside that range, information may be lost because of saturation.

I = imread("Imagenes\lena_gray_512.tif");

F = imread("Imagenes\Contornos.tif");

G = imread("Imagenes\Office1.png");

H = imread("Imagenes\Office2.png");

G = rgb2gray(G);

H = rgb2gray(H);

montage({I,F,G,H})

figure_21.png

Arithmetic addition by a constant

I_50 = I + 50;

We rescale so that we can operate with the images and use the add function to add them

s = size(G);

H = imresize(H,[s(1),s(2)]);

GH_add = imadd(G,H);

montage({I_50,GH_add})

figure_22.png

Subtraction by a constant

I_75 = I - 75;

We rescale so that we can operate with the images and use the subtract function to subtract them

F = rgb2gray(imresize(F,[512,512]));

IF_resta = imsubtract(I,F);

montage({I_75,IF_resta})

figure_23.png

Product by a constant

I_2 = I.*2;

We rescale so that we can operate with the images and use the immultiply function to multiply them

IG_prod = immultiply(I,imresize(G,[512,512]));

montage({I_2,IG_prod})

figure_24.png

Division by a constant

I__2 = I./2;

We rescale so that we can operate with the images and use the imdivide function to divide them

I_div = imdivide(I,imresize(G,[512,512]));

montage({I__2,I_div})

figure_25.png

Logical operations

In a binary image, each pixel can belong only to the background or to the object. Therefore, logical operations can be interpreted as operations between sets of pixels: AND corresponds to intersection, OR to union and NOT to complement. This way of thinking will be especially important when mathematical morphology is introduced.

We binarize the images by thresholding

G_bin = G>128;

H_bin = H>128;

We calculate the logical AND (equivalent to the intersection if the objects are defined by 1)

A = and(G_bin,H_bin);

imshow(A

figure_26.png

We calculate the logical OR (equivalent to the union if the objects are defined by 1)

O = or(G_bin,H_bin);

imshow(O)

figure_27.png

We calculate the logical XOR

X = xor(G_bin,H_bin);

imshow(X)

figure_28.png

We calculate the logical NOT

N = not(G_bin);

imshow(N)

figure_29.png

Operations with pixel intensity

Intensity transformations are point operations, because each pixel is modified according to its own value and not according to its neighborhood. This type of operation does not move objects or change image geometry; it redistributes gray levels. It can be used to brighten dark regions, compress very bright regions or adjust global contrast.

We calculate the negative image

The negative image inverts intensity levels: dark values become bright and bright values become dark. This transformation is simple, but it helps show that many point operations only redistribute values without changing the geometry of the scene.

I_neg = 255-I;

imshow(I_neg)

figure_30.png

We calculate the logarithmic transformation (with constant c = L-1/log(L) and L gray levels of the image). The logarithmic transformation brightens dark areas while preserving areas that were already bright.

imagen = imread("Imagenes\Espalda.jpg");
imagen = rgb2gray(imagen);
imshow(imagen)

figure_31.png

imagen_log = (255/(log(256)))*log(double(imagen) + 1);

imshow(uint8(imagen_log))

figure_32.png

We calculate the exponential transformation (with constant c = L-1/log(L) and L gray levels of the image). The exponential transformation darkens bright areas while preserving areas that were already dark. It is the inverse of the logarithmic transformation.

imagen_1 = imread("Imagenes\cells_internet.jpg");

imshow(imagen_1)

figure_33.png

imagen_exp = exp((log(256)/255)*double(imagen_1))-1;

imshow(uint8(imagen_exp))

figure_34.png

Power-root transformation or gamma correction: It combines and generalizes the logarithmic and exponential transformations

Gamma correction provides flexible control over the relation between input and output intensities. If gamma is smaller than 1, mainly dark regions are brightened; if gamma is greater than 1, mainly bright regions are darkened. It is widely used because it better matches visual perception and display devices.

First step: I transform the intensity values from the interval \(begin:math:display\)0\,255\(end:math:display\) to \(begin:math:display\)0\,1\(end:math:display\)

imagen_1_gamma = (1./255.)*double(imagen_1);

Second step: I define the gamma value and transform

Third step: I transform the intensity values back to the interval \(begin:math:display\)0\,255\(end:math:display\)

gamma = 20;

imagen_1_gamma = uint8(imagen_1_gamma.^gamma * 255);

imshow(imagen_1_gamma)

figure_35.png

Histogram expansion and equalization

Histogram expansion and equalization try to use the available intensity range more effectively. Expansion spreads values between a minimum and a maximum, while equalization seeks a more uniform distribution. These techniques can improve contrast, but they may also exaggerate noise or give unnatural results when illumination is uneven.

We calculate the histogram

h = imhist(I);
bar(h)

figure_36.png

We calculate the maximum and minimum intensity values to expand the histogram

maxi = double(max(max(I)));
mini = double(min(min(I)));
I_exp = 255.*(double(I)-mini)./(maxi-mini);

imshow(uint8(I_exp))

figure_37.png

h_exp = imhist(uint8(I_exp));
bar(h_exp)

figure_38.png

We equalize the histogram, which uniformly distributes the intensity values across the entire interval

I_eq = histeq(I,256);

imshow(I_eq)

figure_39.png

h_eq = imhist(I_eq);
bar(h_eq)

figure_40.png

Geometric operations

Geometric operations change pixel positions or the apparent size of objects. In rotations, translations or scale changes, the new positions do not always match the discrete pixel grid exactly. For this reason, values must be interpolated, and the interpolation method can influence sharpness, edges and the visual quality of the result.

Rotation by angle theta

I_rot = imrotate(I,45);

imshow(I_rot)

figure_41.png

Translation in the (x,y) direction

I_tras = imtranslate(I,[50,150]);

imshow(I_tras)

figure_42.png

Translation with full image

I_tras = imtranslate(I,[50,150],'OutputView','full');

imshow(I_tras)

figure_43.png

Vertical and horizontal symmetries

I_simv = flip(I,1);
I_simh = flip(I,2);

montage({I_simv,I_simh})

figure_44.png

Dilation and reduction by factors d and r.

dilatacion_factor = 2;
reduccion_factor = 0.5;

I_dil = imresize(I,dilatacion_factor);
I_red = imresize(I,reduccion_factor);

montage({I_dil,I_red})

figure_45.png

For some of the geometric transformations, the interpolation method must be chosen (e.g., rotations and translations)

Interpolation defines how a pixel value is computed when a geometric transformation places a coordinate between existing pixels. Nearest-neighbor interpolation is fast but can produce jagged edges; bilinear interpolation gives smoother results; and bicubic interpolation usually preserves continuity better, although it is more expensive.

I_rot_n = imrotate(I,45,'nearest');
I_rot_b = imrotate(I,45,'bilinear');
I_rot_bb = imrotate(I,45,'bicubic');

imshow(I_rot_n)

figure_46.png

imshow(I_rot_b)

figure_47.png

imshow(I_rot_bb)

figure_48.png