• Main Page
  • Related Pages
  • Modules
  • Namespaces
  • Classes
  • Files
  • File List

2007__tour.cc

00001 /* This is a short tour of the basics of Olena.  It isn't in any way
00002    meant to cover all the features of Olena.  Especially, some
00003    important objects, such as the iterators, are NOT presented here.
00004    */
00005 
00006 // We will run some example with 1D, and 2D, so we include these two
00007 // files.
00008 #include <oln/basics1d.hh>
00009 #include <oln/basics2d.hh>
00010 //#include <oln/basics3d.hh>
00011 
00012 // Let's include all types, for simplicity.
00013 #include <ntg/all.hh>
00014 
00015 // Some algorithms...
00016 #include <oln/morpho/erosion.hh>
00017 #include <oln/morpho/dilation.hh>
00018 #include <oln/morpho/opening.hh>
00019 #include <oln/morpho/closing.hh>
00020 #include <oln/morpho/watershed.hh>
00021 
00022 #include <oln/utils/stat.hh>
00023 
00024 // Basic conversion functions.
00025 #include <oln/convert/basics.hh>
00026 
00027 // Always include system headers after Olena.
00028 #include <iostream>
00029 #include <cmath>
00030 
00031 
00032 
00033 // Namespaces.
00034 // ===========
00035 // Olena is organized in a namespace hierarchy.  Every thing is
00036 // declared by Olena under the 'oln::' namespace, and possibly un a
00037 // sub namespace such as 'oln::arith::' (arithmetic operations on
00038 // images), 'oln::morpho::' (morphological operations), etc.
00039 // For the sake of simplicity, we will neglect the 'oln::' prefix
00040 // in this file.
00041 using namespace oln;
00042 
00043 // Data types are defined in the Intègre library, so they are
00044 // in the ntg namespace.
00045 // In this file, we will neglect the 'ntg::' prefix.
00046 using namespace ntg;
00047 
00048 int
00049 main (void)
00050 {
00051   // Basic value types.
00052   // ==================
00053   // Olena ships with a set of basic value types such as
00054   //   int_u8, int_u16, int_u32,   (common unsigned integer types)
00055   //   int_s8, int_s16, int_s32,   (common signed integer types)
00056   //   float_s, float_d,             (common float types)
00057   //   bin                         (binary type: false|true)
00058   //
00059   // These value are defined in the value/ subdirectory.  Actually
00060   // we defined all of them by including value/all.hh.
00061   //
00062   // You should use them instead of the standard C/C++ type, because
00063   // Olena types are equipped with additional members required to
00064   // write generic processings.
00065 
00066   // For instance the max() and min() class methods
00067   // will return the maximum value of a type.
00068   std::cout << "ntg_max_val(int_u8) = " 
00069             << ntg_max_val(int_u8) << std::endl;
00070 
00071   // You can combine these values with the standard operators
00072   // as expected.
00073   int_u8  a = 4;
00074   int_u16 b = 1000;
00075   std::cout << "a + b = " << a + b << std::endl;
00076 
00077   // Sometime it's hard to figure which type a variable has, because
00078   // Olena seems to behave surprisingly at a first glance.
00079   // There are two convenient functions that you can use to
00080   // inspect a type.
00081   // typename_of<T>()   returns a std::string describing T
00082   // typename_of_var(V) returns a std::string describing the type of V
00083   std::cout << typename_of<int_u8>()
00084             << " + "
00085             << typename_of_var(b)
00086             << " = " << typename_of_var(a + b) << std::endl;
00087   // The expression above will print
00088   // "int_u<8> + int_u<16> = int_u<17>"
00089   // This probably seems surprising for two reasons:
00090   // 1) int_u8 is printed as int_u<8>
00091   //    this is because int_u8 is really just a short hand for int_u<8>,
00092   //    Olena allows you to declare unsigned integer on 13 bits, if
00093   //    you want (int_u<13>); but int_u<8>, int_u<16>, and int_u<32> are
00094   //    more common so they have their own typedef.
00095   // 2) (a + b) has type int_u<17>, not int_u<16> as one would expect.
00096   //    The point is that one needs 17 bits to hold the result
00097   //    of an addition between a 8bit and a 16bit value without
00098   //    overflowing.
00099 
00100   // Olena will also ensure that you don't such a large value to
00101   // a variable with a smaller type:
00102   //  int_u16 c = a + b;
00103   // would fail to compile because (a + b) has type int_u<17> which
00104   // cannot fit in int_u<16>.
00105   // If you *know* that the result of (a + b) fits in c, you
00106   // should cast the value to int_u16 explicitly:
00107   int_u16 c = a + b;
00108   std::cout << "c = " << c << std::endl;
00109   // Of course assigning a+b to a variable of type int_u<17> or
00110   // greater is allowed and doesn't require a cast.
00111 
00112 
00113   // Image types.
00114   // ============
00115   // There are three kind of images supported for the moment.
00116   // image1d, image2d, and image3d: the names are self-explanatory.
00117   // All are template, parametrized by the kind of value associated
00118   // to each point (we call 'point' a position in the image).
00119   //
00120   // We will start to use the 1D images, because they are
00121   // easier to fill manually (i.e. there are less value
00122   // to assign), and easy to display.
00123 
00124   // Here are two most basic way to build an image.
00125   image1d<int_u8> img1a;        // Build an empty image.
00126   image1d<int_u8> img1b(20);    // Build an image with 20 points.
00127 
00128   // The empty image hasn't any data.  It's really useful
00129   // only as a placeholder for the result of some processing,
00130   // or another image.
00131 
00132   // Assigning an image to another one is not a costly operation.
00133   // In fact, img1a behave like some pointers.  After the following
00134   // instruction
00135   img1a = img1b;
00136   // img1a and img1b can be used equivalently to access the
00137   // same data (in other words, the point values are shared
00138   // by img1a and img1b).
00139   img1a(10) = 42;               // Assign value 42 to the 11th point of img1a
00140   std::cout << "img1b(10) ="
00141             <<  img1b(10) << std::endl; // print 42, although we use img1b
00142 
00143   // We call these assignments 'shallow copies', because they don't copy the
00144   // data.  If you want to make a deep copy of an image, then use
00145   // the clone() method, as in Java.
00146   img1a = img1b.clone ();
00147   img1a(10) = 13;
00148   std::cout << "img1b(10) ="
00149             <<  img1b(10) << std::endl; // still print 42
00150 
00151   // As show above, assignments can be made using the parenthesis
00152   // operator, which takes to coordinates of the point you want to
00153   // assign a value to in argument.  (In 1D there is only one coordinate.)
00154   // Let's fill our image with a saw-teeth signal.
00155   // (coord is the type used for coordinates)
00156   for (coord col = 0; col < img1a.ncols(); ++col)
00157     img1a(col) = col % 5;
00158 
00159   std::cout << "img1a:" << std::endl;
00160   std::cout << img1a << std::endl;
00161   // 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
00162 
00163   // You can build image2d and image3d objects similarly, except
00164   // you have to account for the extra coordinate.
00165   image2d<bin> img2a(10, 10);
00166   for (coord row = 0; row < img2a.nrows(); ++row)
00167     for (coord col = 0; col < img2a.ncols(); ++col)
00168       img2a(row, col) = (row >= 3 && col >= 3 && row < 7 && col < 7);
00169   std::cout << "img2a:" << std::endl;
00170   std::cout << img2a << std::endl;
00171   // - - - - - - - - - -
00172   // - - - - - - - - - -
00173   // - - - - - - - - - -
00174   // - - - | | | | - - -
00175   // - - - | | | | - - -
00176   // - - - | | | | - - -
00177   // - - - | | | | - - -
00178   // - - - - - - - - - -
00179   // - - - - - - - - - -
00180   // - - - - - - - - - -
00181 
00182   // Point types
00183   // ===========
00184   // As said before, a point is a position in an image.  Because
00185   // Olena supports different dimension, it supports different
00186   // types of points: point1d, point2d, point3d.
00187 
00188   // You build a point by passing it as much coordinate as
00189   // needed.
00190   point1d p1(10);
00191   point2d p2(5, 3);
00192   // You can address the value associated to a particular point
00193   // in a image by using the [] operator:
00194   std::cout << "img1b[p1]" << img1b[p1] << std::endl; // print 42
00195 
00196   img2a[p2] = false;
00197   std::cout << "img2a:" << std::endl;
00198   std::cout << img2a << std::endl;
00199   // - - - - - - - - - -
00200   // - - - - - - - - - -
00201   // - - - - - - - - - -
00202   // - - - | | | | - - -
00203   // - - - | | | | - - -
00204   // - - - - | | | - - -
00205   // - - - | | | | - - -
00206   // - - - - - - - - - -
00207   // - - - - - - - - - -
00208   // - - - - - - - - - -
00209 
00210 
00211   // Delta Point types
00212   // =================
00213   // You can't add points together, this make no sense.  However
00214   // you can use a delta-point to record a displacement, and add
00215   // this delta-point to a point (yielding a new point).
00216   // The types for delta-points are dpoint1d, dpoint2d, and dpoint3d.
00217   dpoint1d dp1(2);
00218   std::cout << "p1 + dp1 = " << p1 + dp1 << std::endl;
00219   dpoint2d dp2(-1, 2);
00220   img2a[p2 + dp2] = false;
00221   std::cout << "img2a:" << std::endl;
00222   std::cout << img2a << std::endl;
00223   // - - - - - - - - - -
00224   // - - - - - - - - - -
00225   // - - - - - - - - - -
00226   // - - - | | | | - - -
00227   // - - - | | - | - - -
00228   // - - - - | | | - - -
00229   // - - - | | | | - - -
00230   // - - - - - - - - - -
00231   // - - - - - - - - - -
00232   // - - - - - - - - - -
00233 
00234 
00235   // Windows
00236   // =======
00237   // Windows are sets of delta-points.
00238   //
00239   // There are some predefined windows.
00240   // For instance here are two common window2d objects:
00241   std::cout << "win_c4p() = " << win_c4p() << std::endl;
00242   // [(-1,0)(0,-1)(0,0)(0,1)(1,0)]
00243   std::cout << "win_c4_only() = " << win_c4_only() << std::endl;
00244   // [(-1,0)(0,-1)(0,1)(1,0)]
00245 
00246   // A window is commonly used to iterate around a fixed point.
00247   // For instance
00248   window2d w2 = win_c4_only();
00249   for (unsigned i = 0; i < w2.card(); ++i)
00250     std::cout << "img2a[p2 + w2[" << i << "]] = "
00251               << "img2a[" << p2 + w2.dp(i) << "] = "
00252               << img2a[p2 + w2.dp(i)] << std::endl;
00253   // img2a[p2 + w2[0]] = img2a[(4,3)] = |
00254   // img2a[p2 + w2[1]] = img2a[(5,2)] = -
00255   // img2a[p2 + w2[2]] = img2a[(5,4)] = |
00256   // img2a[p2 + w2[3]] = img2a[(6,3)] = |
00257 
00258   // You can build you own window using by adding delta-points
00259   // using the add() method.
00260   window1d w1;
00261   w1.add(-1).add(0).add(1);
00262   std::cout << "w1 = " << w1 << std::endl; // [(-1)(0)(1)]
00263 
00264   // Neighborhoods
00265   // =============
00266   // These objects are like Windows, except they have
00267   // some additional properties (a point is not in its neighborhood,
00268   // the neighbor of a point should have the point in its neighborhood).
00269   // For this reason, they have types distinct from the windows:
00270   // neighborhood1d, neighborhood2d, neighborhood3d.
00271   // The interface is just the same as the windows.
00272   neighborhood1d n1;
00273   n1.add(1);
00274   std::cout << "n1 = " << n1 << std::endl; // N[(-1)(1)]
00275 
00276 
00277   // Processings.
00278   // ============
00279   // We have seen how to build images and windows, that's
00280   // enough to call some morphological operator.
00281   // Windows are used as structural elements, so we can
00282   // apply some basic morphological operator on img1a
00283   // (the saw) using w1 as structural element:
00284   std::cout << "erosion" << std::endl;
00285   std::cout << morpho::erosion(img1a, w1) << std::endl;
00286   // 0 0 1 2 0 0 0 1 2 0 0 0 1 2 0 0 0 1 2 3
00287   std::cout << "dilation" << std::endl;
00288   std::cout << morpho::dilation(img1a, w1) << std::endl;
00289   // 1 2 3 4 4 4 2 3 4 4 4 2 3 4 4 4 2 3 4 4
00290   std::cout << "opening" << std::endl;
00291   std::cout << morpho::opening(img1a, w1) << std::endl;
00292   // 0 1 2 2 2 0 1 2 2 2 0 1 2 2 2 0 1 2 3 3
00293   std::cout << "closing" << std::endl;
00294   std::cout << morpho::closing(img1a, w1) << std::endl;
00295   // 1 1 2 3 4 2 2 2 3 4 2 2 2 3 4 2 2 2 3 4
00296 
00297   // Yet, remember that Olena is a *generic* image
00298   // processing library, in which processing are meant
00299   // to be written once but yet work on many kind of images.
00300 
00301   // Let's close the small hole we have added to img2d (in the
00302   // paragraph about delta points).
00303   std::cout << morpho::closing(img2a, win_c4p()) << std::endl;
00304   // - - - - - - - - - -
00305   // - - - - - - - - - -
00306   // - - - - - - - - - -
00307   // - - - | | | | - - -
00308   // - - - | | | | - - -
00309   // - - - - | | | - - -
00310   // - - - | | | | - - -
00311   // - - - - - - - - - -
00312   // - - - - - - - - - -
00313   // - - - - - - - - - -
00314 
00315 
00316   // The watershed algorithm takes a neighborhood (not a window) as
00317   // argument, and a type as template parameter.
00318 
00319   // The type parameter specifies the type of the output image value.
00320   // Watershed are indicated with the max() of this type, other values
00321   // are basin labels.
00322   std::cout << "watershed" << std::endl;
00323   std::cout << morpho::watershed_seg<int_u8>(img1a, n1) << std::endl;
00324   // 4 4 4 4 255 3 3 3 3 255 2 2 2 2 255 1 1 1 1 1
00325 
00326 
00327   // Reading and writing PNM files.
00328   //   // You can read image2d from pnm files.  The kind
00329   // of pnm format supported (1,2,3,4,5,6) is dependent on
00330   // the type of the image you load/save.  The extension
00331   // is also used to guess the format to use.
00332   image2d<int_u8> lena = load(IMGDIR "/lena.pgm");
00333   if (!lena.has_impl())
00334     {
00335       std::cerr << "Could not load " << IMGDIR "/lena.pgm" << std::endl;
00336       exit(1);
00337     }
00338   std::cout << "watershedling lena..." << std::endl;
00339   save(morpho::watershed_seg<int_u8>(lena, neighb_c4()), "output.pgm");
00340 
00341 
00342   // High order operators.
00343   //   // Olena offers a set of high order operators than can simplify
00344   // the writing of simple algorithm.
00345   // For instance `traverse(f, img)' will apply the function `f'
00346   // to each point of `img'.  Actually, `f' do not need to be a function,
00347   // it just have to _behave_ like a function; it can be a functor, i.e.,
00348   // an instance of a class that implements an `operator()' (this allows
00349   // `f' to maintain a state without using a global variable).
00350 
00351   // The header `utils/stat.hh' define some functor to compute
00352   // statistical data.  In the following example we instantiate
00353   // an `f_moments' functor, have it traverse the saw-teeth image,
00354   // and compute some moments from the data gathered during the traversal.
00355   utils::f_moments<int_u8> f;
00356   traverse(f, img1a);
00357   std::cout << "f.mean() = "     << f.mean()     << std::endl;
00358   std::cout << "f.variance() = " << f.variance() << std::endl;
00359 
00360   // `apply' is an high order operator similar to `traverse', but
00361   // it builds an image from the results of the function application.
00362   //
00363   //   o = apply(f, i);
00364   //
00365   // is a short for
00366   //
00367   //  for all point p in i: o[p] = f(i[p])
00368   //
00369   // The type of the output image may be different from the type
00370   // of the input image, so `apply' need a little more help from
00371   // `f' than `traverse' does.  Basically, `f' should define
00372   // a type member named `result_type' so that `apply' knows which
00373   // type of output image it has to create.
00374   //
00375   // (NOTE: The `result_type' presence is part of the requirement of
00376   // the `Adaptable Unary Function' concept in the STL.  So if you
00377   // have a model of `Adaptable Unary Function', you can safely pass
00378   // it to `apply'.)
00379   //
00380   // Some conversion functions (those for which the output type is
00381   // knonw) can be used this way.  For instance the
00382   // `convert::bound<T>' functor will convert any value to type `T'
00383   // and saturate for out-of-bound values.  See how the `4' are
00384   // converted to `3' when the saw-teeth image is converted to an
00385   // image2d<int_u<2> > image.
00386   std::cout << apply(convert::bound<int_u<2> >(), img1a) << std::endl;
00387   // 0 1 2 3 3 0 1 2 3 3 0 1 2 3 3 0 1 2 3 3
00388 
00389   // Another usefull high order operator is `fold':
00390   // - `fold(f, val, img)' computes `f(...f(f(val,i_0),i_1)...,i_n)',
00391   //   where i_0...i_n are the value associated to each image point.
00392   // - `fold(f, img)' computes `f(...f(f(i_0,i_1),i_2)...,i_n)'.
00393   // In both cases, `f' must be a model of the
00394   // `Adaptable Binary Function' STL concept.
00395 }

Generated on Tue Oct 4 2011 15:23:23 for Milena (Olena) by  doxygen 1.7.1