next up previous contents index
Next: Module's header Up: Getting started Previous: Some optimizations   Contents   Index


A little more complex extension

We give in this section a little more complex extension to our algorithm, in order to show the easy way in which two modules can communicate. This will also gives examples of the use of options in the usage.

You have seen in section 3.1 that the addition of two floating point images may be source of errors when the resulting image has to be converted to the unsigned char representation (which may be necessary, for example, when the image has to be displayed). You may want to control the conversion into the addition module. Let us suppose that you want to put an optional thresholding of the output image, which may be preceded by a normalization of the gray levels into a given interval [min,max].

You have to add in the new module inputs for the boundaries of the interval, and input for a flag to say whether or not you want the normalization. You have therefore to familiarize yourself with the options.

There is a general rule about the input/output optional parameters of the module function: they have to be pointers. If the pointer is NULL it means that the parameter has not been selected, if the address is not equal to NULL it points to an allocated space which may be a scalar or a C structure (in case of MegaWave2 memory type). Because all MegaWave2 memory types (e.g. Cimage, Fimage, ...) are pointers to a structure, there is no difference in the type between an optional parameter and a needed parameter. On the contrary, if the input/output optional parameter is a scalar, say a float, it has to be defined has a pointer to a float (float *).

A flag is nothing more than a pointer to any scalar, e.g. a char *. In the following version of our module, called fadd, the flag norm indicates if the normalization has to be computed (see the listing page [*]) :

'n'->norm "Normalize pixel values into [min,max]"+
The letter 'n' means that, in the command mode, the flag is set when the module is called with the user option -n.

The other options are about the boundaries, let have a look to the first one:

'm':min->m0 "Force minimal pixel value",
There is here an additional term, min, which says that the option needs a value when it is selected. This value is put into the C variable addressed by m0. The word min is the one which will appear in the help and in the documentation, instead of the name of the C variable (you can choice the same name). The string following this declaration explains the meaning of the option. Everything is similar for the second boundary option:
'M':max->m1  "Force maximal pixel value",

You have of course to update the declaration of the module function, in order to add the options:

void fadd(A,B,C,norm,m0,m1)

Fimage  A,B,C;
char *norm;
float *m0,*m1;

When the -n option is not selected, it should be possible to give only one boundary of the interval [min,max]. Indeed, the threshold may be done only in one side. But when the user ask for the normalization, both -m and -M options must be selected. So you need to check that somewhere. It is not possible to put this information in the header (we could do that but the header's grammar would be horrible), but you can add the verification in the beginning of the function:

  if (norm && (!m0 || !m1)) mwerror(USAGE,0,
            "Normalization needs selection of [min,max] values\n");
We call the function mwerror with the argument USAGE to tell MegaWave2 that this error is about the usage.

The normalization and thresholding process is called by the last line:

  if (m0 || m1) fthre(C,C,norm,m0,m1);
Where is the function fthre defined ? Since it is not a function of the system library (it doesn't begin with mw), and because this function is not defined in the module, it should be the main function of another module. We could have put the computations into fadd, but by creating another module we offer the possibility to directly use the command fthre in other contexts. In addition, other modules may want to make normalization or thresholding so the code will not be duplicated. Such a (well-known) ``philosophy of overlapped black boxes'' saves space and time, it is one of the important aspect of MegaWave2: if your mathematical algorithm may be decomposed into several independant algorithms, write one module per algorithm.

Now we rapidly study the module fthre. You should refer to the listing page [*]. The header of fthre is copied from the one of fadd, but there is only one input image instead of two. Please notice that the output image (the C variable B) is a priori not the same as the input image A. This allows to call fthre from a module which needs to keep the original image. But you can force fthre to use the same variable for the input and the output, by calling it with twice the same variable: that is what we do in fadd. In this way, you save memory since the instruction

  if ((B = mw_change_fimage(B,A->nrow,A->ncol)) == NULL)
    mwerror(FATAL, 1, "Not enough memory !\n");
won't allocate any memory (A = B and therefore B has the gray plane already allocated for the requested size).

Here is the listing of the addition module fadd. It is a public module put into the group common/float_image:

/*--------------------------- MegaWave2 module  -----------------------------*/
/* mwcommand
 name = {fadd};
 author = {"Jacques Froment"};
 version = {"1.0"};
 function = {"Adds the pixel's gray-levels of two fimages"};
 usage = {
 'n'->norm "Normalize pixel values into [min,max]",
 'm':min->m0 "Force minimal pixel value",
 'M':max->m1  "Force maximal pixel value",
 fimage1->A 
   "Input fimage #1", 
 fimage2->B
   "Input fimage #2", 
 result<-C
      "Output image"
};
*/
/*--------------------------------------------------------------------------*/

#include <stdio.h>
#include  "mw.h"

void fadd(A,B,C,norm,m0,m1)

Fimage  A,B,C;
char *norm;
float *m0,*m1;

{
  register float *ptr1,*ptr2,*ptr3;
  register int i;

  if (norm && (!m0 || !m1)) mwerror(USAGE,0,
            "Normalization needs selection of [min,max] values\n");

  if((A->nrow != B->nrow) || (A->ncol != B->ncol))
    mwerror(FATAL, 1, "The input images have not the same size!\n");

  if ((C = mw_change_fimage(C,A->nrow,A->ncol)) == NULL)
    mwerror(FATAL, 1, "Not enough memory !\n");  

  for (ptr1=A->gray, ptr2=B->gray, ptr3=C->gray, i=0;
       i < A->nrow*A->ncol; ptr1++, ptr2++, ptr3++, i++)
    *ptr3 = *ptr1 + *ptr2;

  if (m0 || m1) fthre(C,C,norm,m0,m1);
}

Here is the listing of the threshold module fthre. It is a public module put into the group common/float_image:

/*--------------------------- MegaWave2 module -----------------------------*/
/* mwcommand
 name = {fthre};
 author = {"Jacques Froment"};
 version = {"1.0"};
 function = {"Threshold the pixel's gray-levels of a fimage"};
 usage = {
 'n'->norm    "Normalize pixel values into [min,max]",
 'm':min->m0  "Force minimal pixel value",
 'M':max->m1  "Force maximal pixel value",
 fimage->A    "Input fimage", 
 result<-B    "Output image"
};
*/
/*--------------------------------------------------------------------------*/

#include <stdio.h>
#include <math.h>
#include  "mw.h"

void fthre(A,B,norm,m0,m1)

Fimage  A,B;
char *norm;
float *m0,*m1;

{
  register float *ptr;
  register int i;
  float min,max,a,b;
  
  if (!m0 && !m1) mwerror(USAGE,0,"At least min or max pixel value requested\n");
  if (norm && (!m0 || !m1)) mwerror(USAGE,0,"Normalization needs selection of [min,max] values\n");
  if (m0 && m1 && (*m1 <= *m0)) mwerror(USAGE,0,"Illegal values of [min,max]\n");

  if ((B = mw_change_fimage(B,A->nrow,A->ncol)) == NULL)
    mwerror(FATAL, 1, "Not enough memory !\n");  
  
  mw_copy_fimage(A,B);  /* Copy pixel values of A into B */

  if (norm)  /* Normalization */
    {
      min=1e20; max=-min;
      for (ptr=B->gray, i=0;  i < B->nrow*B->ncol; ptr++, i++) 
        {
          if (*ptr < min) min=*ptr;
          if (*ptr > max) max=*ptr;
        }
      if (fabs((double) max-min) <= 1e-20)
        mwerror(FATAL,1,"Cannot normalize: constant input image\n");
      a = (*m1-*m0)/(max-min);
      b = *m0 - a * min;
      for (ptr=B->gray, i=0;  i < B->nrow*B->ncol; ptr++, i++) 
        *ptr = a * *ptr + b;
    }

  /* Thresholding */
  if (m0) for (ptr=B->gray, i=0;  i < B->nrow*B->ncol; ptr++, i++)
    if (*ptr < *m0) *ptr=*m0;
  if (m1) for (ptr=B->gray, i=0;  i < B->nrow*B->ncol; ptr++, i++)
    if (*ptr > *m1) *ptr=*m1;
}

In order to make a run-time executable for the module fadd, you have to take the following precaution: when you want to compile a module X (here fadd) which call the function of another module Y (here fthre), be sure to compile Y before X. If you don't, you may get this kind of message during the compilation of X:

  Phase 3 : production of MegaWave command
          linking MegaWave2 command "X" on hp
/bin/ld: Unsatisfied symbols:
   Y (code)
          Error : exit.
There is an eviler possibility: if you change the content of the module Y, think to recompile X after Y (no message will be displayed if you forget that). Therefore, a good habit is to recompile all your modules from time to time (use the macro cmw2_all for that). You may also use the Unix make utility.


next up previous contents index
Next: Module's header Up: Getting started Previous: Some optimizations   Contents   Index
mw 2004-05-05