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