Preprocessing of operators

In order to increase the genericity of the operator functions, Pandore provides its own preprocessor. The goal is to write only one operator function and then to generate several C++ functions, one for each required type composition. The preprocessor will be used when the use of the hierarchy is insufficient (see Hierarchy).

The preprocessor is written in Perl. It converts generic files (files suffixed by .cct or .ht) files into C++ file. Pratically, it is called directly by the Makefile. To convert a .cct file into .cpp file or a file .ht into file .h the Makefile uses the commands:

perl -Ietc/macros etc/macros/template.pl etc/macros/pand_macros file.cct > file.cpp
perl -Ietc/macros etc/macros/template.pl etc/macros/pand_macros file.ht > file.h

where etc/macros directory is a subdirectory of the Pandore directory.

The preprocessor recognizes lines beginning by `##'. Lines beginning with `##;' are considered as preprocessor comments and are discarded from the generated C++ file.

Generic Program Template File

A .cct file respects more or less the following template file:

/* -*- mode: c++; c-basic-offset: 3 -*-
 *
 * Copyright (c) 2013, GREYC.
 * All rights reserved
 *
 * You may use this file under the terms of the BSD license as follows:
 *
 * "Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of the GREYC, nor the name of its
 *     contributors may be used to endorse or promote products
 *     derived from this software without specific prior written
 *     permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 *
 * For more information, refer to:
 * https://clouard.users.greyc.fr/Pandore/
 */

#include <pandore.h>
using namespace pandore;

##begin Operator(TYPE1, VOISS)

Errc Operator( const TYPE1 &ims, TYPE1 &imd, Short nbvois ) {

  return SUCCESS;
}
## append loadcases
   if (objs[0]->Type() == Po_$TYPE1) {
      TYPE1 *const ims = (TYPE1*)objs[0];
      objd[0] = new TYPE1(ims->Props());
      TYPE1 *const imd = (TYPE1*)objd[0];
      result = Operator(*ims, *imd, (Float)atof(parv[0]));
   } else
## end
##end
   
##; Generates Operator for Img1d<T>, Img2d<T> and Img3d<T>.
##forall(Operator, /Img.d/)

##; Generates Operator for Reg2d and Reg3d
##forall(Operator, /Reg[23]d/)
   
#ifdef MAIN
#define USAGE   "usage: %s connexity [-m mask] [im_in|-] [im_out|-]"
#define PARC    1
#define FINC    1
#define FOUTC   1
#define MASK    0

##main(PARC, FINC, FOUTC, MASK, USAGE)
#endif

The template file is composed of three parts:

  1. the body of the operator function enclosed between the ##begin and ##end macros.
  2. the instanciation of the function realized by the ##forall macro;
  3. the main generated by the ##main macro.

The ##begin Macro

The code enclosed between the ##begin and ##end macros is duplicated as many times as it is instantiated from Pandore objects. For example, the code can be instantiated from Img2duc, Graph3d, Reg2d, ... In order to adapt the code from the various types of Pandore objects, the begin macro has some parameters that are set during instanciation. These parameters defined things like dimension, connexity, point and available loops from the type of the Pandore object. For example, if the Pandore type, is a Img2duc then, the dimension is Dimension2d, the point is Point2d and the loop is a double for loop.

The declaration of a generic function looks like the following command -A variable is included only if it is used in the body of the function:

##begin Operator( TYPE, VOISL, POINT, DIM, LOOPP )

Parameters of the ##begin Macro

The list of available parameters is:

For example:

##begin Operator( TYPE, POINT, DIM, VOISL, LOOPP )
Errc Operator(TYPE &ims) {
   POINT p;
   Float sum=0.0F;
   DIM d;
   d=ims.Size();
   TYPE imd;
   imd.New(d);

##LOOPPB(ims,p,5) // Inner loop
   {
      for ( v=0; v<VOISL; v++ )  // VOISL can be 8 for 2D or 26 for 3D.
         sum += ims[p+v$VOISL[v])
   }

   imd[p]=sum/VOISL;
   imd.Frame(ims,1);
   return SUCCESS;
}
is instantiated as follows for an Img2duc image:
Errc Operator(Img2duc &ims) {
   Point2d p;
   Float sum=0.0F;
   Dimension2d d;
   d=ims.Size();
   Img2duc imd;
   imd.New(d);

   for (p.y=5; p.y<ims.Height()-5; p.y++)
        for (p.x=5; p.x<ims.Width()-5; p.x++)
   {
      for ( v=0; v<8; v++ )
         sum += ims[p+v8[v])
   }

   imd[p]=sum/8;
   imd.Frame(ims,1);
   return SUCCESS;
}

Note:
The character $ can be used to separate a preprocessor variable from other words. For instance, the word Po_TYPE must be rewritten as Po_$TYPE to be instantiated as Po_Imx3dsf if TYPE is Imx3dsf.

The ##append Macro

The append macro is used to add a case in the main() switch. All the variables defined in the parameter list of the begin macro and the variables defined in the main function can be used in the code between ##append and ##end macros. Most of the append contents look like the following code:
##begin Operator( TYPE,...., LOOPPB, POINT )
....
## append
   if (objs[0]->Type()==Po_$TYPE) {
      TYPE *const ims=(TYPE)objs[0];
      objd[0]=new TYPE(ims->Props());
      TYPE *const imd(TYPE)obd[0];
      result=Operator(*ims,*imd,(TYPE::ValueType)atof(parv[0]));
   }
## end
##end
The previous code will be instancied as follows if TYPE=Imx3dsf:
   if (objs[0]->Type()==Po_Imx3dsf) {
      Imx3dsf *const ims=(Imx3dsf)objs[0];
        objd[0]=new Imx3dsf(ims->Props());
      Imx3dsf*const imd(Imx3dsf)obd[0];
      result=Operator(*ims,*imd,(Imx3dsf::ValueType)atof(parv[0]));
   }
## end
##end

The ##forall Macro

The macro ##forall is used to generate the list of operator functions from the list of the chosen types. The parameters are the type of Pandore objects.
##forall(Operator,/type1/, /type2/, /type3/, ...)
The type can be specified by using regular expressions. For example:
##forall(Operator,/Imc[23]duc/,/Img2ds/)
generates:
##Operator(imc2duc, Img2dsl)
##Operator(imc2duc, Img2dsf)
##Operator(imc3duc, Img2dsl)
##Operator(imc3duc, Img2dsf)

The ##main macro

The main() function is generated from the macro ##main. The macro uses five parameters:
  1. The string that describes the usage of operator.
  2. The number of parameters;
  3. The number of input Pandore files;
  4. The number of output Pandore files;
  5. the value of the mask flag.

The easier way to parametrize this macro is to define constants as follows:

#ifdef MAIN
#define USAGE   "USAGE: %s connexity [-m mask] [im_in|-][im_out|-]"
#define PARC    1
#define FINC    1
#define FOUTC   1
#define MASK    3

##main(PARC,FINC,FOUTC,MASK,USAGE)
#endif

The contents of the macro ##append is added to the main() in the switch structure.

Example

Here is the complete source of the generic Add operator, which builds a new image (resp. a new graph) from the pixel to pixel mean of two images (resp. node to node of two graphs).
#include <pandore.h>

##; Images
##begin Add(IMG1, IMG2, VARS, LOOPP, POINT)
Errc Add(IMG1 &ims1, IMG2 &ims2, Select<IMG1,IMG2>::Signed &imd) {
   POINT p;

## LOOPP(ims1,p)
   imd[p]=(Select<IMG1,IMG2>::Signed::ValueType)((ims1[p]+ims2[p])/2);
   return SUCCESS;
}

## append loadcases
   if ( ( objs[0]->Type() == Po_$IMG1 ) &&
        ( objs[1]->Type() == Po_$IMG2 ) ) {
      IMG1* const ims1=(IMG1*)objs[0];
        IMG2* const ims2=(IMG2*)objs[1];
        objd[0]=new Select<IMG1,IMG2>::Signed(ims1->Size());
        Select<IMG1,IMG2>::Signed*const imd=(Select<IMG1,IMG2>::Signed*)objd[0];
        result=Add(*ims1,*ims2,*imd);
   } else
## end
##end

##; Graphs
##begin AddGraph(TYPE)
Errc Add(TYPE &gs1,TYPE &gs2,TYPE &gd) {
   int i;

   gd.Init(gs1);
   for (i=1;i<=gd.size;i++)
      if ((g[i]))
         gd[i]->attr=((gs1[i]->attr+gs2[i]->attr)/2.0F);

   return SUCCESS;
}
## append loadcases
   if ( ( objs[0]->Type() == Po_$TYPE ) &&
        ( objs[1]->Type() == Po_$TYPE ) ) {
        TYPE* const gs1=(TYPE*)objs[0];
        TYPE* const gs2=(TYPE*)objs[1];
        objd[0]=new TYPE(gs1->Size());
        TYPE* const gd=(TYPE*)objd[0];
        result=Add(*gs1,*gs2,*gd);
   } else
## end
##end

##forall(Add,Img2d,Img2d)
##forall(Add,Img3d,Img3d)
##forall(AddGraph,Graph)

#ifdef MAIN
##main(0,2,1,1,"USAGE: %s [-m mask] [im_in1|-] [im_in2|-] [im_out|-]")
#endif

The Pantheon project
Image Team GREYC Laboratory
UMR CNRS 6072 - ENSICAEN - University of Caen, France
This page was last modified on 19 June 2015