Milena (Olena)  User documentation 2.0a Id
 All Classes Namespaces Functions Variables Typedefs Enumerator Groups Pages
2008__tour2.cc
1 // Copyright (C) 2001, 2007 EPITA Research and Development Laboratory
2 //
3 // This file is part of Olena.
4 //
5 // Olena is free software: you can redistribute it and/or modify it under
6 // the terms of the GNU General Public License as published by the Free
7 // Software Foundation, version 2 of the License.
8 //
9 // Olena is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Olena. If not, see <http://www.gnu.org/licenses/>.
16 //
17 // As a special exception, you may use this file as part of a free
18 // software project without restriction. Specifically, if other files
19 // instantiate templates or use macros or inline functions from this
20 // file, or you compile this file and link it with other files to produce
21 // an executable, this file does not by itself cause the resulting
22 // executable to be covered by the GNU General Public License. This
23 // exception does not however invalidate any other reasons why the
24 // executable file might be covered by the GNU General Public License.
25 
26 // File: tour2.cc.
27 
28 #include <oln/core/2d/image2d.hh>
29 #include <oln/core/2d/window2d.hh>
30 #include <oln/debug/println.hh>
31 
32 
33 
34 // Note to the reader: If you do not have read the tour1.cc file, you
35 // should have a quick look at it before proceeding with this present
36 // file. Some important features and practices are described in the
37 // former tour step that are here assumed to be known.
38 
39 
40 
41 int main()
42 {
43  using namespace oln;
44 
45  // As shown before, image data read access and pixel value
46  // assignments can be performed using:
47  // - either the parenthesis operator with takes a point as its
48  // argument,
49  // - or the ".at" method which requires the coordinates of the point
50  // you want to access to.
51 
52  // ex: ima(p), with p a 1D point, or ima.at(i), ima being a 1D
53  // image and i an index---single coordinate.
54 
55 
56  // Objects from image2d and image3d types can be build and access to
57  // in a similar way than image1d, except you have to account for
58  // respectively one and two extra coordinates. Let us take an
59  // example.
60 
61  image2d<bool> img(4, 5); // A 4x5 2D binary image.
62 
63  for (unsigned row = 0; row < img.nrows(); ++row)
64  for (unsigned col = 0; col < img.ncols(); ++col)
65  img.at(row, col) =
66  (row > 0 and row < 3) and (col > 0 and col < 4);
67 
68  debug::println(img); // Gives:
69  // - - - - -
70  // - | | | -
71  // - | | | -
72  // - - - - -
73 
74  // When debug::print'ing binary images, the 'true' (object) and
75  // 'false' (background) values are respectively depicted by the '|'
76  // and '-' symbols.
77 
78 
79 
80 
81  // As said before, a point is a position in an image. Because
82  // Olena supports different dimensions, it supports different
83  // point types: point1d, point2d, and point3d.
84 
85  // We build a point by passing it as much coordinates as
86  // needed:
87  point2d p(2, 0); // a 2D point so a couple of coordinates.
88 
89  // Each point coordinate can be accessed separately...
90  std::cout << "row = " << p.row() << std::endl
91  << "col = " << p.col() << std::endl;
92 
93  // ...and modified:
94  p.col() = 1;
95  std::cout << "col = " << p.col() << " (new value!)" << std::endl;
96 
97  // So we have a way to access image data:
98  img(p) = false;
99  // which is shorter than the equivalent writing:
100  // ima2a.at(2, 1) = false;
101 
102  debug::println(img);
103  // Gives (with hand-written coordinates):
104  // col 0 1 2 3 4
105  // row v
106  // 0 - - - - -
107  // 1 - | | | -
108  // 2 > - - | | -
109  // 3 - - - - -
110 
111  // Points in Olena are not really like mathematical vectors. The
112  // reason is twofold. Some operations over vectors are meaningless
113  // with points; for instance adding points together makes no sense.
114  // Furthermore we want C++ expressions with points to be more
115  // strongly typed so that errors from the user can be more easily
116  // pointed out.
117 
118  // To record a displacement from one point to another one, Olena
119  // introduces the notion of "delta-points". The types for
120  // delta-points are dpoint1d, dpoint2d, and dpoint3d. We have:
121 
122  dpoint2d dp(-1, +2); // -1 applies to row, +2 applies to column.
123  img(p + dp) = false;
124  debug::println(img);
125  // Gives:
126  // col 0 1 2 3 4
127  // row . . v
128  // 0 - - - - -
129  // 1 > - | | - -
130  // 2 . - - | | -
131  // 3 - - - - -
132 
133  // Let us verify:
134  point2d p2 = p + dp;
135  std::cout << "p2 " << p2
136  << " = p " << p
137  << " + dp " << dp << std::endl;
138  assert(p2 == point2d(1, 3)); // Right: p2 is (1, 3).
139 
140  // Taking the difference between a couple of points gives a
141  // delta-point.
142  dpoint2d dp2 = p2 - p; // That should be equal to 'dp'.
143  assert(dp2 == dp); // Indeed.
144 
145  // Delta-points (amongst them) feature the classical arithmetical
146  // operators:
147  dpoint2d dp3 = dp + dp2;
148  dp3 -= dp2;
149  assert(dp3 == dp);
150 
151 
152  // The notion of delta-point is at the base of the definitions of
153  // classical window and neighborhood.
154 
155 
156  // A window, or "sliding window", is a region defined around a
157  // point. In its most usual forms offered by Olena, for instance
158  // window2d for the 2D case, windows are internally defined by a set
159  // of delta-points.
160 
161  window2d win; // Empty window.
162  win
163  .take(dpoint2d(-1, -1)) // Add a delta-point.
164  .take(dpoint2d( 0, 0))
165  .take(dpoint2d( 1, 1))
166  .take(dpoint2d( 1, 2));
167  std::cout << "win = " << win << std::endl;
168  // win = [ (-1, -1), (0, 0), (1, 1), (1, 2) ]
169 
170  // A window is commonly used to iterate around a fixed point.
171  std::cout << "around " << p
172  << " window points are ";
173  for (unsigned i = 0; i < win.size(); ++i)
174  std::cout << (p + win[i]) // win[i] gives the ith element of the
175  << ' '; // window, i.e., a delta-point.
176  std::cout << std::endl;
177  // The code above outputs:
178  // around (2, 1) window points are (1, 0) (2, 1) (3, 2) (3, 3)
179 
180 
181  // If we try to generalize this kind of display to all image points,
182  // one straightforward code is the following:
183 
184  {
185  // First version.
186 
187  std::cout << std::endl
188  << "First version:" << std::endl
189  << std::endl;
190 
191  for (unsigned row = 0; row < img.nrows(); ++row)
192  for (unsigned col = 0; col < img.ncols(); ++col)
193  {
194  point2d p(row, col);
195  std::cout << p << ": ";
196  for (unsigned i = 0; i < win.size(); ++i)
197  {
198  point2d q = p + win[i]; // q is a point around p;
199  // precisely, the ith point of
200  // the window win centered at
201  // point p
202  if (img.has(q)) // we only print q if it actually lies
203  // within the image
204  std::cout << q << ' ';
205  }
206  std::cout << std::endl;
207  }
208 
209  } // End of 1st version.
210 
211  // We obtain:
212  // (0, 0): (0, 0) (1, 1) (1, 2)
213  // (0, 1): (0, 1) (1, 2) (1, 3)
214  // (0, 2): (0, 2) (1, 3) (1, 4)
215  // (0, 3): (0, 3) (1, 4)
216  // (0, 4): (0, 4)
217  // (1, 0): (1, 0) (2, 1) (2, 2)
218  // (1, 1): (0, 0) (1, 1) (2, 2) (2, 3)
219  // (1, 2): (0, 1) (1, 2) (2, 3) (2, 4)
220  // (1, 3): (0, 2) (1, 3) (2, 4)
221  // (1, 4): (0, 3) (1, 4)
222  // (2, 0): (2, 0) (3, 1) (3, 2)
223  // (2, 1): (1, 0) (2, 1) (3, 2) (3, 3)
224  // (2, 2): (1, 1) (2, 2) (3, 3) (3, 4)
225  // (2, 3): (1, 2) (2, 3) (3, 4)
226  // (2, 4): (1, 3) (2, 4)
227  // (3, 0): (3, 0)
228  // (3, 1): (2, 0) (3, 1)
229  // (3, 2): (2, 1) (3, 2)
230  // (3, 3): (2, 2) (3, 3)
231  // (3, 4): (2, 3) (3, 4)
232 
233 
234  // An equivalent code, given here just for you to realize that you
235  // may continue to handle images and points with Olena just the way
236  // you are used to it:
237 
238  /*
239 
240  { // A variation.
241 
242  int nrows = img.nrows(), ncols = img.ncols();
243  unsigned n = win.size();
244  for (int row = 0; row < nrows; ++row)
245  for (int col = 0; col < ncols; ++col)
246  {
247  std::cout << "(" << row << ", " << col << "): "; // print p
248  for (unsigned i = 0; i < n; ++i)
249  {
250  int // define q coordinates:
251  r = row + win[i].row(),
252  c = col + win[i].col();
253  if (r >= 0 and r < nrows and c >= 0 and c < ncols) // q is in img
254  std::cout << "(" << r << ", " << c << ") "; // print q
255  }
256  std::cout << std::endl;
257  }
258 
259  } // End of a variation.
260 
261  */
262 
263 
264  // Such samples of "classical" image processing code have 3 (three!)
265  // main drawbacks:
266 
267  // - it is error-prone; note that there is rather a lot of code for
268  // a so simple algorithm;
269 
270  // - the algorithm, that is, the most important part of this code, is
271  // totally drowned in the middle of implementation details;
272 
273  // - this kind of writing only applies to a very special type of
274  // images (2D ones, rectangular, and starting at (0,0)) so it is
275  // not reusable.
276 
277 
278  // If we express the algorithm into natural language, we can say:
279  //
280  // p, a point of img
281  // q, a point of win centered at p
282  //
283  // for all p
284  // print p
285  // for all q
286  // if q is in img
287  // print q
288  // print end of line
289  // end for
290  // end for
291 
292 
293  // The Olena library has been designed so that you can easily
294  // translate your algorithms into code. With the running example we
295  // can write:
296 
297  { // Second version.
298 
299  std::cout << std::endl
300  << "Second version (same result):" << std::endl
301  << std::endl;
302 
303  box2d::piter p (img.points());
304  window2d::qiter q (win, p);
305 
306  for_all(p)
307  {
308  std::cout << p << ": ";
309  for_all(q)
310  if (img.has(q))
311  std::cout << q << ' ';
312  std::cout << std::endl;
313  }
314 
315  } // End of 2nd version.
316 
317  std::cout << std::endl;
318 
319 
320  // Above, p and q behave just like points; for instance, the
321  // following expressions are valid:
322 
323  // int r = p.row();
324  // to get the current row value,
325 
326  // bool b = img(p);
327  // to get the pixel value at the current point,
328 
329  // or point2d pp = p + dp;
330  // where dp is a delta-point to get a point nearby p.
331 
332  // Yet, p and q are "more than points" since they allow to
333  // browse/iterate over a set of points, respectivelly, the domain of
334  // 'img' and the window centered at p.
335 
336 
337  // The domain of 'img' is obtained with "img.points()" and is
338  // provided to the 'p' object so that it knows how to iterate.
339 
340  // For a "basic" image, its set of points is an n-dimensional box.
341  // In the 2D space, the box type is called 'box2d'. We also have
342  // 'box1d' and 'box3d' for other dimensions.
343 
344  box2d pts = img.points();
345  std::cout << "img points are " << pts << std::endl;
346  // Prints:
347  // img points are { (0, 0) .. (3, 4) }
348 
349  // The type of iterators over a point set is obtained with the
350  // expression: "name_of_the_point_set_type::piter", where 'piter'
351  // means "point iterator" for short.
352 
353  // The same construction is available for iterators on window
354  // points, whose types are obtained in a similar way with
355  // "name_of_the_window_type::qiter". Here the 'q' in 'qiter'
356  // emphases the fact that a window is not really a set of points but
357  // "a set of dpoints and a center point".
358 
359 
360 
361  // The second version of our example contrasts with the more
362  // "classical" ones; it is:
363 
364  // - shorter,
365  // so it is less error-prone for the developer;
366 
367  // - easy to read,
368  // so the algorithm appears clearly from the code;
369 
370  // - (almost) "generic",
371  // for instance, no details within the loops indicate that we are
372  // processing a 2D image, that this image is rectangular, etc.
373 
374 
375  // +-----------------------------------------------------------+
376  // | |
377  // | A major feature of Olena is to offer to its users a way |
378  // | to write GENERIC algorithms, that is, algorithms that |
379  // | accept different image types as input. |
380  // | |
381  // +-----------------------------------------------------------+
382 
383 
384  // The next files of the tour give many details about what you can
385  // expect from the notion of "genericity" applied to image
386  // processing.
387 
388  // Now you can jump to tour3.cc
389 
390 
391 }