The Monash Image Library is a library of over 200 C functions for loading, saving and manipulating digital images. It was developed internally at the Department of Computer Science (now School of Computer Science and Software Engineering) at Monash University and is used by both staff and students.
This document is intended to provide an introduction to setting up and using the Monash Image Library under Linux. It is assumed the reader is familiar with C programming and has some basic understanding of Linux, the command line, and Makefiles. The aim has been to teach how to use the library functions by using programming examples as much as possible so that the reader can quickly commence writing their own programs.
Firstly, some simple ``image'' concepts will be reviewed. Then we show how to set up your Linux account for using the Monash Image Library. The essential data types and functions available to the programmer will then be described, followed by a series of explained C code examples. The use of command line versions of functions and Unix pipes will finish the discussion.
The word ``image'' is used in the computing world to describe the pictures and graphics that are stored digitally and can be displayed on an output device such as your computer screen or printer.
The Monash Image Library has a slightly more restrictive view of what an image is. It considers an image to be a rectangular grid of pixels. An image therefore has 3 main attributes: the height or number of rows in the image, the width or number of columns in the image, and the type of pixel at each grid position.
The word ``pixel'' is short for ``picture element''. Each pixel occupies one position in the rectangular grid which makes up an image. We usually think of pixels as having a particular ``colour''. The Monash Image Library supports many different pixel types but for most purposes you will only need to use the following two types:
A greyscale image is one in which the pixels are all shades of grey, much like the picture that an old monochrome or ``black and white'' television set provides. The different shades correspond to different intensities of light, ranging from black to grey to white.
A typical greyscale image would have 256 different intensity levels. The Monash Image Library uses integers to describe these intensities, with 0 representing the darkest shade (ie. black) and 255 the brightest (ie. white). Shades in between black and white take on intermediate values. For example, a dark grey might have intensity value 37.
For the computer to store a pixel intensity which could take on 256 different values, it requires 8 bits per pixel, as 28 = 256. An image whose pixels only ranged from 0 (black) to 15 (white) would only need 4 bits per pixel, as 24 = 16, log216 = 4.
Colour images are slightly more complicated. The Monash Image Library supports many different colour image formats, but I will only describe the simplest and most common one which will be used - the RGB format.
In the RGB format, each pixel is represented by a three integers: a red component, a blue component and a green component. Usually we use 8 bits per component, which results in 24 bits per pixel overall.
The colour of the pixel is generated by mixing the three primary components. For example, the brightest purest green would have the RGB values (0,255,0). That is, no red, full green and no blue. Mixing red and green gives yellow, so pure yellow would be (255,255,0), and a medium purple (or indigo, violet) shade could be represented by the RGB triplet (136,0,136). Black is (0,0,0) and white is (255,255,255).
From the previous section we have learnt that an image is just a two-dimensional rectangular grid of pixels. Each pixel has a coordinate or position in the grid (which row and column it is in). In the Monash Image Library pixels are represented by integers. Greyscale pixel intensities are represented by a single integer, usually ranging from 0 to 255 (black to white). Colour pixels require a vector of 3 integers containing the red/green/blue components.
To use the various image library and utility programs you need to set some Unix environment variables. How this is done varies depending on which Unix shell you use. Typing finger $USER when you've logged in will tell you which shell you are using.
Add the following lines to the end of $HOME/.bashrc file:
export MILHOME=/cs/cc/lib/mil
export PATH=${PATH}:${MILHOME}/bin
export IMAGES=${MILHOME}/images/grey:${MILHOME}/images/colour:.
You can now log out and log back in or just type:
source $HOME/.bashrc ; hash -r
Add the following lines to the end of $HOME/.tcshrc file:
setenv MILHOME /cs/cc/lib/mil
setenv PATH ${PATH}:${MILHOME}/bin
setenv IMAGES ${MILHOME}/images/grey:${MILHOME}/images/colour:.
You can now log out and log back in or just type:
source $HOME/.tcshrc ; rehash
User applications which call functions in the library must #include <image.h> and link with -limage -lX11 -lm. This header file and the library of functions must be specified when compiling.
The best way is to copy and adapt the Makefiles supplied with the example programs. However if you need to to compile prog.c to prog on the command line you would (all on the one line) type:
The example C programs in Sections 5-8 of this document can also be found in $MILHOME/examples.
Make sure you examine the source code of the examples and the Makefiles carefully before beginning the assignments. To compile and test them, you will have to first copy the directories to your home directory structure somewhere. Then type make. Assuming you have set up your PATH variable properly, you will end up with an executable which you can try out.
Remember to type make clean when you are done. This will remove the .o files, any core files and the usually very large executable.
You must set the $MILHOME environment variable to where you installed the Monash Image Library eg. setenv MILHOME $HOME/mil means you didn't set the appropriate environment variables as described in Section 3.1
Segmentation fault dumped or Bus error or Trace BPT trap and Abort (core dumped) means that you have probably incorrectly used a pointer variable in your program, such as going over the end of an array or using an uninitialized pointer.
sh: xv: command not found means that you do not have the xv program in your PATH or even installed at all. This needs to be installed.
sh: less: command not found means that you do not have the less file viewer program in your PATH or even installed at all. You need to either install it or make a symbolic link to another pager program such as more or cat.
To print images to the Computer Centre printers you must first convert the
images to the Postscript format. This can be done either with xv
(which also allows you to cut and paste images together) or with the
idump
command. For example, assuming the printer outside Room 107
is called cl_ctl_107_ps, you could type:
idump mark.gif mark ps (Note the space before ps)
lpr -h -x -Pcl_ctl_107_ps mark.ps
Ideally, you should preview the Postscript file before printing so
any errors can be caught. To do this you can use ghostview or
gs. eg. gs mark.ps
$MILHOME/images/grey/ has greyscale images suitable for enhancement, segmentation and edge detection algorithms. $MILHOME/images/colour/ has some colour (24 bit RGB) images.
The image library programs you write, their object files, and the images and Postscript files you generate all consume large amounts of disk space. It is in your better interests to only maintain the source files and details of how the images were generated so that they can be reproduced only when required.
If you have any problems with the image library, please ensure that you have carefully read the handouts provided, the example programs, and the help pages provided by ihelp. Fellow students are also a good source of information, as they are probably having or have had similar difficulties.
If you have serious problems with the library, or you believe you have found a bug in any of the routines, please email Torsten Seemann at torsten@csse.monash.edu.au with the string ``Monash Image Library'' in the Subject.
The Monash Image Library has one fundamental data type which is a C structure called IMAGE. It has many different fields but only the four which are relevant to most programmers are shown below:
typedef struct IMAGE_T
{
long rows; /* number of scan lines */
long cols; /* pixels per scan line */
long bitsperpixel; /* pixel size */
char name[I_MAXNAM]; /* name of image */
/* Have a look in image.h for the rest of the fields */
}
IMAGE;
When writing programs an IMAGE structure is always accessed via a pointer like the picture variable shown below. To access the fields the C -> notation must be used.
IMAGE* picture = 0;
/* ... Open or create an image here with other functions ... */
printf("rows = %ld\n", picture->rows);
printf("cols = %ld\n", picture->cols);
printf("bpp = %ld\n", picture->bpp );
printf("name = %s\n", picture->name);
Image level functions are those which operate on or return whole images (via IMAGE pointers). The functions to create empty images, load, save and display images are examples of these. Here is a list of the most common image level functions:
IMAGE *i_open(char *filename)
Pixel level functions are those which manipulate individual pixels within an image. Here is a small list of the more commonly used pixel level functions:
int i_getpix(IMAGE *image, int row, int col)
This example will take an image filename as a parameter and display both the image and its histogram on the screen.
#include <image.h>
int main(int argc, char *argv[])
{
IMAGE* in;
IMAGE* hist;
if (argc < 2)
i_error(I_FATAL, "Usage: %s <image>", argv[0]);
in = i_open(argv[1]);
i_xv(in);
hist = i_hist(in);
i_xv(hist);
i_close(in);
i_close(hist);
return 0;
}
Line 1 includes the standard Monash Image Library header file. In lines 5 and 6 we create two local variables which are pointers to IMAGE types. These pointers do not point to anything yet - they will be used later in the program to point to IMAGEs returned by other functions.
Lines 8-9 just check if the user has supplied an image filename as a parameter to the program. If not, it calls the i_error function which will print out an error message explaining how to use the program, and then exit.
Line 11 uses the i_open function to open an existing image from disk; here we pass it the first command line parameter. The function returns a pointer to the image, which is stored in in. This in pointer variable can then be passed to other functions which operate on the image in some way.
For example, in line 12 we pass it to the i_xv function. This function will display the image on the screen in a new window.
Line 14 uses the i_hist function which takes our original input image (via the in pointer) and returns a pointer to a new image which provids a graphical histogram and some statistics about the original image. In line 15 it is also displayed on the screen.
In lines 17 and 18 we close the two images we have created (one was created using the i_open function, and one using the i_hist function). This will free up any memory that the images were using.
Running ex1 parts.gif results in Figures 1 and 2.
This example will show how to create a new blank image, modify the individual pixels in the image, display the image and save the image to disk.
#include <image.h>
int main(int argc, char *argv[])
{
IMAGE* out;
int row, col, value;
out = i_mktemp(256, 256, 8);
i_zero(out);
for (row=0; row < out->rows; row++)
{
for (col=0; col < out->cols; col++)
{
value = col;
i_putpix(out, row, col, value);
}
}
i_xv(out);
i_dump(out, "pattern", "gif");
i_close(out);
return 0;
}
The start of the program is similar to Example 1. In line 8 we use the i_mktemp function to create a new image which has 256 rows, 256 columns, and 8 bits per pixel. ie. a small square greyscale image which can hold pixel values from 0 to 255. Line 8 uses the i_zero function to set each pixel in the image to value 0, which corresponds to black.
For this example we will draw a smooth surface on the image going from black on the left to white on the right. That is, all the pixels in column 0 will have grey level 0, column 1 has value 1, until the final column 255 has pixels with value 255.
Lines 11-18 show two nested for loops. The outer loop goes through each row one by one and the inner loop goes across each column in each of those rows. Note that we are accessing the rows and cols members of the out IMAGE structure.
The body of the two loops is in lines 15-16. In line 16 the i_putpix function is used to set the pixel of the out image at the specified row and column to value.
Line 20 displays the image in a new window on the screen. Line 21 uses the i_dump function to save the out image to disk in the gif format resulting in the filename pattern.gif. The image can be viewed using the ixv command (not C function - see Section 9) on the Unix command line. eg. ixv pattern.gif
Running ex2 results in the output of in Figure 3.
This program opens an existing image and creates a new image which is the ``photographic negative'' of it. The program works on greyscale images of any size and any bits per pixel.
#include <image.h>
int main(int argc, char *argv[])
{
IMAGE* in;
IMAGE* out;
int row, col, value;
int maxPixelValue;
if (argc < 2)
i_error(I_FATAL, "Usage: %s <image>", argv[0]);
in = i_open(argv[1]);
out = i_mktemp(in->rows, in->cols, in->bitsperpixel);
maxPixelValue = (1 << in->bitsperpixel) - 1;
for (row=0; row < out->rows; row++)
{
for (col=0; col < out->cols; col++)
{
value = maxPixelValue - i_getpix(in, row, col);
i_putpix(out, row, col, value);
}
}
i_xv(in);
i_close(in);
i_xv(out);
i_close(out);
return 0;
}
Line 13 opens the specified image. Line 14 creates a new empty image which has the the same dimensions and same pixel type. It does this by accessing the rows, cols and bpp members of the out IMAGE structure.
Line 16 computes the largest pixel value that is allowed. This is equal to 2bitsperpixel-1. For example an 8 bpp image would come out to be 255, and we know from previous examples that these images have pixels which range from 0 (black) to 255 (white).
To ``negate'' an image means that our idea of black and white are reversed: black becomes white, white becomes black, dark grey become light grey etc. So for an 8 bpp image we can achieve this by subtracting the old pixel value from 255 to get the new pixel value.
Lines 18-25 loop through each pixel, using the i_getpix function to read the original pixel value from in, and then placing the reversed pixel value into the out image using the i_putpix function.
The original and ``negative'' images are then both displayed on the screen and then closed.
Figures 4 and 5 shows the result of running ex3 mark.gif.
This program will read in a 24 bit per pixel colour image and output
an 8 bit per pixel greyscale version of the image. The formula
|
#include <image.h>
int main(int argc, char *argv[])
{
IMAGE* in;
IMAGE* out;
int row, col;
int r, g, b;
int maxPixelValue;
int intensity;
if (argc < 2)
i_error(I_FATAL, "Usage: %s <colour_image>", argv[0]);
in = i_open(argv[1]);
if (in->bitsperpixel != 24)
{
i_close(in);
i_error(I_FATAL, "This program only works on 24 bpp colour images.");
}
maxPixelValue = (1 << in->bitsperpixel) - 1;
out = i_mktemp(in->rows, in->cols, 8);
for (row=0; row < out->rows; row++)
{
for (col=0; col < out->cols; col++)
{
i_getrgb(in, row, col, &r, &g, &b);
/* Or we could have used the following three lines:
r = i_getred(in, row, col);
g = i_getgreen(in, row, col);
b = i_getblue(in, row, col); */
intensity = (int) sqrt( (r*r + g*g + b*b) / 3.0 );
if (intensity < 0)
intensity = 0;
else if (intensity > maxPixelValue)
intensity = maxPixelValue;
i_putpix(out, row, col, intensity);
}
}
i_close(in);
i_xv(out);
i_close(out);
return 0;
}
In lines 17-20 we check to see that the input image is a 24 bpp colour image. If not, we print out an error message and exit. In line 25 we create an empty greyscale output image of the same dimensions.
The body of the program is the familiar double loop through rows and columns. The i_getred, i_getgreen, i_getblue functions can be used to extract the three colour components from a colour image just as the i_getpix function does for a greyscale image. However, as it is common to require all three components at once, the more efficient i_getrgb function is used. Notice that we pass three pointers to integers to this function so that it can modify the r,g,b variables.
Line 38 applies the supplied formula to produce an intensity value. Lines 40-43 are used to ensure that the intensity value falls into the legal range for an 8 bpp image, that is 0 to 255. In line 45 we place the clipped pixel value into the output image. The images are then closed and displayed.
Running ex4 lenna_512_512.rgb results in our colour input image being converted into a greyscale version, as shown in Figures 6 and 7.
So far we have shown how to use the Monash Image Library library of C functions in your own C programs to open, save, manipulate and view images stored on disk. However most of the image level functions are also available as programs which can be directly run on the Unix command line.
In some of the example programs we used the i_xv C function to display an image (stored in an IMAGE variable) in a new window on the screen. If you just need to temporarily view an image you can use the corresponding command line program ixv. Notice that is has the same name as the C function, but without the underscore ``_'' character in it. To view an image called landscape.cif you would simply type the following:
ixv landscape.cif
By just typing ixv by itself on the command line you will be given some help describing the C function and the command line versions of the function. For ixv it would print something like this:
display image using xv as a forked child process
Usage: ixv [-b{123a}] in
Function prototype: no transformation or return value
void i_xv(IMAGE *image)
Here are some of the more useful command line programs you may wish to use:
ixv in
These command line functions become more powerful when used in conjunction with Unix pipes. A pipe allows you do use the output of one program as the input to the next. It does this by using the special Unix files stdin and stdout. All the image level functions in the Monash Image Library either just take an image as input (and output non-image info), produce an image as output, or have an image as both input and output.
The special filename ``-'' (the minus or dash character) is used to tell the Monash Image Library to use stdin or stdout instead of reading/writing a filename to/from the disk. Here is an example of displaying the histogram of an image called mandrill.cif:
ihist mandrill.cif - | ixv -
Usually, instead of the first minus sign, you would put the name of the file you wanted to save the histogram image into, but here we want to send it to the next program in the pipe which is ixv. The pipe character ``|'' is used to separate the two programs in the pipe (this is standard Unix shell syntax). The second minus sign (which is in place of the name of a filename to view) says that the input file will be coming from the previous program in the pipe (assuming it used a minus sign on its output, which it has).
This is useful because it is quicker than writing and compiling a program, you don't need temporary files, and the pipes can consist of a series of programs, not just two.
isob mandrill.cif - | ibinary - - 40 | ixv -
For example, the above line will do Sobel edge detection on the mandrill.cif image, perform binary edge detection with threshold 40 and then display the result.