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 }