00001 // Copyright (C) 2001, 2007 EPITA Research and Development Laboratory 00002 // 00003 // This file is part of the Milena Library. This library is free 00004 // software; you can redistribute it and/or modify it under the terms 00005 // of the GNU General Public License version 2 as published by the 00006 // Free Software Foundation. 00007 // 00008 // This library is distributed in the hope that it will be useful, 00009 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 // General Public License for more details. 00012 // 00013 // You should have received a copy of the GNU General Public License 00014 // along with this library; see the file COPYING. If not, write to 00015 // the Free Software Foundation, 51 Franklin Street, Fifth Floor, 00016 // Boston, MA 02111-1307, USA. 00017 // 00018 // As a special exception, you may use this file as part of a free 00019 // software library without restriction. Specifically, if other files 00020 // instantiate templates or use macros or inline functions from this 00021 // file, or you compile this file and link it with other files to 00022 // produce an executable, this file does not by itself cause the 00023 // resulting executable to be covered by the GNU General Public 00024 // License. This exception does not however invalidate any other 00025 // reasons why the executable file might be covered by the GNU General 00026 // Public License. 00027 00028 // File: tour2.cc. 00029 00030 #include <oln/core/2d/image2d.hh> 00031 #include <oln/core/2d/window2d.hh> 00032 #include <oln/debug/println.hh> 00033 00034 00035 00036 // Note to the reader: If you do not have read the tour1.cc file, you 00037 // should have a quick look at it before proceeding with this present 00038 // file. Some important features and practices are described in the 00039 // former tour step that are here assumed to be known. 00040 00041 00042 00043 int main() 00044 { 00045 using namespace oln; 00046 00047 // As shown before, image data read access and pixel value 00048 // assignments can be performed using: 00049 // - either the parenthesis operator with takes a point as its 00050 // argument, 00051 // - or the ".at" method which requires the coordinates of the point 00052 // you want to access to. 00053 00054 // ex: ima(p), with p a 1D point, or ima.at(i), ima being a 1D 00055 // image and i an index---single coordinate. 00056 00057 00058 // Objects from image2d and image3d types can be build and access to 00059 // in a similar way than image1d, except you have to account for 00060 // respectively one and two extra coordinates. Let us take an 00061 // example. 00062 00063 image2d<bool> img(4, 5); // A 4x5 2D binary image. 00064 00065 for (unsigned row = 0; row < img.nrows(); ++row) 00066 for (unsigned col = 0; col < img.ncols(); ++col) 00067 img.at(row, col) = 00068 (row > 0 and row < 3) and (col > 0 and col < 4); 00069 00070 debug::println(img); // Gives: 00071 // - - - - - 00072 // - | | | - 00073 // - | | | - 00074 // - - - - - 00075 00076 // When debug::print'ing binary images, the 'true' (object) and 00077 // 'false' (background) values are respectively depicted by the '|' 00078 // and '-' symbols. 00079 00080 00081 00082 00083 // As said before, a point is a position in an image. Because 00084 // Olena supports different dimensions, it supports different 00085 // point types: point1d, point2d, and point3d. 00086 00087 // We build a point by passing it as much coordinates as 00088 // needed: 00089 point2d p(2, 0); // a 2D point so a couple of coordinates. 00090 00091 // Each point coordinate can be accessed separately... 00092 std::cout << "row = " << p.row() << std::endl 00093 << "col = " << p.col() << std::endl; 00094 00095 // ...and modified: 00096 p.col() = 1; 00097 std::cout << "col = " << p.col() << " (new value!)" << std::endl; 00098 00099 // So we have a way to access image data: 00100 img(p) = false; 00101 // which is shorter than the equivalent writing: 00102 // ima2a.at(2, 1) = false; 00103 00104 debug::println(img); 00105 // Gives (with hand-written coordinates): 00106 // col 0 1 2 3 4 00107 // row v 00108 // 0 - - - - - 00109 // 1 - | | | - 00110 // 2 > - - | | - 00111 // 3 - - - - - 00112 00113 // Points in Olena are not really like mathematical vectors. The 00114 // reason is twofold. Some operations over vectors are meaningless 00115 // with points; for instance adding points together makes no sense. 00116 // Furthermore we want C++ expressions with points to be more 00117 // strongly typed so that errors from the user can be more easily 00118 // pointed out. 00119 00120 // To record a displacement from one point to another one, Olena 00121 // introduces the notion of "delta-points". The types for 00122 // delta-points are dpoint1d, dpoint2d, and dpoint3d. We have: 00123 00124 dpoint2d dp(-1, +2); // -1 applies to row, +2 applies to column. 00125 img(p + dp) = false; 00126 debug::println(img); 00127 // Gives: 00128 // col 0 1 2 3 4 00129 // row . . v 00130 // 0 - - - - - 00131 // 1 > - | | - - 00132 // 2 . - - | | - 00133 // 3 - - - - - 00134 00135 // Let us verify: 00136 point2d p2 = p + dp; 00137 std::cout << "p2 " << p2 00138 << " = p " << p 00139 << " + dp " << dp << std::endl; 00140 assert(p2 == point2d(1, 3)); // Right: p2 is (1, 3). 00141 00142 // Taking the difference between a couple of points gives a 00143 // delta-point. 00144 dpoint2d dp2 = p2 - p; // That should be equal to 'dp'. 00145 assert(dp2 == dp); // Indeed. 00146 00147 // Delta-points (amongst them) feature the classical arithmetical 00148 // operators: 00149 dpoint2d dp3 = dp + dp2; 00150 dp3 -= dp2; 00151 assert(dp3 == dp); 00152 00153 00154 // The notion of delta-point is at the base of the definitions of 00155 // classical window and neighborhood. 00156 00157 00158 // A window, or "sliding window", is a region defined around a 00159 // point. In its most usual forms offered by Olena, for instance 00160 // window2d for the 2D case, windows are internally defined by a set 00161 // of delta-points. 00162 00163 window2d win; // Empty window. 00164 win 00165 .take(dpoint2d(-1, -1)) // Add a delta-point. 00166 .take(dpoint2d( 0, 0)) 00167 .take(dpoint2d( 1, 1)) 00168 .take(dpoint2d( 1, 2)); 00169 std::cout << "win = " << win << std::endl; 00170 // win = [ (-1, -1), (0, 0), (1, 1), (1, 2) ] 00171 00172 // A window is commonly used to iterate around a fixed point. 00173 std::cout << "around " << p 00174 << " window points are "; 00175 for (unsigned i = 0; i < win.size(); ++i) 00176 std::cout << (p + win[i]) // win[i] gives the ith element of the 00177 << ' '; // window, i.e., a delta-point. 00178 std::cout << std::endl; 00179 // The code above outputs: 00180 // around (2, 1) window points are (1, 0) (2, 1) (3, 2) (3, 3) 00181 00182 00183 // If we try to generalize this kind of display to all image points, 00184 // one straightforward code is the following: 00185 00186 { 00187 // First version. 00188 00189 std::cout << std::endl 00190 << "First version:" << std::endl 00191 << std::endl; 00192 00193 for (unsigned row = 0; row < img.nrows(); ++row) 00194 for (unsigned col = 0; col < img.ncols(); ++col) 00195 { 00196 point2d p(row, col); 00197 std::cout << p << ": "; 00198 for (unsigned i = 0; i < win.size(); ++i) 00199 { 00200 point2d q = p + win[i]; // q is a point around p; 00201 // precisely, the ith point of 00202 // the window win centered at 00203 // point p 00204 if (img.has(q)) // we only print q if it actually lies 00205 // within the image 00206 std::cout << q << ' '; 00207 } 00208 std::cout << std::endl; 00209 } 00210 00211 } // End of 1st version. 00212 00213 // We obtain: 00214 // (0, 0): (0, 0) (1, 1) (1, 2) 00215 // (0, 1): (0, 1) (1, 2) (1, 3) 00216 // (0, 2): (0, 2) (1, 3) (1, 4) 00217 // (0, 3): (0, 3) (1, 4) 00218 // (0, 4): (0, 4) 00219 // (1, 0): (1, 0) (2, 1) (2, 2) 00220 // (1, 1): (0, 0) (1, 1) (2, 2) (2, 3) 00221 // (1, 2): (0, 1) (1, 2) (2, 3) (2, 4) 00222 // (1, 3): (0, 2) (1, 3) (2, 4) 00223 // (1, 4): (0, 3) (1, 4) 00224 // (2, 0): (2, 0) (3, 1) (3, 2) 00225 // (2, 1): (1, 0) (2, 1) (3, 2) (3, 3) 00226 // (2, 2): (1, 1) (2, 2) (3, 3) (3, 4) 00227 // (2, 3): (1, 2) (2, 3) (3, 4) 00228 // (2, 4): (1, 3) (2, 4) 00229 // (3, 0): (3, 0) 00230 // (3, 1): (2, 0) (3, 1) 00231 // (3, 2): (2, 1) (3, 2) 00232 // (3, 3): (2, 2) (3, 3) 00233 // (3, 4): (2, 3) (3, 4) 00234 00235 00236 // An equivalent code, given here just for you to realize that you 00237 // may continue to handle images and points with Olena just the way 00238 // you are used to it: 00239 00240 /* 00241 00242 { // A variation. 00243 00244 int nrows = img.nrows(), ncols = img.ncols(); 00245 unsigned n = win.size(); 00246 for (int row = 0; row < nrows; ++row) 00247 for (int col = 0; col < ncols; ++col) 00248 { 00249 std::cout << "(" << row << ", " << col << "): "; // print p 00250 for (unsigned i = 0; i < n; ++i) 00251 { 00252 int // define q coordinates: 00253 r = row + win[i].row(), 00254 c = col + win[i].col(); 00255 if (r >= 0 and r < nrows and c >= 0 and c < ncols) // q is in img 00256 std::cout << "(" << r << ", " << c << ") "; // print q 00257 } 00258 std::cout << std::endl; 00259 } 00260 00261 } // End of a variation. 00262 00263 */ 00264 00265 00266 // Such samples of "classical" image processing code have 3 (three!) 00267 // main drawbacks: 00268 00269 // - it is error-prone; note that there is rather a lot of code for 00270 // a so simple algorithm; 00271 00272 // - the algorithm, that is, the most important part of this code, is 00273 // totally drowned in the middle of implementation details; 00274 00275 // - this kind of writing only applies to a very special type of 00276 // images (2D ones, rectangular, and starting at (0,0)) so it is 00277 // not reusable. 00278 00279 00280 // If we express the algorithm into natural language, we can say: 00281 // 00282 // p, a point of img 00283 // q, a point of win centered at p 00284 // 00285 // for all p 00286 // print p 00287 // for all q 00288 // if q is in img 00289 // print q 00290 // print end of line 00291 // end for 00292 // end for 00293 00294 00295 // The Olena library has been designed so that you can easily 00296 // translate your algorithms into code. With the running example we 00297 // can write: 00298 00299 { // Second version. 00300 00301 std::cout << std::endl 00302 << "Second version (same result):" << std::endl 00303 << std::endl; 00304 00305 box2d::piter p (img.points()); 00306 window2d::qiter q (win, p); 00307 00308 for_all(p) 00309 { 00310 std::cout << p << ": "; 00311 for_all(q) 00312 if (img.has(q)) 00313 std::cout << q << ' '; 00314 std::cout << std::endl; 00315 } 00316 00317 } // End of 2nd version. 00318 00319 std::cout << std::endl; 00320 00321 00322 // Above, p and q behave just like points; for instance, the 00323 // following expressions are valid: 00324 00325 // int r = p.row(); 00326 // to get the current row value, 00327 00328 // bool b = img(p); 00329 // to get the pixel value at the current point, 00330 00331 // or point2d pp = p + dp; 00332 // where dp is a delta-point to get a point nearby p. 00333 00334 // Yet, p and q are "more than points" since they allow to 00335 // browse/iterate over a set of points, respectivelly, the domain of 00336 // 'img' and the window centered at p. 00337 00338 00339 // The domain of 'img' is obtained with "img.points()" and is 00340 // provided to the 'p' object so that it knows how to iterate. 00341 00342 // For a "basic" image, its set of points is an n-dimensional box. 00343 // In the 2D space, the box type is called 'box2d'. We also have 00344 // 'box1d' and 'box3d' for other dimensions. 00345 00346 box2d pts = img.points(); 00347 std::cout << "img points are " << pts << std::endl; 00348 // Prints: 00349 // img points are { (0, 0) .. (3, 4) } 00350 00351 // The type of iterators over a point set is obtained with the 00352 // expression: "name_of_the_point_set_type::piter", where 'piter' 00353 // means "point iterator" for short. 00354 00355 // The same construction is available for iterators on window 00356 // points, whose types are obtained in a similar way with 00357 // "name_of_the_window_type::qiter". Here the 'q' in 'qiter' 00358 // emphases the fact that a window is not really a set of points but 00359 // "a set of dpoints and a center point". 00360 00361 00362 00363 // The second version of our example contrasts with the more 00364 // "classical" ones; it is: 00365 00366 // - shorter, 00367 // so it is less error-prone for the developer; 00368 00369 // - easy to read, 00370 // so the algorithm appears clearly from the code; 00371 00372 // - (almost) "generic", 00373 // for instance, no details within the loops indicate that we are 00374 // processing a 2D image, that this image is rectangular, etc. 00375 00376 00377 // +-----------------------------------------------------------+ 00378 // | | 00379 // | A major feature of Olena is to offer to its users a way | 00380 // | to write GENERIC algorithms, that is, algorithms that | 00381 // | accept different image types as input. | 00382 // | | 00383 // +-----------------------------------------------------------+ 00384 00385 00386 // The next files of the tour give many details about what you can 00387 // expect from the notion of "genericity" applied to image 00388 // processing. 00389 00390 // Now you can jump to tour3.cc 00391 00392 00393 }