[ VIGRA Homepage | Function Index | Class Index | Namespaces | File List | Main Page ]

vigra/multi_morphology.hxx

00001 /************************************************************************/
00002 /*                                                                      */
00003 /*     Copyright 2003-2007 by Kasim Terzic, Christian-Dennis Rahn       */
00004 /*                        and Ullrich Koethe                            */
00005 /*       Cognitive Systems Group, University of Hamburg, Germany        */
00006 /*                                                                      */
00007 /*    This file is part of the VIGRA computer vision library.           */
00008 /*    ( Version 1.6.0, Aug 13 2008 )                                    */
00009 /*    The VIGRA Website is                                              */
00010 /*        http://kogs-www.informatik.uni-hamburg.de/~koethe/vigra/      */
00011 /*    Please direct questions, bug reports, and contributions to        */
00012 /*        ullrich.koethe@iwr.uni-heidelberg.de    or                    */
00013 /*        vigra@informatik.uni-hamburg.de                               */
00014 /*                                                                      */
00015 /*    Permission is hereby granted, free of charge, to any person       */
00016 /*    obtaining a copy of this software and associated documentation    */
00017 /*    files (the "Software"), to deal in the Software without           */
00018 /*    restriction, including without limitation the rights to use,      */
00019 /*    copy, modify, merge, publish, distribute, sublicense, and/or      */
00020 /*    sell copies of the Software, and to permit persons to whom the    */
00021 /*    Software is furnished to do so, subject to the following          */
00022 /*    conditions:                                                       */
00023 /*                                                                      */
00024 /*    The above copyright notice and this permission notice shall be    */
00025 /*    included in all copies or substantial portions of the             */
00026 /*    Software.                                                         */
00027 /*                                                                      */
00028 /*    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND    */
00029 /*    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES   */
00030 /*    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND          */
00031 /*    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT       */
00032 /*    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,      */
00033 /*    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING      */
00034 /*    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR     */
00035 /*    OTHER DEALINGS IN THE SOFTWARE.                                   */
00036 /*                                                                      */
00037 /************************************************************************/
00038 
00039 #ifndef VIGRA_MULTI_MORPHOLOGY_HXX
00040 #define VIGRA_MULTI_MORPHOLOGY_HXX
00041 
00042 #include <vector>
00043 #include <cmath>
00044 #include "multi_distance.hxx"
00045 #include "array_vector.hxx"
00046 #include "multi_array.hxx"
00047 #include "accessor.hxx"
00048 #include "numerictraits.hxx"
00049 #include "navigator.hxx"
00050 #include "metaprogramming.hxx"
00051 #include "multi_pointoperators.hxx"
00052 #include "functorexpression.hxx"
00053 
00054 namespace vigra
00055 {
00056 
00057 /** \addtogroup MultiArrayMorphology Morphological operators for multi-dimensional arrays.
00058 
00059     These functions perform morphological operations on an arbitrary
00060     dimensional array that is specified by iterators (compatible to \ref MultiIteratorPage)
00061     and shape objects. It can therefore be applied to a wide range of data structures
00062     (\ref vigra::MultiArrayView, \ref vigra::MultiArray etc.).
00063 */
00064 //@{
00065 
00066 /********************************************************/
00067 /*                                                      */
00068 /*             multiBinaryErosion                       */
00069 /*                                                      */
00070 /********************************************************/
00071 /** \brief Binary erosion on multi-dimensional arrays.
00072 
00073     This function applies a flat circular erosion operator with a given radius. The
00074     operation is isotropic.
00075     The input is a binary multi-dimensional array where non-zero pixels represent 
00076     foreground and zero pixels represent background.
00077     
00078     This function may work in-place, which means that <tt>siter == diter</tt> is allowed.
00079     A full-sized internal array is only allocated if working on the destination
00080     array directly would cause overflow errors (i.e. if
00081     <tt> typeid(typename DestAccessor::value_type) < N * M*M</tt>, where M is the
00082     size of the largest dimension of the array.
00083            
00084     <b> Declarations:</b>
00085 
00086     pass arguments explicitly:
00087     \code
00088     namespace vigra {
00089         template <class SrcIterator, class SrcShape, class SrcAccessor,
00090                   class DestIterator, class DestAccessor>
00091         void
00092         multiBinaryErosion(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
00093                                     DestIterator diter, DestAccessor dest, int radius);
00094 
00095     }
00096     \endcode
00097 
00098     use argument objects in conjunction with \ref ArgumentObjectFactories :
00099     \code
00100     namespace vigra {
00101         template <class SrcIterator, class SrcShape, class SrcAccessor,
00102                   class DestIterator, class DestAccessor>
00103         void
00104         multiBinaryErosion(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00105                                     pair<DestIterator, DestAccessor> const & dest, 
00106                                     int radius);
00107 
00108     }
00109     \endcode
00110 
00111     <b> Usage:</b>
00112 
00113     <b>\#include</b> <<a href="multi__morphology_8hxx-source.html">vigra/multi_morphology.hxx</a>>
00114 
00115     \code
00116     MultiArray<3, unsigned char>::size_type shape(width, height, depth);
00117     MultiArray<3, unsigned char> source(shape);
00118     MultiArray<3, unsigned char> dest(shape);
00119     ...
00120 
00121     // perform isotropic binary erosion
00122     multiBinaryErosion(srcMultiArrayRange(source), destMultiArray(dest), 3);
00123     \endcode
00124 
00125     \see vigra::discErosion()
00126 */
00127 doxygen_overloaded_function(template <...> void multiBinaryErosion)
00128 
00129 template <class SrcIterator, class SrcShape, class SrcAccessor,
00130           class DestIterator, class DestAccessor>
00131 void
00132 multiBinaryErosion( SrcIterator s, SrcShape const & shape, SrcAccessor src,
00133                              DestIterator d, DestAccessor dest, float radius)
00134 {
00135     typedef typename NumericTraits<typename DestAccessor::value_type>::ValueType DestType;
00136     typedef typename NumericTraits<typename DestAccessor::value_type>::Promote TmpType;
00137     DestType MaxValue = NumericTraits<DestType>::max();
00138     float radius2 = (float) radius * radius;
00139     enum { N = 1 + SrcIterator::level };
00140     
00141     int MaxDim = 0; 
00142     for( int i=0; i<N; i++)
00143         if(MaxDim < shape[i]) MaxDim = shape[i];
00144    
00145     using namespace vigra::functor;
00146 
00147     // Get the distance squared transform of the image
00148     if(N*MaxDim*MaxDim > MaxValue)
00149     {
00150         // Allocate a new temporary array if the distances squared wouldn't fit
00151         MultiArray<SrcShape::static_size, TmpType> tmpArray(shape);
00152         //detail::internalSeparableMultiArrayDistTmp( s, shape, src, tmpArray.traverser_begin(),
00153         //    typename AccessorTraits<TmpType>::default_accessor()/*, false*/ );
00154         
00155         separableMultiDistSquared(s, shape, src, tmpArray.traverser_begin(),
00156                 typename AccessorTraits<TmpType>::default_accessor(), false );
00157         
00158         // threshold everything less than radius away from the edge
00159         // std::cerr << "Thresholding!!!!!" << std::endl;
00160         transformMultiArray( tmpArray.traverser_begin(), shape, 
00161             typename AccessorTraits<TmpType>::default_accessor(), d, dest, 
00162             ifThenElse( Arg1() > Param(radius2),
00163                 Param(MaxValue), Param(0) ) );
00164     }
00165     else    // work directly on the destination array
00166     {
00167         //detail::internalSeparableMultiArrayDistTmp( s, shape, src, d, dest/*, false*/ );
00168         separableMultiDistSquared( s, shape, src, d, dest, false );
00169         
00170         // threshold everything less than radius away from the edge
00171         transformMultiArray( d, shape, dest, d, dest, 
00172             ifThenElse( Arg1() > Param(radius2),
00173                 Param(MaxValue), Param(0) ) );
00174     }
00175 }
00176 
00177 template <class SrcIterator, class SrcShape, class SrcAccessor,
00178           class DestIterator, class DestAccessor>
00179 inline
00180 void multiBinaryErosion(
00181     triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00182     pair<DestIterator, DestAccessor> const & dest, int radius)
00183 {
00184     multiBinaryErosion( source.first, source.second, source.third,
00185                                  dest.first, dest.second, radius );
00186 }
00187 
00188 
00189 /********************************************************/
00190 /*                                                      */
00191 /*             multiBinaryDilation                      */
00192 /*                                                      */
00193 /********************************************************/
00194 
00195 /** \brief Binary dilation on multi-dimensional arrays.
00196 
00197     This function applies a flat circular dilation operator with a given radius. The
00198     operation is isotropic.
00199     The input is a binary multi-dimensional array where non-zero pixels represent
00200     foreground and zero pixels represent background.
00201     
00202     This function may work in-place, which means that <tt>siter == diter</tt> is allowed.
00203     A full-sized internal array is only allocated if working on the destination
00204     array directly would cause overflow errors (i.e. if
00205     <tt> typeid(typename DestAccessor::value_type) < N * M*M</tt>, where M is the
00206     size of the largest dimension of the array.
00207            
00208     <b> Declarations:</b>
00209 
00210     pass arguments explicitly:
00211     \code
00212     namespace vigra {
00213         template <class SrcIterator, class SrcShape, class SrcAccessor,
00214                   class DestIterator, class DestAccessor>
00215         void
00216         multiBinaryDilation(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
00217                                     DestIterator diter, DestAccessor dest, int radius);
00218 
00219     }
00220     \endcode
00221 
00222     use argument objects in conjunction with \ref ArgumentObjectFactories :
00223     \code
00224     namespace vigra {
00225         template <class SrcIterator, class SrcShape, class SrcAccessor,
00226                   class DestIterator, class DestAccessor>
00227         void
00228         multiBinaryDilation(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00229                                     pair<DestIterator, DestAccessor> const & dest, 
00230                                     int radius);
00231 
00232     }
00233     \endcode
00234 
00235     <b> Usage:</b>
00236 
00237     <b>\#include</b> <<a href="multi__morphology_8hxx-source.html">vigra/multi_morphology.hxx</a>>
00238 
00239     \code
00240     MultiArray<3, unsigned char>::size_type shape(width, height, depth);
00241     MultiArray<3, unsigned char> source(shape);
00242     MultiArray<3, unsigned char> dest(shape);
00243     ...
00244 
00245     // perform isotropic binary erosion
00246     multiBinaryDilation(srcMultiArrayRange(source), destMultiArray(dest), 3);
00247     \endcode
00248 
00249     \see vigra::discDilation()
00250 */
00251 doxygen_overloaded_function(template <...> void multiBinaryDilation)
00252 
00253 template <class SrcIterator, class SrcShape, class SrcAccessor,
00254           class DestIterator, class DestAccessor>
00255 void
00256 multiBinaryDilation( SrcIterator s, SrcShape const & shape, SrcAccessor src,
00257                              DestIterator d, DestAccessor dest, float radius)
00258 {
00259     typedef typename NumericTraits<typename DestAccessor::value_type>::ValueType DestType;
00260     typedef typename NumericTraits<typename DestAccessor::value_type>::Promote TmpType;
00261     DestType MaxValue = NumericTraits<DestType>::max();
00262     float radius2 = (float) radius * radius;
00263     enum { N = 1 + SrcIterator::level };
00264     
00265     int MaxDim = 0; 
00266     for( int i=0; i<N; i++)
00267         if(MaxDim < shape[i]) MaxDim = shape[i];
00268    
00269     using namespace vigra::functor;
00270 
00271     // Get the distance squared transform of the image
00272     if(N*MaxDim*MaxDim > MaxValue)
00273     {
00274         // Allocate a new temporary array if the distances squared wouldn't fit
00275         MultiArray<SrcShape::static_size, TmpType> tmpArray(shape);
00276         //detail::internalSeparableMultiArrayDistTmp( s, shape, src, tmpArray.traverser_begin(),
00277         //    typename AccessorTraits<TmpType>::default_accessor(), true );
00278         
00279         separableMultiDistSquared(s, shape, src, tmpArray.traverser_begin(),
00280             typename AccessorTraits<TmpType>::default_accessor(), true );
00281       
00282         // threshold everything less than radius away from the edge
00283         transformMultiArray( tmpArray.traverser_begin(), shape, 
00284             typename AccessorTraits<TmpType>::default_accessor(), d, dest, 
00285             ifThenElse( Arg1() > Param(radius2),
00286                 Param(0), Param(MaxValue) ) );
00287     }
00288     else    // work directly on the destination array
00289     {
00290         //detail::internalSeparableMultiArrayDistTmp( s, shape, src, d, dest, true );
00291         separableMultiDistSquared( s, shape, src, d, dest, true );
00292         
00293         // threshold everything less than radius away from the edge
00294         transformMultiArray( d, shape, dest, d, dest, 
00295             ifThenElse( Arg1() > Param(radius2),
00296                 Param(0), Param(MaxValue) ) );
00297     }
00298 }
00299 
00300 template <class SrcIterator, class SrcShape, class SrcAccessor,
00301           class DestIterator, class DestAccessor>
00302 inline
00303 void multiBinaryDilation(
00304     triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00305     pair<DestIterator, DestAccessor> const & dest, int radius)
00306 {
00307     multiBinaryDilation( source.first, source.second, source.third,
00308                                  dest.first, dest.second, radius );
00309 }
00310 
00311 /********************************************************/
00312 /*                                                      */
00313 /*             multiGrayscaleErosion                    */
00314 /*                                                      */
00315 /********************************************************/
00316 /** \brief Parabolic grayscale erosion on multi-dimensional arrays.
00317 
00318     This function applies a parabolic erosion operator with a given spread (sigma) on
00319     a grayscale array. The operation is isotropic.
00320     The input is a grayscale multi-dimensional array.
00321     
00322     This function may work in-place, which means that <tt>siter == diter</tt> is allowed.
00323     A full-sized internal array is only allocated if working on the destination
00324     array directly would cause overflow errors (i.e. if
00325     <tt> typeid(typename DestAccessor::value_type) < N * M*M</tt>, where M is the
00326     size of the largest dimension of the array.
00327            
00328     <b> Declarations:</b>
00329 
00330     pass arguments explicitly:
00331     \code
00332     namespace vigra {
00333         template <class SrcIterator, class SrcShape, class SrcAccessor,
00334                   class DestIterator, class DestAccessor>
00335         void
00336         multiGrayscaleErosion(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
00337                                     DestIterator diter, DestAccessor dest, float sigma);
00338 
00339     }
00340     \endcode
00341 
00342     use argument objects in conjunction with \ref ArgumentObjectFactories :
00343     \code
00344     namespace vigra {
00345         template <class SrcIterator, class SrcShape, class SrcAccessor,
00346                   class DestIterator, class DestAccessor>
00347         void
00348         multiGrayscaleErosion(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00349                                     pair<DestIterator, DestAccessor> const & dest, 
00350                                     float sigma);
00351 
00352     }
00353     \endcode
00354 
00355     <b> Usage:</b>
00356 
00357     <b>\#include</b> <<a href="multi__morphology_8hxx-source.html">vigra/multi_morphology.hxx</a>>
00358 
00359     \code
00360     MultiArray<3, unsigned char>::size_type shape(width, height, depth);
00361     MultiArray<3, unsigned char> source(shape);
00362     MultiArray<3, unsigned char> dest(shape);
00363     ...
00364 
00365     // perform isotropic grayscale erosion
00366     multiGrayscaleErosion(srcMultiArrayRange(source), destMultiArray(dest), 3.0);
00367     \endcode
00368 
00369     \see vigra::discErosion()
00370 */
00371 doxygen_overloaded_function(template <...> void multiGrayscaleErosion)
00372 
00373 template <class SrcIterator, class SrcShape, class SrcAccessor,
00374           class DestIterator, class DestAccessor>
00375 void
00376 multiGrayscaleErosion( SrcIterator s, SrcShape const & shape, SrcAccessor src,
00377                        DestIterator d, DestAccessor dest, float sigma)
00378 {
00379     typedef typename NumericTraits<typename DestAccessor::value_type>::ValueType DestType;
00380     typedef typename NumericTraits<typename DestAccessor::value_type>::Promote TmpType;
00381     DestType MaxValue = NumericTraits<DestType>::max();
00382     enum { N = 1 + SrcIterator::level };
00383     
00384     // temporay array to hold the current line to enable in-place operation
00385     ArrayVector<TmpType> tmp( shape[0] );
00386         
00387     typedef MultiArrayNavigator<SrcIterator, N> SNavigator;
00388     typedef MultiArrayNavigator<DestIterator, N> DNavigator;
00389     
00390     int MaxDim = 0; 
00391     for( int i=0; i<N; i++)
00392         if(MaxDim < shape[i]) MaxDim = shape[i];
00393     
00394     using namespace vigra::functor;
00395     
00396     // Allocate a new temporary array if the distances squared wouldn't fit
00397     if(N*MaxDim*MaxDim > MaxValue)
00398     {
00399         MultiArray<SrcShape::static_size, TmpType> tmpArray(shape);
00400 
00401         detail::internalSeparableMultiArrayDistTmp( s, shape, src, tmpArray.traverser_begin(),
00402             typename AccessorTraits<TmpType>::default_accessor(), sigma );
00403         
00404         transformMultiArray( tmpArray.traverser_begin(), shape,
00405                 typename AccessorTraits<TmpType>::default_accessor(), d, dest,
00406                 ifThenElse( Arg1() > Param(MaxValue), Param(MaxValue), Arg1() ) );
00407         //copyMultiArray( tmpArray.traverser_begin(), shape,
00408         //        typename AccessorTraits<TmpType>::default_accessor(), d, dest );
00409     }
00410     else
00411     {
00412         detail::internalSeparableMultiArrayDistTmp( s, shape, src, d, dest, sigma );
00413     }
00414 
00415 }
00416 
00417 template <class SrcIterator, class SrcShape, class SrcAccessor,
00418           class DestIterator, class DestAccessor>
00419 inline 
00420 void multiGrayscaleErosion(
00421     triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00422     pair<DestIterator, DestAccessor> const & dest, float sigma)
00423 {
00424     multiGrayscaleErosion( source.first, source.second, source.third, 
00425             dest.first, dest.second, sigma);
00426 }
00427 
00428 /********************************************************/
00429 /*                                                      */
00430 /*             multiGrayscaleDilation                   */
00431 /*                                                      */
00432 /********************************************************/
00433 /** \brief Parabolic grayscale dilation on multi-dimensional arrays.
00434 
00435     This function applies a parabolic dilation operator with a given spread (sigma) on
00436     a grayscale array. The operation is isotropic.
00437     The input is a grayscale multi-dimensional array.
00438     
00439     This function may work in-place, which means that <tt>siter == diter</tt> is allowed.
00440     A full-sized internal array is only allocated if working on the destination
00441     array directly would cause overflow errors (i.e. if
00442     <tt> typeid(typename DestAccessor::value_type) < N * M*M</tt>, where M is the
00443     size of the largest dimension of the array.
00444            
00445     <b> Declarations:</b>
00446 
00447     pass arguments explicitly:
00448     \code
00449     namespace vigra {
00450         template <class SrcIterator, class SrcShape, class SrcAccessor,
00451                   class DestIterator, class DestAccessor>
00452         void
00453         multiGrayscaleDilation(SrcIterator siter, SrcShape const & shape, SrcAccessor src,
00454                                     DestIterator diter, DestAccessor dest, float sigma);
00455 
00456     }
00457     \endcode
00458 
00459     use argument objects in conjunction with \ref ArgumentObjectFactories :
00460     \code
00461     namespace vigra {
00462         template <class SrcIterator, class SrcShape, class SrcAccessor,
00463                   class DestIterator, class DestAccessor>
00464         void
00465         multiGrayscaleDilation(triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00466                                     pair<DestIterator, DestAccessor> const & dest, 
00467                                     float sigma);
00468 
00469     }
00470     \endcode
00471 
00472     <b> Usage:</b>
00473 
00474     <b>\#include</b> <<a href="multi__morphology_8hxx-source.html">vigra/multi_morphology.hxx</a>>
00475 
00476     \code
00477     MultiArray<3, unsigned char>::size_type shape(width, height, depth);
00478     MultiArray<3, unsigned char> source(shape);
00479     MultiArray<3, unsigned char> dest(shape);
00480     ...
00481 
00482     // perform isotropic grayscale erosion
00483     multiGrayscaleDilation(srcMultiArrayRange(source), destMultiArray(dest), 3.0);
00484     \endcode
00485 
00486     \see vigra::discErosion()
00487 */
00488 doxygen_overloaded_function(template <...> void multiGrayscaleDilation)
00489 
00490 template <class SrcIterator, class SrcShape, class SrcAccessor,
00491           class DestIterator, class DestAccessor>
00492 void multiGrayscaleDilation( SrcIterator s, SrcShape const & shape, SrcAccessor src,
00493                              DestIterator d, DestAccessor dest, float sigma)
00494 {
00495     typedef typename NumericTraits<typename DestAccessor::value_type>::ValueType DestType;
00496     typedef typename NumericTraits<typename DestAccessor::value_type>::Promote TmpType;
00497     DestType MinValue = NumericTraits<DestType>::min();
00498     DestType MaxValue = NumericTraits<DestType>::max();
00499     enum { N = 1 + SrcIterator::level };
00500         
00501     // temporay array to hold the current line to enable in-place operation
00502     ArrayVector<TmpType> tmp( shape[0] );
00503         
00504     typedef MultiArrayNavigator<SrcIterator, N> SNavigator;
00505     typedef MultiArrayNavigator<DestIterator, N> DNavigator;
00506     
00507     int MaxDim = 0; 
00508     for( int i=0; i<N; i++)
00509         if(MaxDim < shape[i]) MaxDim = shape[i];
00510     
00511     using namespace vigra::functor;
00512 
00513     // Allocate a new temporary array if the distances squared wouldn't fit
00514     if(-N*MaxDim*MaxDim < MinValue || N*MaxDim*MaxDim > MaxValue)
00515     {
00516         MultiArray<SrcShape::static_size, TmpType> tmpArray(shape);
00517 
00518         detail::internalSeparableMultiArrayDistTmp( s, shape, src, tmpArray.traverser_begin(),
00519             typename AccessorTraits<TmpType>::default_accessor(), sigma, true );
00520         
00521         transformMultiArray( tmpArray.traverser_begin(), shape,
00522                 typename AccessorTraits<TmpType>::default_accessor(), d, dest,
00523                 ifThenElse( Arg1() > Param(MaxValue), Param(MaxValue), 
00524                     ifThenElse( Arg1() < Param(MinValue), Param(MinValue), Arg1() ) ) );
00525     }
00526     else
00527     {
00528         detail::internalSeparableMultiArrayDistTmp( s, shape, src, d, dest, sigma, true );
00529     }
00530 
00531 }
00532 
00533 
00534 template <class SrcIterator, class SrcShape, class SrcAccessor,
00535           class DestIterator, class DestAccessor>
00536 inline 
00537 void multiGrayscaleDilation(
00538     triple<SrcIterator, SrcShape, SrcAccessor> const & source,
00539     pair<DestIterator, DestAccessor> const & dest, float sigma)
00540 {
00541     multiGrayscaleDilation( source.first, source.second, source.third, 
00542             dest.first, dest.second, sigma);
00543 }
00544 
00545 
00546 //@}
00547 
00548 } //-- namespace vigra
00549 
00550 
00551 #endif        //-- VIGRA_MULTI_MORPHOLOGY_HXX

© Ullrich Köthe (ullrich.koethe@iwr.uni-heidelberg.de)
Heidelberg Collaboratory for Image Processing, University of Heidelberg, Germany

html generated using doxygen and Python
VIGRA 1.6.0 (13 Aug 2008)