08 December, 2013

Visualizing cross sections of a grayscale image

Visualization is an important task in image processing: a good figure may help a lot in understanding a problem. In this post a cross section is visualized in context of its source image.

The visualization process contains the following steps:

• Read the source image and sample the given row
• Generate the mask of the area for visualization
• Create a the output RGB image
• Copy the mask onto the red channel and highlight the sampled line

At the end of the post a full code example is found for better understanding.

Read the source image and sample the given row

We suppose a 8-bit gray-scale input image, where each pixel has an intensity between 0 and 255:

$$\begin{matrix} I_{1,1} & I_{1,2} & \cdots & I_{1,n} \\ \color{red}{I_{2,1}} & \color{red}{I_{2,2}} & \color{red}{\cdots} & \color{red}{I_{2,n}} \\ \vdots & \vdots & \ddots & \vdots \\ I_{m,1} & I_{m,1} & \cdots & I_{m,n} \end{matrix}$$

The sampling is done by simply indexing a whole row: the second row is marked by red in the figure. All the values in a gray-scale image are between 0 and 255.

The image we will use is a chest-radiograph taken from Wikipedia:

Generating the mask of the area

The visualization of the cross section is generated by the method described in a previous post: selecting pixels of a given area, which returns a binary mask. For convenience, we shall flip the mask in vertical direction: in a regular plot y coordinates increase upwards, but downwards in the mask generated by the given method.

The flipped mask generated for the 200th row for the input image:

Create the output RGB image

The output RGB image shall have three colour channels:

$$\begin{matrix}\color{red}{R} & \color{red}{R} & \color{red}{\cdots} & \color{red}{R} & & \\ \color{red}{R} & \color{red}{R} & \color{red}{\cdots} & \color{red}{R} & \color{green}{G} & \\ \color{red}{\vdots} & \color{red}{\vdots} & \color{red}{\ddots} & \color{red}{\vdots} & \color{green}{G} & \color{blue}{B} \\ \color{red}{R} & \color{red}{R} & \color{red}{\cdots} & \color{red}{R} & \color{green}{\vdots} & \color{blue}{B} \\ & \color{green}{G} & \color{green}{G} & \color{green}{\cdots} & \color{green}{G} & \color{blue}{\vdots} \\ & & \color{blue}{B} & \color{blue}{B} & \color{blue}{\cdots} & \color{blue}{B} \\ \end{matrix}$$

To generate an RGB image from the input, repeat the input image along the third dimension. The RGB image itself remains gray, but now we have the ability to manipulate its channels independently. We will copy the mask into the red channel.

Why shall we put the mask into the red channel instead of the green or blue one? An RGB colour is converted to an intensity value by using the following formula:

$$I = 0.2126 \cdot R + 0.7125 \cdot G + 0.0722 \cdot B$$

It means, that by using the green channel, 70% of the intensity comes from the mask, which is too bright to see the image itself, but the 7% weight of the blue channel would not give enough intensity to produce a good contrast.

Copy the mask onto the output

During masking process we copy an object to an image, modifying the pixels covered by the object and leaving the rest of the image untouched. To see a simple example of this operation let us have a binary M mask containing a cross and copy it onto image I:

\begin{aligned} M &= \left[ \begin{matrix} 0 & 1 & 0 \\ 1 & 1 & 1 \\ 0 & 1 & 0 \end{matrix} \right] \\ I &= \left[ \begin{matrix} 3 & 8 & 7 \\ 1 & 4 & 2 \\ 6 & 0 & 7 \end{matrix} \right] \end{aligned}

Suppose, that the maximal intensity in I is 9, and we want highlight the pixels covered by the mask. An element-by-element maximum operation perfectly fits our needs, we have only to multiply the mask by the maximal intensity: this way, all of the pixels covered by the mask will be highlighted while the others will not be changed. See the following operation:

$$max(M \cdot 9, I) = \left[ \begin{matrix} 3 & 9 & 7 \\ 9 & 9 & 9 \\ 6 & 9 & 7 \end{matrix} \right]$$

The pixels covered by the cross were set to the maximal intensity.

After copying the mask onto the red channel and highlighting the sampled row, our output image looks like this:

Putting it all together

The following code example demonstrates the whole process for generating a sample output image:

% reading the source grayscale image

% sample the 460th row
r = 460
data = im(r, :);

% visualization of the area
A = bsxfun(@le, (0 : 255)', data);

% flip the area vertically and convert to grayscale
A = flipud(A);
A = uint8(A) * 255;

% create the RGB output image
rgb = repmat(im, [1 1 3]);

% highlight the sampled row
rgb(r, :, :) = 255;

% mask the area from the 150th row onto the 1st, red channel
s = 150;
rgb(s : (s + 255), :, 1) = max(rgb(s : (s + 255), :, 1), A);

% save the RGB output image
imwrite(rgb, 'visualization.png');


By generating a sample for each line and joining them into a video, we can get a nice animation: