Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
2007__tour.cc
1 /* This is a short tour of the basics of Olena. It isn't in any way
2  meant to cover all the features of Olena. Especially, some
3  important objects, such as the iterators, are NOT presented here.
4  */
5 
6 // We will run some example with 1D, and 2D, so we include these two
7 // files.
8 #include <oln/basics1d.hh>
9 #include <oln/basics2d.hh>
10 //#include <oln/basics3d.hh>
11 
12 // Let's include all types, for simplicity.
13 #include <ntg/all.hh>
14 
15 // Some algorithms...
16 #include <oln/morpho/erosion.hh>
17 #include <oln/morpho/dilation.hh>
18 #include <oln/morpho/opening.hh>
19 #include <oln/morpho/closing.hh>
20 #include <oln/morpho/watershed.hh>
21 
22 #include <oln/utils/stat.hh>
23 
24 // Basic conversion functions.
25 #include <oln/convert/basics.hh>
26 
27 // Always include system headers after Olena.
28 #include <iostream>
29 #include <cmath>
30 
31 
32 
33 // Namespaces.
34 // ===========
35 // Olena is organized in a namespace hierarchy. Every thing is
36 // declared by Olena under the 'oln::' namespace, and possibly un a
37 // sub namespace such as 'oln::arith::' (arithmetic operations on
38 // images), 'oln::morpho::' (morphological operations), etc.
39 // For the sake of simplicity, we will neglect the 'oln::' prefix
40 // in this file.
41 using namespace oln;
42 
43 // Data types are defined in the Intègre library, so they are
44 // in the ntg namespace.
45 // In this file, we will neglect the 'ntg::' prefix.
46 using namespace ntg;
47 
48 int
49 main (void)
50 {
51  // Basic value types.
52  // ==================
53  // Olena ships with a set of basic value types such as
54  // int_u8, int_u16, int_u32, (common unsigned integer types)
55  // int_s8, int_s16, int_s32, (common signed integer types)
56  // float_s, float_d, (common float types)
57  // bin (binary type: false|true)
58  //
59  // These value are defined in the value/ subdirectory. Actually
60  // we defined all of them by including value/all.hh.
61  //
62  // You should use them instead of the standard C/C++ type, because
63  // Olena types are equipped with additional members required to
64  // write generic processings.
65 
66  // For instance the max() and min() class methods
67  // will return the maximum value of a type.
68  std::cout << "ntg_max_val(int_u8) = "
69  << ntg_max_val(int_u8) << std::endl;
70 
71  // You can combine these values with the standard operators
72  // as expected.
73  int_u8 a = 4;
74  int_u16 b = 1000;
75  std::cout << "a + b = " << a + b << std::endl;
76 
77  // Sometime it's hard to figure which type a variable has, because
78  // Olena seems to behave surprisingly at a first glance.
79  // There are two convenient functions that you can use to
80  // inspect a type.
81  // typename_of<T>() returns a std::string describing T
82  // typename_of_var(V) returns a std::string describing the type of V
83  std::cout << typename_of<int_u8>()
84  << " + "
85  << typename_of_var(b)
86  << " = " << typename_of_var(a + b) << std::endl;
87  // The expression above will print
88  // "int_u<8> + int_u<16> = int_u<17>"
89  // This probably seems surprising for two reasons:
90  // 1) int_u8 is printed as int_u<8>
91  // this is because int_u8 is really just a short hand for int_u<8>,
92  // Olena allows you to declare unsigned integer on 13 bits, if
93  // you want (int_u<13>); but int_u<8>, int_u<16>, and int_u<32> are
94  // more common so they have their own typedef.
95  // 2) (a + b) has type int_u<17>, not int_u<16> as one would expect.
96  // The point is that one needs 17 bits to hold the result
97  // of an addition between a 8bit and a 16bit value without
98  // overflowing.
99 
100  // Olena will also ensure that you don't such a large value to
101  // a variable with a smaller type:
102  // int_u16 c = a + b;
103  // would fail to compile because (a + b) has type int_u<17> which
104  // cannot fit in int_u<16>.
105  // If you *know* that the result of (a + b) fits in c, you
106  // should cast the value to int_u16 explicitly:
107  int_u16 c = a + b;
108  std::cout << "c = " << c << std::endl;
109  // Of course assigning a+b to a variable of type int_u<17> or
110  // greater is allowed and doesn't require a cast.
111 
112 
113  // Image types.
114  // ============
115  // There are three kind of images supported for the moment.
116  // image1d, image2d, and image3d: the names are self-explanatory.
117  // All are template, parametrized by the kind of value associated
118  // to each point (we call 'point' a position in the image).
119  //
120  // We will start to use the 1D images, because they are
121  // easier to fill manually (i.e. there are less value
122  // to assign), and easy to display.
123 
124  // Here are two most basic way to build an image.
125  image1d<int_u8> img1a; // Build an empty image.
126  image1d<int_u8> img1b(20); // Build an image with 20 points.
127 
128  // The empty image hasn't any data. It's really useful
129  // only as a placeholder for the result of some processing,
130  // or another image.
131 
132  // Assigning an image to another one is not a costly operation.
133  // In fact, img1a behave like some pointers. After the following
134  // instruction
135  img1a = img1b;
136  // img1a and img1b can be used equivalently to access the
137  // same data (in other words, the point values are shared
138  // by img1a and img1b).
139  img1a(10) = 42; // Assign value 42 to the 11th point of img1a
140  std::cout << "img1b(10) ="
141  << img1b(10) << std::endl; // print 42, although we use img1b
142 
143  // We call these assignments 'shallow copies', because they don't copy the
144  // data. If you want to make a deep copy of an image, then use
145  // the clone() method, as in Java.
146  img1a = img1b.clone ();
147  img1a(10) = 13;
148  std::cout << "img1b(10) ="
149  << img1b(10) << std::endl; // still print 42
150 
151  // As show above, assignments can be made using the parenthesis
152  // operator, which takes to coordinates of the point you want to
153  // assign a value to in argument. (In 1D there is only one coordinate.)
154  // Let's fill our image with a saw-teeth signal.
155  // (coord is the type used for coordinates)
156  for (coord col = 0; col < img1a.ncols(); ++col)
157  img1a(col) = col % 5;
158 
159  std::cout << "img1a:" << std::endl;
160  std::cout << img1a << std::endl;
161  // 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
162 
163  // You can build image2d and image3d objects similarly, except
164  // you have to account for the extra coordinate.
165  image2d<bin> img2a(10, 10);
166  for (coord row = 0; row < img2a.nrows(); ++row)
167  for (coord col = 0; col < img2a.ncols(); ++col)
168  img2a(row, col) = (row >= 3 && col >= 3 && row < 7 && col < 7);
169  std::cout << "img2a:" << std::endl;
170  std::cout << img2a << std::endl;
171  // - - - - - - - - - -
172  // - - - - - - - - - -
173  // - - - - - - - - - -
174  // - - - | | | | - - -
175  // - - - | | | | - - -
176  // - - - | | | | - - -
177  // - - - | | | | - - -
178  // - - - - - - - - - -
179  // - - - - - - - - - -
180  // - - - - - - - - - -
181 
182  // Point types
183  // ===========
184  // As said before, a point is a position in an image. Because
185  // Olena supports different dimension, it supports different
186  // types of points: point1d, point2d, point3d.
187 
188  // You build a point by passing it as much coordinate as
189  // needed.
190  point1d p1(10);
191  point2d p2(5, 3);
192  // You can address the value associated to a particular point
193  // in a image by using the [] operator:
194  std::cout << "img1b[p1]" << img1b[p1] << std::endl; // print 42
195 
196  img2a[p2] = false;
197  std::cout << "img2a:" << std::endl;
198  std::cout << img2a << std::endl;
199  // - - - - - - - - - -
200  // - - - - - - - - - -
201  // - - - - - - - - - -
202  // - - - | | | | - - -
203  // - - - | | | | - - -
204  // - - - - | | | - - -
205  // - - - | | | | - - -
206  // - - - - - - - - - -
207  // - - - - - - - - - -
208  // - - - - - - - - - -
209 
210 
211  // Delta Point types
212  // =================
213  // You can't add points together, this make no sense. However
214  // you can use a delta-point to record a displacement, and add
215  // this delta-point to a point (yielding a new point).
216  // The types for delta-points are dpoint1d, dpoint2d, and dpoint3d.
217  dpoint1d dp1(2);
218  std::cout << "p1 + dp1 = " << p1 + dp1 << std::endl;
219  dpoint2d dp2(-1, 2);
220  img2a[p2 + dp2] = false;
221  std::cout << "img2a:" << std::endl;
222  std::cout << img2a << std::endl;
223  // - - - - - - - - - -
224  // - - - - - - - - - -
225  // - - - - - - - - - -
226  // - - - | | | | - - -
227  // - - - | | - | - - -
228  // - - - - | | | - - -
229  // - - - | | | | - - -
230  // - - - - - - - - - -
231  // - - - - - - - - - -
232  // - - - - - - - - - -
233 
234 
235  // Windows
236  // =======
237  // Windows are sets of delta-points.
238  //
239  // There are some predefined windows.
240  // For instance here are two common window2d objects:
241  std::cout << "win_c4p() = " << win_c4p() << std::endl;
242  // [(-1,0)(0,-1)(0,0)(0,1)(1,0)]
243  std::cout << "win_c4_only() = " << win_c4_only() << std::endl;
244  // [(-1,0)(0,-1)(0,1)(1,0)]
245 
246  // A window is commonly used to iterate around a fixed point.
247  // For instance
248  window2d w2 = win_c4_only();
249  for (unsigned i = 0; i < w2.card(); ++i)
250  std::cout << "img2a[p2 + w2[" << i << "]] = "
251  << "img2a[" << p2 + w2.dp(i) << "] = "
252  << img2a[p2 + w2.dp(i)] << std::endl;
253  // img2a[p2 + w2[0]] = img2a[(4,3)] = |
254  // img2a[p2 + w2[1]] = img2a[(5,2)] = -
255  // img2a[p2 + w2[2]] = img2a[(5,4)] = |
256  // img2a[p2 + w2[3]] = img2a[(6,3)] = |
257 
258  // You can build you own window using by adding delta-points
259  // using the add() method.
260  window1d w1;
261  w1.add(-1).add(0).add(1);
262  std::cout << "w1 = " << w1 << std::endl; // [(-1)(0)(1)]
263 
264  // Neighborhoods
265  // =============
266  // These objects are like Windows, except they have
267  // some additional properties (a point is not in its neighborhood,
268  // the neighbor of a point should have the point in its neighborhood).
269  // For this reason, they have types distinct from the windows:
270  // neighborhood1d, neighborhood2d, neighborhood3d.
271  // The interface is just the same as the windows.
272  neighborhood1d n1;
273  n1.add(1);
274  std::cout << "n1 = " << n1 << std::endl; // N[(-1)(1)]
275 
276 
277  // Processings.
278  // ============
279  // We have seen how to build images and windows, that's
280  // enough to call some morphological operator.
281  // Windows are used as structural elements, so we can
282  // apply some basic morphological operator on img1a
283  // (the saw) using w1 as structural element:
284  std::cout << "erosion" << std::endl;
285  std::cout << morpho::erosion(img1a, w1) << std::endl;
286  // 0 0 1 2 0 0 0 1 2 0 0 0 1 2 0 0 0 1 2 3
287  std::cout << "dilation" << std::endl;
288  std::cout << morpho::dilation(img1a, w1) << std::endl;
289  // 1 2 3 4 4 4 2 3 4 4 4 2 3 4 4 4 2 3 4 4
290  std::cout << "opening" << std::endl;
291  std::cout << morpho::opening(img1a, w1) << std::endl;
292  // 0 1 2 2 2 0 1 2 2 2 0 1 2 2 2 0 1 2 3 3
293  std::cout << "closing" << std::endl;
294  std::cout << morpho::closing(img1a, w1) << std::endl;
295  // 1 1 2 3 4 2 2 2 3 4 2 2 2 3 4 2 2 2 3 4
296 
297  // Yet, remember that Olena is a *generic* image
298  // processing library, in which processing are meant
299  // to be written once but yet work on many kind of images.
300 
301  // Let's close the small hole we have added to img2d (in the
302  // paragraph about delta points).
303  std::cout << morpho::closing(img2a, win_c4p()) << std::endl;
304  // - - - - - - - - - -
305  // - - - - - - - - - -
306  // - - - - - - - - - -
307  // - - - | | | | - - -
308  // - - - | | | | - - -
309  // - - - - | | | - - -
310  // - - - | | | | - - -
311  // - - - - - - - - - -
312  // - - - - - - - - - -
313  // - - - - - - - - - -
314 
315 
316  // The watershed algorithm takes a neighborhood (not a window) as
317  // argument, and a type as template parameter.
318 
319  // The type parameter specifies the type of the output image value.
320  // Watershed are indicated with the max() of this type, other values
321  // are basin labels.
322  std::cout << "watershed" << std::endl;
323  std::cout << morpho::watershed_seg<int_u8>(img1a, n1) << std::endl;
324  // 4 4 4 4 255 3 3 3 3 255 2 2 2 2 255 1 1 1 1 1
325 
326 
327  // Reading and writing PNM files.
328  // // You can read image2d from pnm files. The kind
329  // of pnm format supported (1,2,3,4,5,6) is dependent on
330  // the type of the image you load/save. The extension
331  // is also used to guess the format to use.
332  image2d<int_u8> lena = load(IMGDIR "/lena.pgm");
333  if (!lena.has_impl())
334  {
335  std::cerr << "Could not load " << IMGDIR "/lena.pgm" << std::endl;
336  exit(1);
337  }
338  std::cout << "watershedling lena..." << std::endl;
339  save(morpho::watershed_seg<int_u8>(lena, neighb_c4()), "output.pgm");
340 
341 
342  // High order operators.
343  // // Olena offers a set of high order operators than can simplify
344  // the writing of simple algorithm.
345  // For instance `traverse(f, img)' will apply the function `f'
346  // to each point of `img'. Actually, `f' do not need to be a function,
347  // it just have to _behave_ like a function; it can be a functor, i.e.,
348  // an instance of a class that implements an `operator()' (this allows
349  // `f' to maintain a state without using a global variable).
350 
351  // The header `utils/stat.hh' define some functor to compute
352  // statistical data. In the following example we instantiate
353  // an `f_moments' functor, have it traverse the saw-teeth image,
354  // and compute some moments from the data gathered during the traversal.
355  utils::f_moments<int_u8> f;
356  traverse(f, img1a);
357  std::cout << "f.mean() = " << f.mean() << std::endl;
358  std::cout << "f.variance() = " << f.variance() << std::endl;
359 
360  // `apply' is an high order operator similar to `traverse', but
361  // it builds an image from the results of the function application.
362  //
363  // o = apply(f, i);
364  //
365  // is a short for
366  //
367  // for all point p in i: o[p] = f(i[p])
368  //
369  // The type of the output image may be different from the type
370  // of the input image, so `apply' need a little more help from
371  // `f' than `traverse' does. Basically, `f' should define
372  // a type member named `result_type' so that `apply' knows which
373  // type of output image it has to create.
374  //
375  // (NOTE: The `result_type' presence is part of the requirement of
376  // the `Adaptable Unary Function' concept in the STL. So if you
377  // have a model of `Adaptable Unary Function', you can safely pass
378  // it to `apply'.)
379  //
380  // Some conversion functions (those for which the output type is
381  // knonw) can be used this way. For instance the
382  // `convert::bound<T>' functor will convert any value to type `T'
383  // and saturate for out-of-bound values. See how the `4' are
384  // converted to `3' when the saw-teeth image is converted to an
385  // image2d<int_u<2> > image.
386  std::cout << apply(convert::bound<int_u<2> >(), img1a) << std::endl;
387  // 0 1 2 3 3 0 1 2 3 3 0 1 2 3 3 0 1 2 3 3
388 
389  // Another usefull high order operator is `fold':
390  // - `fold(f, val, img)' computes `f(...f(f(val,i_0),i_1)...,i_n)',
391  // where i_0...i_n are the value associated to each image point.
392  // - `fold(f, img)' computes `f(...f(f(i_0,i_1),i_2)...,i_n)'.
393  // In both cases, `f' must be a model of the
394  // `Adaptable Binary Function' STL concept.
395 }