• Home
  • Introduction to Computer Vision: First steps

Introduction to Computer Vision: First steps

In this chapter, we will discover our first structures and functions of OpenCV in learning how to open, display and save images using the module highgui. Although they are not particularly interesting on the theoretical level, these core functions will be reused in almost all our programs by the following.

Hello, world!

Your first program

Without further introduction, let’s take the code that I showed you at the end of the previous chapter, to which I added comments.

#include <stdio.h>
#include <stdlib.h>
#include <opencv/highgui.h>

int main (int argc, char* argv[])
{
  IplImage* img = NULL; 
  const char* window_title = "Hello, OpenCV!";

  /* Vérification: au moins un argument doit être passé au programme.*/
  if (argc < 2)
  {
    fprintf (stderr, "usage: %s IMAGE\n", argv[0]);
    return EXIT_FAILURE;
  }

  /* Chargement de l'image passée en argument */
  img = cvLoadImage(argv[1], CV_LOAD_IMAGE_UNCHANGED);

  if (!img)
  {
    fprintf (stderr, "couldn't open image file: %s\n", argv[1]);
    return EXIT_FAILURE;
  }

  /* Création d'une fenêtre intitulée "Hello, OpenCV!" */
  cvNamedWindow (window_title, CV_WINDOW_AUTOSIZE);

  /* Affichage de l'image */
  cvShowImage (window_title, img);

  /* Pause le temps que l'utilisateur appuie sur une touche */
  cvWaitKey (0);

  /* Destruction de la fenêtre */
  cvDestroyAllWindows ();
  
  /* Libération de la mémoire */
  cvReleaseImage (&img);

  return EXIT_SUCCESS;
}

As you have most likely realized, this program expects that you pass it one argument : the path to an image file on your computer.

Under Windows, simply, graphically, to take an image file and drag and drop on the executable of your program.

Otherwise, you can skip this image in the command line. For example, under GNU/Linux :

arnaud@netbook$ cd prg/c/vpo/helloworld
arnaud@netbook$ ./hello
usage: ./hello IMAGE
arnaud@netbook$ ./hello /home/arnaud/img/vpo/lena.jpg

Press a key on the keyboard the window closes and the program stops.

Analysis of the code

To begin, let’s take the code step by step, and analyze the steps together.

#include <stdio.h>
#include <stdlib.h>

/* ... */

int main (int argc, char* argv[])
{
  /* ... */

  /* Vérification: au moins un argument doit être passé au programme.*/
  if (argc < 2)
  {
    fprintf (stderr, "usage: %s IMAGE\n", argv[0]);
    return EXIT_FAILURE;
  }

It is thanks to this piece of code that we pass one argument to the program. As a reminder, the arguments passed to the program are contained in the form of character strings in the table argv (for “argument values“) and the length of this array is contained in the whole argc (“argument count“).

The first item of’argv (argv[0]) is the name by which the program was called. It is used here to display the help message (“use”), in the standard error output if the program was invoked without argument.

Let’s move on to the following, where we use clearly OpenCV.

#include <opencv/highgui.h>

IplImage* img = NULL; 

/* Chargement de l'image passée en argument */
img = cvLoadImage(argv[1], CV_LOAD_IMAGE_UNCHANGED);

if (!img)
{
  fprintf (stderr, "couldn't open image file: %s\n", argv[1]);
  return EXIT_FAILURE;
}

Here, two interesting things. The first is to declare that we are going to use a pointer to a structure IplImage. This structure, we will consider it as a “black box” in this chapter, and we will begin to dissect in the next chapter. For now, just remember that it is’an image in OpenCV.

We then use the function cvLoadImageto which we pass the first argument to the program (the path to an image file on your computer), as well as a constant which we will deal later. This function opens the image in which we pass the path as argument, and returns a IplImage* containing its data. In case of failure of the opening, as shown by the following condition, the function cvLoadImage returns the pointer NULL.

Here is the behavior of the program if I pass as argument the path to a file that it can’t load, or a file that doesn’t exist :

arnaud@netbook$ ./hello world
couldn't open image file: world

Well, now, the following :

const char* window_title = "Hello, OpenCV!";

/* Création d'une fenêtre intitulée "Hello, OpenCV!" */
cvNamedWindow (window_title, CV_WINDOW_AUTOSIZE);

/* Affichage de l'image */
cvShowImage (window_title, img);

Here’s what will make highgui your best friend to test your programs. These two lines are enough to create a window and display an image in it ! The windows in this library, are identified by their title. This means that you can not have two windows that have the same title, but you will agree that this limitation is worth the money, since out of this, you don’t even have to worry about the details of the display ; this is all handled for you !

Finally, the end of the program :

/* Pause le temps que l'utilisateur appuie sur une touche */
cvWaitKey (0);

/* Destruction de la fenêtre */
cvDestroyAllWindows ();
  
/* Libération de la mémoire */
cvReleaseImage (&img);

The function cvWaitKeywhen a window is displayed on the screen, allows you to pause the program until the user presses a key on the keyboard. The argument 0, here, allows to wait indefinitely.

The following two functions are self-explanatory. Just note that the function cvReleaseImage expects as a parameter a IplImage** and non-a IplImage*.

Load and save images

Let’s start from the beginning.

To work with images, we need a way to load these into our programs, and possibly save the result.

Load an image

We have seen earlier in the function cvLoadImage. Here is its signature :

/**
 * Charge une image
 * arguments: 
 * - filename: chemin de l'image à charger
 * - iscolor: option permettant de charger l'image en couleur ou en 
 *            niveaux de gris
 * retourne: 
 * Un pointeur sur la structure IplImage dans laquelle le fichier a 
 * été chargé. NULL en cas d'échec.
 */
IplImage* cvLoadImage (const char* filename, int iscolor);

OpenCV can load several different image formats. I cannot give you a list of exact formats that will work for you given that it depends on your system, and compilation settings if you compiled OpenCV yourself, but in principle, you should without problem be able to open images in the format .jpg.png.bmp, and .tif.

Now let’s look at this argument iscolor a little more closely, and in particular, the various constants that we can use :

  • CV_LOAD_IMAGE_COLOR : The file will be loaded in color mode, even if the image is grayscale.

  • CV_LOAD_IMAGE_GRAYSCALE : The image will be loaded in gray scale.

  • CV_LOAD_IMAGE_UNCHANGED : The image will be loaded as it

These considerations on the color mode or grayscale will make sense in the next chapter, when we will see more in detail the structure IplImage. Just remember that this feature allows you to automatically make the conversion if necessary when loading the file.

Save an image

Now here is the function that will allow us to save an image on the hard drive :

/**
 * Sauvegarde une image sur le disque dur
 * arguments:
 * - filename: chemin du fichier à sauvegarder
 * - image: image à sauvegarder
 * - params: paramètres optionnels
 * retourne:
 * 1 si le fichier a bien été sauvegardé, 0 sinon.
 */
int cvSaveImage (const char* filename, const CvArr* image, const int* params);

CvArr* ? It is a structure that I don’t know it !o_O

Don’t panic ! By consulting the documentation of OpenCV, you will realize that the definition of CvArr is a little bit troubling :

typedef void CvArr;

In fact, OpenCV contains several structures to represent images, matrices, or tables in general, but as its API is well-made, most of the functions can take as arguments any of these structures. In this case, the ” Arr “CvArr means “array” (table).

We will see in the next chapter that the images are in fact paintings. You can so, when you read the documentation of OpenCV, you say that every time that a function takes an CvArr* argument, you can pass in a IplImage*.

And this is what, these optional parameters ?

In reality, these are settings that are used to choose, for example, the image quality for the JPEG, or the compression force for the PNG, or parameters that, in the context of this course, we are interested in absolutely not ! In our case, we will just leave this argument NULL to use the default settings that are largely sufficient.

If you are brave or extremely determined , and that the understanding of this argument is absolutely vital to you, be aware that the documentation of OpenCV is suffering from a slight wandering about. To point you in the right direction, the behavior of this function is the same as that of the function cv::imwrite the C++ API. You will find the right constants to go digging a little bit in the headers of OpenCV (opencv2/highgui/highgui_c.h at around line 220). So I encourage you to do this little research for yourself on this point of detail if the heart you in said, because it is rather a trainer, and in my opinion it is a very good habit to get into.;)

JPEG ? PNG ? But… how do we determines what format to save ?!

Further proof that highgui is malignant : the format of the saved image will be determined by the extension you give him ! For example, if you pass in the argument string "image.bmp", your image will be automatically saved in the format Windows Bitmap.

Example of use

To show you how to work this recording, here is a very simple program, which takes two command-line arguments (if you don’t want to use your console, you can always give “hard” paths of the two images on your computer), and which converts an image from one format to another :

/**
 * Converting an image from one format to another 
 * For example: JPG-> png
 */

#include <stdio.h>
#include <stdlib.h>
#include <opencv/highgui.h>

/**
 * Le programme prend deux arguments:
 * - l'image à convertir (SRC_IMAGE)
 * - l'image dans laquelle écrire le résultat (DST_IMAGE)
 */
int main (int argc, char* argv[])
{
  IplImage* img = NULL;
  int error = 0;

  /* Vérification des arguments */
  if (argc < 3)
  {
    fprintf (stderr, "usage: %s SRC_IMAGE DST_IMAGE\n", argv[0]);
    return EXIT_FAILURE;
  }

  /* Chargement du fichier image à convertir */
  if (!(img = cvLoadImage (argv[1], CV_LOAD_IMAGE_UNCHANGED)))
  {
    fprintf (stderr, "couldn't open image file: %s\n", argv[1]);
    return EXIT_FAILURE;
  }

  /* Écriture du fichier converti */
  if (!cvSaveImage (argv[2], img, NULL))
  {
    fprintf (stderr, "couldn't write image file: %s\n", argv[2]); 
    error = 1;
  }
    
  /* Libération de la mémoire */
  cvReleaseImage (&img);

  return (error ? EXIT_FAILURE : EXIT_SUCCESS);
}

Let’s try it in a console under GNU/Linux :

arnaud@netbook$ ./cvtimg ~/img/vpo/lena.jpg ~/img/vpo/lena.png
arnaud@netbook$ file ~/img/vpo/lena.jpg ~/img/vpo/lena.png
/home/arnaud/img/vpo/lena.jpg: JPEG image data, JFIF standard 1.01
/home/arnaud/img/vpo/lena.png: PNG image data, 512 x 512, 8-bit/color RGB, non-interlaced

Obviously, depending on the command file, it worked perfectly, the image has been converted from JPEG format to PNG format.

But who is this “Lena” of which you have the photo on your computer ?:p

Patience, a little curious ! Lena is an image used conventionally to test the image processing algorithms in the research community. I will show you, and you conterai the curious history of the ” mona lisa of the computer vision, in the next chapter. You will not be disappointed !;)

In the meantime, we remain focused on, and we pass to the functions that we have yet to see it, go !

A simple interface, but practical

What is the point in it to work on images if one could not display them ?

The few functions that follow allow you to create windows in which to display the results of our treatments. As you have realized in the program ” Hello, world! “these are very simple to use, and it’s just as well, because that is what they demand !

Create windows and display images

To create a window, we simply call the following function :

/**
* Creates a window 
* arguments: 
*-Name: Window Title 
*-Flags: Optional parameters
*/
int cvNamedWindow(const char* name, int flags);

As we have already seen above, the windows in OpenCV are identified by their title. For the argument flags, be aware that the only one which is supported by the C API is CV_WINDOW_AUTOSIZE. This means that the window will be automatically resized to fit the size of the image displayed.

Speaking of display pictures, here is the only function we will need to do this :

/**
 * Displays an image.
 * arguments:
 * - name: The title of the window in which to display the image
 * - image: Image to display
 */
void cvShowImage(const char* name, const CvArr* image);

No difficulty here. As for the function cvSaveImage, we can pass pointers to several different types of structures different in the second argument of this function, which IplImage.

Here are finally the last two functions you need to know about the management of windows, namely those that allow to destroy them :

/**
 * Destroying a window.
 * arguments:
 * - name: Title of the window to be destroyed
 */
void cvDestroyWindow(const char* name);

/**
 * Destroy all HighGUI windows displayed on the screen.
 */
void cvDestroyAllWindows(void);

I think that there is nothing to add if not is this small note, for the sake of completeness :

Although this far exceeds the framework of this course, it is a thing that it is good to know, since this new Qt interface includes some features very interesting (the ability to zoom, etc). In our case, we shall restrict ourselves to the basic interface, which is largely sufficient to meet all our needs. The documentation of OpenCV will tell you more about it.

Management of keyboard events and timer

Here’s a function I like : cvWaitKey !

/**
* Pauses the program for a while, 
* waiting for the user to press a key. 
* Arguments: 
*-delay: Maximum pause time (in milliseconds) 
* Returns: 
* The code (ASCII when applicable) of the pressed key 
* or-1 if nothing happened.
*/
int cvWaitKey(int delay);

This small function passe-partout will allow us to manage many things at once in our graphical interfaces, as we will discover as of the course. I would like to remind you (a second time), however, it does that from the moment a window is displayed on the screen.;)

This concludes our first overview of OpenCV through the HighGUI.

I make you a flower : there will be no exercise or QUIZ for this chapter. The goal is not that you know these functions by heart from the start, but just you introduce. We will have ample opportunity to use them in the remainder of this course so that in the long run, their use becomes second breath for you !

See you in the next chapter, in which we are going to begin to play with images.:p