
https://svn.lrde.epita.fr/svn/oln/branches/cleanup-2008/milena Index: ChangeLog from Thierry Geraud <thierry.geraud@lrde.epita.fr> Add tour files from old projects. * doc/oldies: New. * doc/oldies/2007__tour.cc: New. * doc/oldies/2008__tour1.cc: New. * doc/oldies/2008__tour2.cc: New. * doc/oldies/2008__tour3.cc: New. * doc/oldies/2008__tour1_extra.cc: New. * doc/oldies/2006__tour_1.txt: New. * doc/oldies/2006__tour_2.txt: New. 2006__tour_1.txt | 671 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2006__tour_2.txt | 404 ++++++++++++++++++++++++++++++ 2007__tour.cc | 395 ++++++++++++++++++++++++++++++ 2008__tour1.cc | 322 ++++++++++++++++++++++++ 2008__tour1_extra.cc | 214 ++++++++++++++++ 2008__tour2.cc | 393 +++++++++++++++++++++++++++++ 2008__tour3.cc | 331 +++++++++++++++++++++++++ 7 files changed, 2730 insertions(+) Index: doc/oldies/2007__tour.cc --- doc/oldies/2007__tour.cc (revision 0) +++ doc/oldies/2007__tour.cc (revision 0) @@ -0,0 +1,395 @@ +/* This is a short tour of the basics of Olena. It isn't in any way + meant to cover all the features of Olena. Especially, some + important objects, such as the iterators, are NOT presented here. + */ + +// We will run some example with 1D, and 2D, so we include these two +// files. +#include <oln/basics1d.hh> +#include <oln/basics2d.hh> +//#include <oln/basics3d.hh> + +// Let's include all types, for simplicity. +#include <ntg/all.hh> + +// Some algorithms... +#include <oln/morpho/erosion.hh> +#include <oln/morpho/dilation.hh> +#include <oln/morpho/opening.hh> +#include <oln/morpho/closing.hh> +#include <oln/morpho/watershed.hh> + +#include <oln/utils/stat.hh> + +// Basic conversion functions. +#include <oln/convert/basics.hh> + +// Always include system headers after Olena. +#include <iostream> +#include <cmath> + + + +// Namespaces. +// =========== +// Olena is organized in a namespace hierarchy. Every thing is +// declared by Olena under the 'oln::' namespace, and possibly un a +// sub namespace such as 'oln::arith::' (arithmetic operations on +// images), 'oln::morpho::' (morphological operations), etc. +// For the sake of simplicity, we will neglect the 'oln::' prefix +// in this file. +using namespace oln; + +// Data types are defined in the Int�gre library, so they are +// in the ntg namespace. +// In this file, we will neglect the 'ntg::' prefix. +using namespace ntg; + +int +main (void) +{ + // Basic value types. + // ================== + // Olena ships with a set of basic value types such as + // int_u8, int_u16, int_u32, (common unsigned integer types) + // int_s8, int_s16, int_s32, (common signed integer types) + // float_s, float_d, (common float types) + // bin (binary type: false|true) + // + // These value are defined in the value/ subdirectory. Actually + // we defined all of them by including value/all.hh. + // + // You should use them instead of the standard C/C++ type, because + // Olena types are equipped with additional members required to + // write generic processings. + + // For instance the max() and min() class methods + // will return the maximum value of a type. + std::cout << "ntg_max_val(int_u8) = " + << ntg_max_val(int_u8) << std::endl; + + // You can combine these values with the standard operators + // as expected. + int_u8 a = 4; + int_u16 b = 1000; + std::cout << "a + b = " << a + b << std::endl; + + // Sometime it's hard to figure which type a variable has, because + // Olena seems to behave surprisingly at a first glance. + // There are two convenient functions that you can use to + // inspect a type. + // typename_of<T>() returns a std::string describing T + // typename_of_var(V) returns a std::string describing the type of V + std::cout << typename_of<int_u8>() + << " + " + << typename_of_var(b) + << " = " << typename_of_var(a + b) << std::endl; + // The expression above will print + // "int_u<8> + int_u<16> = int_u<17>" + // This probably seems surprising for two reasons: + // 1) int_u8 is printed as int_u<8> + // this is because int_u8 is really just a short hand for int_u<8>, + // Olena allows you to declare unsigned integer on 13 bits, if + // you want (int_u<13>); but int_u<8>, int_u<16>, and int_u<32> are + // more common so they have their own typedef. + // 2) (a + b) has type int_u<17>, not int_u<16> as one would expect. + // The point is that one needs 17 bits to hold the result + // of an addition between a 8bit and a 16bit value without + // overflowing. + + // Olena will also ensure that you don't such a large value to + // a variable with a smaller type: + // int_u16 c = a + b; + // would fail to compile because (a + b) has type int_u<17> which + // cannot fit in int_u<16>. + // If you *know* that the result of (a + b) fits in c, you + // should cast the value to int_u16 explicitly: + int_u16 c = a + b; + std::cout << "c = " << c << std::endl; + // Of course assigning a+b to a variable of type int_u<17> or + // greater is allowed and doesn't require a cast. + + + // Image types. + // ============ + // There are three kind of images supported for the moment. + // image1d, image2d, and image3d: the names are self-explanatory. + // All are template, parametrized by the kind of value associated + // to each point (we call 'point' a position in the image). + // + // We will start to use the 1D images, because they are + // easier to fill manually (i.e. there are less value + // to assign), and easy to display. + + // Here are two most basic way to build an image. + image1d<int_u8> img1a; // Build an empty image. + image1d<int_u8> img1b(20); // Build an image with 20 points. + + // The empty image hasn't any data. It's really useful + // only as a placeholder for the result of some processing, + // or another image. + + // Assigning an image to another one is not a costly operation. + // In fact, img1a behave like some pointers. After the following + // instruction + img1a = img1b; + // img1a and img1b can be used equivalently to access the + // same data (in other words, the point values are shared + // by img1a and img1b). + img1a(10) = 42; // Assign value 42 to the 11th point of img1a + std::cout << "img1b(10) =" + << img1b(10) << std::endl; // print 42, although we use img1b + + // We call these assignments 'shallow copies', because they don't copy the + // data. If you want to make a deep copy of an image, then use + // the clone() method, as in Java. + img1a = img1b.clone (); + img1a(10) = 13; + std::cout << "img1b(10) =" + << img1b(10) << std::endl; // still print 42 + + // As show above, assignments can be made using the parenthesis + // operator, which takes to coordinates of the point you want to + // assign a value to in argument. (In 1D there is only one coordinate.) + // Let's fill our image with a saw-teeth signal. + // (coord is the type used for coordinates) + for (coord col = 0; col < img1a.ncols(); ++col) + img1a(col) = col % 5; + + std::cout << "img1a:" << std::endl; + std::cout << img1a << std::endl; + // 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 4 + + // You can build image2d and image3d objects similarly, except + // you have to account for the extra coordinate. + image2d<bin> img2a(10, 10); + for (coord row = 0; row < img2a.nrows(); ++row) + for (coord col = 0; col < img2a.ncols(); ++col) + img2a(row, col) = (row >= 3 && col >= 3 && row < 7 && col < 7); + std::cout << "img2a:" << std::endl; + std::cout << img2a << std::endl; + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - | | | | - - - + // - - - | | | | - - - + // - - - | | | | - - - + // - - - | | | | - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + + // Point types + // =========== + // As said before, a point is a position in an image. Because + // Olena supports different dimension, it supports different + // types of points: point1d, point2d, point3d. + + // You build a point by passing it as much coordinate as + // needed. + point1d p1(10); + point2d p2(5, 3); + // You can address the value associated to a particular point + // in a image by using the [] operator: + std::cout << "img1b[p1]" << img1b[p1] << std::endl; // print 42 + + img2a[p2] = false; + std::cout << "img2a:" << std::endl; + std::cout << img2a << std::endl; + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - | | | | - - - + // - - - | | | | - - - + // - - - - | | | - - - + // - - - | | | | - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + + + // Delta Point types + // ================= + // You can't add points together, this make no sense. However + // you can use a delta-point to record a displacement, and add + // this delta-point to a point (yielding a new point). + // The types for delta-points are dpoint1d, dpoint2d, and dpoint3d. + dpoint1d dp1(2); + std::cout << "p1 + dp1 = " << p1 + dp1 << std::endl; + dpoint2d dp2(-1, 2); + img2a[p2 + dp2] = false; + std::cout << "img2a:" << std::endl; + std::cout << img2a << std::endl; + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - | | | | - - - + // - - - | | - | - - - + // - - - - | | | - - - + // - - - | | | | - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + + + // Windows + // ======= + // Windows are sets of delta-points. + // + // There are some predefined windows. + // For instance here are two common window2d objects: + std::cout << "win_c4p() = " << win_c4p() << std::endl; + // [(-1,0)(0,-1)(0,0)(0,1)(1,0)] + std::cout << "win_c4_only() = " << win_c4_only() << std::endl; + // [(-1,0)(0,-1)(0,1)(1,0)] + + // A window is commonly used to iterate around a fixed point. + // For instance + window2d w2 = win_c4_only(); + for (unsigned i = 0; i < w2.card(); ++i) + std::cout << "img2a[p2 + w2[" << i << "]] = " + << "img2a[" << p2 + w2.dp(i) << "] = " + << img2a[p2 + w2.dp(i)] << std::endl; + // img2a[p2 + w2[0]] = img2a[(4,3)] = | + // img2a[p2 + w2[1]] = img2a[(5,2)] = - + // img2a[p2 + w2[2]] = img2a[(5,4)] = | + // img2a[p2 + w2[3]] = img2a[(6,3)] = | + + // You can build you own window using by adding delta-points + // using the add() method. + window1d w1; + w1.add(-1).add(0).add(1); + std::cout << "w1 = " << w1 << std::endl; // [(-1)(0)(1)] + + // Neighborhoods + // ============= + // These objects are like Windows, except they have + // some additional properties (a point is not in its neighborhood, + // the neighbor of a point should have the point in its neighborhood). + // For this reason, they have types distinct from the windows: + // neighborhood1d, neighborhood2d, neighborhood3d. + // The interface is just the same as the windows. + neighborhood1d n1; + n1.add(1); + std::cout << "n1 = " << n1 << std::endl; // N[(-1)(1)] + + + // Processings. + // ============ + // We have seen how to build images and windows, that's + // enough to call some morphological operator. + // Windows are used as structural elements, so we can + // apply some basic morphological operator on img1a + // (the saw) using w1 as structural element: + std::cout << "erosion" << std::endl; + std::cout << morpho::erosion(img1a, w1) << std::endl; + // 0 0 1 2 0 0 0 1 2 0 0 0 1 2 0 0 0 1 2 3 + std::cout << "dilation" << std::endl; + std::cout << morpho::dilation(img1a, w1) << std::endl; + // 1 2 3 4 4 4 2 3 4 4 4 2 3 4 4 4 2 3 4 4 + std::cout << "opening" << std::endl; + std::cout << morpho::opening(img1a, w1) << std::endl; + // 0 1 2 2 2 0 1 2 2 2 0 1 2 2 2 0 1 2 3 3 + std::cout << "closing" << std::endl; + std::cout << morpho::closing(img1a, w1) << std::endl; + // 1 1 2 3 4 2 2 2 3 4 2 2 2 3 4 2 2 2 3 4 + + // Yet, remember that Olena is a *generic* image + // processing library, in which processing are meant + // to be written once but yet work on many kind of images. + + // Let's close the small hole we have added to img2d (in the + // paragraph about delta points). + std::cout << morpho::closing(img2a, win_c4p()) << std::endl; + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - | | | | - - - + // - - - | | | | - - - + // - - - - | | | - - - + // - - - | | | | - - - + // - - - - - - - - - - + // - - - - - - - - - - + // - - - - - - - - - - + + + // The watershed algorithm takes a neighborhood (not a window) as + // argument, and a type as template parameter. + + // The type parameter specifies the type of the output image value. + // Watershed are indicated with the max() of this type, other values + // are basin labels. + std::cout << "watershed" << std::endl; + std::cout << morpho::watershed_seg<int_u8>(img1a, n1) << std::endl; + // 4 4 4 4 255 3 3 3 3 255 2 2 2 2 255 1 1 1 1 1 + + + // Reading and writing PNM files. + // // You can read image2d from pnm files. The kind + // of pnm format supported (1,2,3,4,5,6) is dependent on + // the type of the image you load/save. The extension + // is also used to guess the format to use. + image2d<int_u8> lena = load(IMGDIR "/lena.pgm"); + if (!lena.has_impl()) + { + std::cerr << "Could not load " << IMGDIR "/lena.pgm" << std::endl; + exit(1); + } + std::cout << "watershedling lena..." << std::endl; + save(morpho::watershed_seg<int_u8>(lena, neighb_c4()), "output.pgm"); + + + // High order operators. + // // Olena offers a set of high order operators than can simplify + // the writing of simple algorithm. + // For instance `traverse(f, img)' will apply the function `f' + // to each point of `img'. Actually, `f' do not need to be a function, + // it just have to _behave_ like a function; it can be a functor, i.e., + // an instance of a class that implements an `operator()' (this allows + // `f' to maintain a state without using a global variable). + + // The header `utils/stat.hh' define some functor to compute + // statistical data. In the following example we instantiate + // an `f_moments' functor, have it traverse the saw-teeth image, + // and compute some moments from the data gathered during the traversal. + utils::f_moments<int_u8> f; + traverse(f, img1a); + std::cout << "f.mean() = " << f.mean() << std::endl; + std::cout << "f.variance() = " << f.variance() << std::endl; + + // `apply' is an high order operator similar to `traverse', but + // it builds an image from the results of the function application. + // + // o = apply(f, i); + // + // is a short for + // + // for all point p in i: o[p] = f(i[p]) + // + // The type of the output image may be different from the type + // of the input image, so `apply' need a little more help from + // `f' than `traverse' does. Basically, `f' should define + // a type member named `result_type' so that `apply' knows which + // type of output image it has to create. + // + // (NOTE: The `result_type' presence is part of the requirement of + // the `Adaptable Unary Function' concept in the STL. So if you + // have a model of `Adaptable Unary Function', you can safely pass + // it to `apply'.) + // + // Some conversion functions (those for which the output type is + // knonw) can be used this way. For instance the + // `convert::bound<T>' functor will convert any value to type `T' + // and saturate for out-of-bound values. See how the `4' are + // converted to `3' when the saw-teeth image is converted to an + // image2d<int_u<2> > image. + std::cout << apply(convert::bound<int_u<2> >(), img1a) << std::endl; + // 0 1 2 3 3 0 1 2 3 3 0 1 2 3 3 0 1 2 3 3 + + // Another usefull high order operator is `fold': + // - `fold(f, val, img)' computes `f(...f(f(val,i_0),i_1)...,i_n)', + // where i_0...i_n are the value associated to each image point. + // - `fold(f, img)' computes `f(...f(f(i_0,i_1),i_2)...,i_n)'. + // In both cases, `f' must be a model of the + // `Adaptable Binary Function' STL concept. +} Index: doc/oldies/2008__tour1.cc --- doc/oldies/2008__tour1.cc (revision 0) +++ doc/oldies/2008__tour1.cc (revision 0) @@ -0,0 +1,322 @@ +// Copyright (C) 2001, 2007 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +// File: tour1.cc. + +#include <oln/core/1d/image1d.hh> + +#include <oln/arith/plus.hh> // arith + +#include <oln/level/fill.hh> // level +#include <oln/level/paste.hh> + +#include <oln/debug/println.hh> // debug + + +int main() +{ + // Olena is organized in a namespace hierarchy. Everything is + // declared by Olena within the 'oln::' namespace, and possibly a + // sub-namespace such as 'oln::arith::' (arithmetic operations on + // images), 'oln::morpho::' (morphological operations), etc. For + // the sake of simplicity, we will neglect the 'oln::' prefix in + // this file. + + using namespace oln; + + // There are three kinds of image dimensions supported for the + // moment: 1D, 2D, and 3D. The respective basic image types are + // respectively image1d, image2d, and image3d. All are template'd, + // parametrized by the kind of value associated to each point. + // + // We will start to use the 1D images, because there are less values + // to assign (!) and because they are easy to display. Although we + // will be in 1D, we will talk about "points" and "pixels", just + // like in the classical 2D world. + + // Here are the two most basic ways to build an image: + + image1d<int> img1a; // which builds an empty image; + image1d<int> img1b(20); // which builds an image with 20 pixels. + + // The type of values enclosed in those images is 'int' for + // integers. + + // The empty image has no data and its definition domain is still + // unset. We do not know yet the number of pixels it contains. + // However, it is really useful to have such an "empty image" + // because it is a placeholder for the result of some processing, or + // another image. + + // Trying to access the pixel value from an empty image leads to an + // error at run-time. + + // Assigning an image to another one is NOT a costly operation. + // Actually img1a behaves like some mathematical variable. Put + // differently it is just a name to designate an image. After the + // following instruction + + img1a = img1b; + + // Both variables img1a and img1b can be used equivalently to access + // the same data. Just like in maths we can state that "img1a IS + // img2b" and we only have ONE actual image in memory. The image + // definition---its domain and its pixel values--is shared by img1a + // AND img1b. + + + // Since an image is a function that maps values to points, to read + // or modify a pixel value, we just the same notation than the one + // of functions: + + img1a.at(0) = 7; // Assign value 7 to the 1st pixel of img1a. + img1a.at(10) = 51; // Assign value 51 to the 11th pixel of img1a. + + std::cout << "img1b.at(10) = " << img1b.at(10) + << std::endl; // Print 51 since img1b is img1a. + + // As you can see, Olena provides a unique easy syntax to access an + // image value, whatever we want to read this value or to modify it: + // "ima(p)", where p is a variable that represents a point of the 1D + // domain. In "val = ima(p)" we read the pixel value, whereas in + // "ima(p) = new_val" we write in an image. For 1D images, a 1D + // point behaves just like an index so the user can write "ima(i)" + // with i being an integer. + + + // A single image variable can be used to designate one image and + // then another one. + + image1d<int> img2a(20); // An image (not empty). + image1d<int> img2b(20); // Another one (not empty). + image1d<int> img; // A variable. + + img = img2a; // img is img2a + img.at(5) = 19; // so img2a at index 5 is set to 19. + + img = img2b; // img is NOW img2b + img.at(5) = 69; // so img2b at index 5 is set to 69. + + std::cout << (100 * img2a.at(5) + img2b.at(5)) // Gives 1969. + << std::endl; + + + + // In the Olena library, all image types behave like image1d: + // + // +-----------------------------------------------------------+ + // | | + // | - an "empty" image actually is a mathematical variable | + // | | + // | => just think in a mathemetical way when dealing | + // | with images; | + // | | + // | | + // | - no dynamic memory allocation/deallocation is required | + // | | + // | the user never has to use "new / delete" (the C++ | + // | equivalent for the C "malloc / free") so she does | + // | not have to manipulate pointers or to directly | + // | access memory | + // | | + // | => Olena prevents the user from making mistakes; | + // | | + // | | + // | - image data/values can be shared between several | + // | variables and the memory used for image data is | + // | handled by the library | + // | | + // | => memory management is automatic. | + // | | + // | | + // +-----------------------------------------------------------+ + + + + + image1d<char> img3a(0, 19); // These 1D image data go from the + // index 0 to the index 19; it thus has + // 20 points. + + // Memory has been allocated so data can be stored but pixel values + // have not been initialized yet. So we fill img3a, that is, all + // pixels of img3a, with the value 'a': + level::fill(inplace(img3a), 'a'); + + // The "fill" algorithm is located in the sub-namespace "level" + // since this algorithm deals with the "level" of pixel values. + + // Note that the term "level" refers to the fact that an + // image can be considered as a landscape where the + // elevation at a particular location/point is given by the + // corresponding pixel value. + + // The full name of this routine is "oln::level::fill". To access + // to a particular algorithm, the proper file shall be included. + // The file names of algorithms strictly map their C++ name; so + // oln::level::fill is defined in the file "oln/level/fill.hh". + + // Most algorithms in Olena are constructed following the classical + // scheme: "output algo(input)", where the input image is only read. + // However some few algorithms take an input image in order to + // modify it. To enforce this particular feature, the user shall + // explicitly state that the image is provided so that its data is + // modified "inplace". + + // The algorithm call shall be "level::fill(inplace(ima), val)". + // When forgetting the "inplace(..)" statement it does not compile. + + + // We then define below a second image to play with. As you can see + // this image has data for the indices 5 to 14 (so it has 10 + // points). The definition domain of a 1D image can start from + // any index, even a negative one. + image1d<char> img3b(5, 14); + + // We initialize the image values. + level::fill(inplace(img3b), 'b'); + + // Last we now paste the contents of img3b in img3a... + level::paste(img3b, inplace(img3a)); + + // ...and print the result. + debug::println(img3a); + + // Before pasting, the couple of images looked like: + // + // index = 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 + // + // img3a = a a a a a a a a a a a a a a a a a a a a + // img3b = b b b b b b b b b b + // + // so after pasting we get: + // img3a = a a a a a b b b b b b b b b b a a a a a + // + // Just remember that a point is an absolute location on the + // discrete domain. For the 1D case, an index is thus NOT an + // offset. When we write "imgb(5)", we designate the first pixel of + // imgb since this image starts at index 5. + + + // With this simple example we can see that images defined on + // different domains (or set of points) can interoperate. The set + // of points an image is defined on can be accessed and printed. + // The following code: + + std::cout << "img3a.points() = " << img3a.points() << std::endl + << "img3b.points() = " << img3b.points() << std::endl; + + // gives: + // img3a.points() = { (0) .. (19) } + // img3b.points() = { (5) .. (14) } + + // The notion of point sets plays an important role in Olena. Many + // tests are performed at run-time to ensure that the program is + // correct. + + // For instance, the algorithm level::paste tests that the set of + // points of img3b (whose values are to be pasted) is a subset of + // the destination image. We can reproduce this same test: + + std::cout << (img3b.points() <= img3a.points() ? + "true" : + "false") << std::endl; // which gives 'true'. + + // Accessing the value of an image at a given point also performs a + // test to make sure that this point belongs to the image domain + // (and consequently that data access in memory is valid). For + // instance, calling "img3a(9)" actually runs a code equivalent to + // this test: + + point1d p(9); + std::cout << (img3a.points().has(p) ? + "true" : + "false") << std::endl; // which gives 'true'. + + // The type of 1D image points is "point1d". The 1D image type + // features the method "operator()(point1d p)"; when we write + // "img3a(9)" it is just a shortcut for "img3a( point1d(9) )". + + // When having a point we can access its index following: + std::cout << p.ind() << std::endl; // which gives 9. + + // To know if a point belongs to an image domain or not, we can run + // this shorter test: + std::cout << (img3a.has(p) ? + "true" : + "false") << std::endl; // which gives 'true'. + + + // Since the notion of point is independent from the image it + // applies on, we can form expressions where p is used on several + // images: + + img3a(p) = 'M', img3b(p) = 'W'; // At index 9, both values change. + debug::println(img3a); + debug::println(img3b); + // we obtain: + // a a a a a b b b b M b b b b b a a a a a + // b b b b W b b b b b + + + // To modify all the values of an image we can iterate over its + // domain with an index i: + + for (int i = 5; i <= 14; ++i) + img3b.at(i) = 'a' + i - 5; + debug::println(img3b); + // a b c d e f g h i j + + // There is a safer way to iterate that prevents from providing a + // wrong value for the minimum or maximum index value: + for (int i = img3b.min_ind(); i <= img3b.max_ind(); ++i) + img3b.at(i) = 'b' + i - img3b.min_ind(); + debug::println(img3b); + // b c d e f g h i j k + + + // The Olena library was built with the following rationale: + // + // +-----------------------------------------------------------+ + // | | + // | If you do not want to be "generic", if you are used to | + // | the classical way of browsing pixel values, you can! | + // | | + // +-----------------------------------------------------------+ + + + // Indeed, the loops above depict the "classical" way to iterate + // over the contents of a 1D image. The next tour file introduces + // the solution provided by Olena to write better loops... + + + // Jump to tour1_extra.cc if you want some more information about + // image data management. + + // Jump to tour2.cc to continue the tour. +} Index: doc/oldies/2008__tour2.cc --- doc/oldies/2008__tour2.cc (revision 0) +++ doc/oldies/2008__tour2.cc (revision 0) @@ -0,0 +1,393 @@ +// Copyright (C) 2001, 2007 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +// File: tour2.cc. + +#include <oln/core/2d/image2d.hh> +#include <oln/core/2d/window2d.hh> +#include <oln/debug/println.hh> + + + +// Note to the reader: If you do not have read the tour1.cc file, you +// should have a quick look at it before proceeding with this present +// file. Some important features and practices are described in the +// former tour step that are here assumed to be known. + + + +int main() +{ + using namespace oln; + + // As shown before, image data read access and pixel value + // assignments can be performed using: + // - either the parenthesis operator with takes a point as its + // argument, + // - or the ".at" method which requires the coordinates of the point + // you want to access to. + + // ex: ima(p), with p a 1D point, or ima.at(i), ima being a 1D + // image and i an index---single coordinate. + + + // Objects from image2d and image3d types can be build and access to + // in a similar way than image1d, except you have to account for + // respectively one and two extra coordinates. Let us take an + // example. + + image2d<bool> img(4, 5); // A 4x5 2D binary image. + + for (unsigned row = 0; row < img.nrows(); ++row) + for (unsigned col = 0; col < img.ncols(); ++col) + img.at(row, col) = + (row > 0 and row < 3) and (col > 0 and col < 4); + + debug::println(img); // Gives: + // - - - - - + // - | | | - + // - | | | - + // - - - - - + + // When debug::print'ing binary images, the 'true' (object) and + // 'false' (background) values are respectively depicted by the '|' + // and '-' symbols. + + + + + // As said before, a point is a position in an image. Because + // Olena supports different dimensions, it supports different + // point types: point1d, point2d, and point3d. + + // We build a point by passing it as much coordinates as + // needed: + point2d p(2, 0); // a 2D point so a couple of coordinates. + + // Each point coordinate can be accessed separately... + std::cout << "row = " << p.row() << std::endl + << "col = " << p.col() << std::endl; + + // ...and modified: + p.col() = 1; + std::cout << "col = " << p.col() << " (new value!)" << std::endl; + + // So we have a way to access image data: + img(p) = false; + // which is shorter than the equivalent writing: + // ima2a.at(2, 1) = false; + + debug::println(img); + // Gives (with hand-written coordinates): + // col 0 1 2 3 4 + // row v + // 0 - - - - - + // 1 - | | | - + // 2 > - - | | - + // 3 - - - - - + + // Points in Olena are not really like mathematical vectors. The + // reason is twofold. Some operations over vectors are meaningless + // with points; for instance adding points together makes no sense. + // Furthermore we want C++ expressions with points to be more + // strongly typed so that errors from the user can be more easily + // pointed out. + + // To record a displacement from one point to another one, Olena + // introduces the notion of "delta-points". The types for + // delta-points are dpoint1d, dpoint2d, and dpoint3d. We have: + + dpoint2d dp(-1, +2); // -1 applies to row, +2 applies to column. + img(p + dp) = false; + debug::println(img); + // Gives: + // col 0 1 2 3 4 + // row . . v + // 0 - - - - - + // 1 > - | | - - + // 2 . - - | | - + // 3 - - - - - + + // Let us verify: + point2d p2 = p + dp; + std::cout << "p2 " << p2 + << " = p " << p + << " + dp " << dp << std::endl; + assert(p2 == point2d(1, 3)); // Right: p2 is (1, 3). + + // Taking the difference between a couple of points gives a + // delta-point. + dpoint2d dp2 = p2 - p; // That should be equal to 'dp'. + assert(dp2 == dp); // Indeed. + + // Delta-points (amongst them) feature the classical arithmetical + // operators: + dpoint2d dp3 = dp + dp2; + dp3 -= dp2; + assert(dp3 == dp); + + + // The notion of delta-point is at the base of the definitions of + // classical window and neighborhood. + + + // A window, or "sliding window", is a region defined around a + // point. In its most usual forms offered by Olena, for instance + // window2d for the 2D case, windows are internally defined by a set + // of delta-points. + + window2d win; // Empty window. + win + .take(dpoint2d(-1, -1)) // Add a delta-point. + .take(dpoint2d( 0, 0)) + .take(dpoint2d( 1, 1)) + .take(dpoint2d( 1, 2)); + std::cout << "win = " << win << std::endl; + // win = [ (-1, -1), (0, 0), (1, 1), (1, 2) ] + + // A window is commonly used to iterate around a fixed point. + std::cout << "around " << p + << " window points are "; + for (unsigned i = 0; i < win.size(); ++i) + std::cout << (p + win[i]) // win[i] gives the ith element of the + << ' '; // window, i.e., a delta-point. + std::cout << std::endl; + // The code above outputs: + // around (2, 1) window points are (1, 0) (2, 1) (3, 2) (3, 3) + + + // If we try to generalize this kind of display to all image points, + // one straightforward code is the following: + + { + // First version. + + std::cout << std::endl + << "First version:" << std::endl + << std::endl; + + for (unsigned row = 0; row < img.nrows(); ++row) + for (unsigned col = 0; col < img.ncols(); ++col) + { + point2d p(row, col); + std::cout << p << ": "; + for (unsigned i = 0; i < win.size(); ++i) + { + point2d q = p + win[i]; // q is a point around p; + // precisely, the ith point of + // the window win centered at + // point p + if (img.has(q)) // we only print q if it actually lies + // within the image + std::cout << q << ' '; + } + std::cout << std::endl; + } + + } // End of 1st version. + + // We obtain: + // (0, 0): (0, 0) (1, 1) (1, 2) + // (0, 1): (0, 1) (1, 2) (1, 3) + // (0, 2): (0, 2) (1, 3) (1, 4) + // (0, 3): (0, 3) (1, 4) + // (0, 4): (0, 4) + // (1, 0): (1, 0) (2, 1) (2, 2) + // (1, 1): (0, 0) (1, 1) (2, 2) (2, 3) + // (1, 2): (0, 1) (1, 2) (2, 3) (2, 4) + // (1, 3): (0, 2) (1, 3) (2, 4) + // (1, 4): (0, 3) (1, 4) + // (2, 0): (2, 0) (3, 1) (3, 2) + // (2, 1): (1, 0) (2, 1) (3, 2) (3, 3) + // (2, 2): (1, 1) (2, 2) (3, 3) (3, 4) + // (2, 3): (1, 2) (2, 3) (3, 4) + // (2, 4): (1, 3) (2, 4) + // (3, 0): (3, 0) + // (3, 1): (2, 0) (3, 1) + // (3, 2): (2, 1) (3, 2) + // (3, 3): (2, 2) (3, 3) + // (3, 4): (2, 3) (3, 4) + + + // An equivalent code, given here just for you to realize that you + // may continue to handle images and points with Olena just the way + // you are used to it: + + /* + + { // A variation. + + int nrows = img.nrows(), ncols = img.ncols(); + unsigned n = win.size(); + for (int row = 0; row < nrows; ++row) + for (int col = 0; col < ncols; ++col) + { + std::cout << "(" << row << ", " << col << "): "; // print p + for (unsigned i = 0; i < n; ++i) + { + int // define q coordinates: + r = row + win[i].row(), + c = col + win[i].col(); + if (r >= 0 and r < nrows and c >= 0 and c < ncols) // q is in img + std::cout << "(" << r << ", " << c << ") "; // print q + } + std::cout << std::endl; + } + + } // End of a variation. + + */ + + + // Such samples of "classical" image processing code have 3 (three!) + // main drawbacks: + + // - it is error-prone; note that there is rather a lot of code for + // a so simple algorithm; + + // - the algorithm, that is, the most important part of this code, is + // totally drowned in the middle of implementation details; + + // - this kind of writing only applies to a very special type of + // images (2D ones, rectangular, and starting at (0,0)) so it is + // not reusable. + + + // If we express the algorithm into natural language, we can say: + // + // p, a point of img + // q, a point of win centered at p + // + // for all p + // print p + // for all q + // if q is in img + // print q + // print end of line + // end for + // end for + + + // The Olena library has been designed so that you can easily + // translate your algorithms into code. With the running example we + // can write: + + { // Second version. + + std::cout << std::endl + << "Second version (same result):" << std::endl + << std::endl; + + box2d::piter p (img.points()); + window2d::qiter q (win, p); + + for_all(p) + { + std::cout << p << ": "; + for_all(q) + if (img.has(q)) + std::cout << q << ' '; + std::cout << std::endl; + } + + } // End of 2nd version. + + std::cout << std::endl; + + + // Above, p and q behave just like points; for instance, the + // following expressions are valid: + + // int r = p.row(); + // to get the current row value, + + // bool b = img(p); + // to get the pixel value at the current point, + + // or point2d pp = p + dp; + // where dp is a delta-point to get a point nearby p. + + // Yet, p and q are "more than points" since they allow to + // browse/iterate over a set of points, respectivelly, the domain of + // 'img' and the window centered at p. + + + // The domain of 'img' is obtained with "img.points()" and is + // provided to the 'p' object so that it knows how to iterate. + + // For a "basic" image, its set of points is an n-dimensional box. + // In the 2D space, the box type is called 'box2d'. We also have + // 'box1d' and 'box3d' for other dimensions. + + box2d pts = img.points(); + std::cout << "img points are " << pts << std::endl; + // Prints: + // img points are { (0, 0) .. (3, 4) } + + // The type of iterators over a point set is obtained with the + // expression: "name_of_the_point_set_type::piter", where 'piter' + // means "point iterator" for short. + + // The same construction is available for iterators on window + // points, whose types are obtained in a similar way with + // "name_of_the_window_type::qiter". Here the 'q' in 'qiter' + // emphases the fact that a window is not really a set of points but + // "a set of dpoints and a center point". + + + + // The second version of our example contrasts with the more + // "classical" ones; it is: + + // - shorter, + // so it is less error-prone for the developer; + + // - easy to read, + // so the algorithm appears clearly from the code; + + // - (almost) "generic", + // for instance, no details within the loops indicate that we are + // processing a 2D image, that this image is rectangular, etc. + + + // +-----------------------------------------------------------+ + // | | + // | A major feature of Olena is to offer to its users a way | + // | to write GENERIC algorithms, that is, algorithms that | + // | accept different image types as input. | + // | | + // +-----------------------------------------------------------+ + + + // The next files of the tour give many details about what you can + // expect from the notion of "genericity" applied to image + // processing. + + // Now you can jump to tour3.cc + + +} Index: doc/oldies/2008__tour3.cc --- doc/oldies/2008__tour3.cc (revision 0) +++ doc/oldies/2008__tour3.cc (revision 0) @@ -0,0 +1,331 @@ +// Copyright (C) 2001, 2007 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +// File: tour2.cc. + +#include <oln/core/2d/image2d.hh> +#include <oln/core/2d/neighb2d.hh> + +#include <oln/core/gen/such_as.hh> +#include <oln/core/gen/torus_image.hh> +#include <oln/core/gen/pw_value.hh> +#include <oln/core/gen/fun_ops.hh> + +#include <oln/level/fill.hh> +#include <oln/debug/fill.hh> +#include <oln/debug/println.hh> + + +// Note to the reader: you should have read the files tour1.cc and +// tour2.cc before starting with this present file. + + + +// We have encapsulated an algorithm into a procedure which is forward +// declared below so that it can be used in the section 'main'. +template <typename I> void algo(const I& img); + + +// Some functions that will be useful in the following: +bool chessboard(oln::point2d p) +{ + return (p.row() + p.col()) % 2; +} + + + +int main() +{ + using namespace oln; + + // First our domain is 2d box: + box2d b(point2d(0, 0), point2d(2, 2)); + // ^^^^ ^^^^ + // from to + + // We define a binary image with values on that box. + image2d<bool> img(b); + + // With an array of Booleans (1 means true, 0 means false)... + bool vals[] = { 1, 0, 0, + 0, 1, 0, + 0, 1, 1 }; + // ...the debug::fill routine allows for manually initializing + // image data: + debug::fill(inplace(img), vals); + std::cout << "img = " << std::endl; + debug::println(img); + // img = + // | - - + // - | - + // - | | + + image2d<int> ima(b); // An image of integers with the same + box2d::piter p(ima.points()); // domain as img... + int i = 0; + for_all(p) + ima(p) = i++; // ...and manually filled with values. + + std::cout << "ima = " << std::endl; + debug::println(ima); + // ima = + // 0 1 2 + // 3 4 5 + // 6 7 8 + + + + // The algorithm defined at the end of this file is very close to + // the one of the tour former file. The major difference is that it + // does not rely on a window but on a neighborhood. + + // In image processing, we usually say that "an image has a given + // neighborhood" or that "we associate/embed a neighborhood to/into + // an image". In Olena, that is really the case: the image can + // "have" a neighborhood, meaning that a neighborhood can be added + // to an image in order to obtain an "image with a neighborhood". + + // Joining an image with a neighborhood is performed with the + // operator '+': + algo(ima + c4); // c4 is the 2D neighborhood corresponding to + // 4-connectivity; such as many classical + // neighborhoods it is provided by Olena. + // The result is given below. + + // ---input: + // 0 1 2 + // 1 2 3 + // 2 3 4 + // ---output: + // 0: 1 1 + // 1: 0 2 2 + // 2: 1 3 + // 1: 0 2 2 + // 2: 1 1 3 3 + // 3: 2 2 4 + // 2: 1 3 + // 3: 2 2 4 + // 4: 3 3 + + // That was expectable... + + + // And now for a little test: what is the result of this code? + { + image2d<int> test(ima.points()); + level::fill(inplace(test), ima); + (test + c4).at(1, 1) = 9; + debug::println(test); + } + // and can you tell why? + // The answers are given in the file tour3-test.txt + + + // Now let us start experimenting the genericity of Olena! + + + // First, imagine that you want to restrict the domain of ima to a + // subset of points, a region or whatever. For instance, the + // chessboard function takes a point as argument and returns a + // Boolean so it is like a predicate. We can want to consider only + // the points of ima "such as" this predicate is verified. The + // "such as" mathematical symbol is '|' so let's rock: + + algo((ima | chessboard) + c8); + // gives: + + // ---input: + // 1 + // 3 5 + // 7 + // ---output: + // 1: 3 5 + // 3: 1 7 + // 5: 1 7 + // 7: 3 5 + + // where the blanks in printing the input image denote that the + // corresponding points do NOT belong to the image domain. + + // Another similar example is based on the binary image created at + // the beginning of this tour: + algo((ima | img) + c8); + // which gives: + + // ---input: + // 0 + // 4 + // 7 8 + // ---output: + // 0: 4 + // 4: 0 7 8 + // 7: 4 8 + // 8: 4 7 + + + + // Second, imagine that you want your initial image to get the + // geodesy of a torus, that is, a 2D image wrapped on a torus. + // Points located at the image boundary have neighbors; for + // instance, the point denoted by the 'x' cross below has for + // 4-connectivity neighbors: t, l, r, and b (respectively for top, + // left, right, and bottom): + + // b o o o + // o o o o + // t o o o + // x r o l + + // Let us try: + algo(torus(ima) + c8); + // gives: + + // ---input: + // 0 1 2 + // 3 4 5 + // 6 7 8 + // ---output: + // 0: 8 6 7 2 1 5 3 4 + // 1: 6 7 8 0 2 3 4 5 + // 2: 7 8 6 1 0 4 5 3 + // 3: 2 0 1 5 4 8 6 7 + // 4: 0 1 2 3 5 6 7 8 + // 5: 1 2 0 4 3 7 8 6 + // 6: 5 3 4 8 7 2 0 1 + // 7: 3 4 5 6 8 0 1 2 + // 8: 4 5 3 7 6 1 2 0 + + + + // We can have both the torus geodesy and a sub-domain: + + algo(torus(ima | chessboard) + c8); + algo(torus(ima | img) + c8); + + // which respectively give: + + // ---input: + // 1 + // 3 5 + // 7 + // ---output: + // 1: 7 3 5 + // 3: 1 5 7 + // 5: 1 3 7 + // 7: 3 5 1 + + // and: + + // ---input: + // 0 + // 4 + // 7 8 + // ---output: + // 0: 8 7 4 + // 4: 0 7 8 + // 7: 4 8 0 + // 8: 4 7 0 + + + + + // Last, the way a predicate is defined can also rely on some image + // values. For that the user can on the fly provide an expression + // built with the "pw_value" facility, where "pw_" means + // "point-wise" for short: + + algo((ima | (pw_value(ima) < 4)) + c4); + + // In this example, "pw_value(ima)" is the function that represents + // the point-wise value of the 'ima' image, that is, the function + // "p -> ima(p)". This naturally leads to: + + // ---input: + // 0 1 2 + // 3 + // + // ---output: + // 0: 1 3 + // 1: 0 2 + // 2: 1 + // 3: 0 + + + + // From those examples, you should realize that: + + + // +-----------------------------------------------------------+ + // | | + // | The level of "genericity" provided by Olena is rather | + // | high; it means: | + // | | + // | - taking the image dimension you work on; | + // | | + // | - having the type of pixel values you need; | + // | | + // | - choosing the neighborhood you want; | + // | | + // | - changing the geodesy if you need it; | + // | | + // | - being able to restrict the image domain; | + // | | + // | - and many other features that are addressed further | + // | in the tour... | + // | | + // +-----------------------------------------------------------+ + +} + + + + + +// The algorithm 'algo': + +template <typename I> +void algo(const I& img) +{ + std::cout << "---input:" << std::endl; + oln::debug::print(img); + std::cout << "---output:" << std::endl; + + oln_piter(I) p(img.points()); // p iterates on img points + oln_niter(I) n(img, p); // n iterates in img on neighbors of p + + for_all(p) + { + std::cout << oln::debug::format(img(p)) + << ':'; + for_all(n) + std::cout << ' ' + << oln::debug::format(img(n)); + std::cout << std::endl; + } + + std::cout << std::endl; +} Index: doc/oldies/2008__tour1_extra.cc --- doc/oldies/2008__tour1_extra.cc (revision 0) +++ doc/oldies/2008__tour1_extra.cc (revision 0) @@ -0,0 +1,214 @@ +// Copyright (C) 2007 EPITA Research and Development Laboratory +// +// This file is part of the Olena Library. This library is free +// software; you can redistribute it and/or modify it under the terms +// of the GNU General Public License version 2 as published by the +// Free Software Foundation. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING. If not, write to +// the Free Software Foundation, 51 Franklin Street, Fifth Floor, +// Boston, MA 02111-1307, USA. +// +// As a special exception, you may use this file as part of a free +// software library without restriction. Specifically, if other files +// instantiate templates or use macros or inline functions from this +// file, or you compile this file and link it with other files to +// produce an executable, this file does not by itself cause the +// resulting executable to be covered by the GNU General Public +// License. This exception does not however invalidate any other +// reasons why the executable file might be covered by the GNU General +// Public License. + +// File: tour1_extra.cc. + +#include <oln/core/1d/image1d.hh> +#include <oln/arith/plus.hh> +#include <oln/debug/println.hh> + + + +int main() +{ + using namespace oln; + + + // Let us take a few examples to illustrate how images and data are + // managed. In these examples, we will use the notion of "block of + // instructions" from the C/C++ languages: "{" ... "}". + + + + + // Example 1: + + { // A new block starts here. + + image1d<float> local_ima(1000); + // A non-empty image is created; a memory buffer is automatically + // allocated to store image data. Some code using 'local_ima' can + // be inserted here... + + } // The block ends and 'local_ima' is not more accessible. + + // At the end of the block, since the user cannot use 'local_ima' + // anymore, the memory is automatically deallocated. + + + + + // Example 2: + + image1d<float> ima; // An empty image---a variable. + + { // A new block. + + image1d<float> local_ima(1000); // A non-empty image. + // Some work is performed with 'local_ima' here. + // ... + ima = local_ima; // Last, we state that ima IS local_ima. + + } // End of the block: 'local_ima' is no more accessible. + + assert(not ima.is_empty()); // We test that 'ima' is no more empty. + + // Since we state that "ima IS local_ima" and since 'ima' is still + // accessible by the user, the contents of 'local_ima' is preserved. + + // Conversely to the example 1, the memory allocated for 'local_ima' + // is thus NOT deallocated: the data of 'local_ima' is "transfered" + // to 'ima'. There is NO data "copy" involved during this transfer + // so it is fast and it saves memory. + + + + + // Example 3: + + { + image1d<int> orig(5); // An original image. + for (int i = 0; i < 5; ++i) + orig.at(i) = i; + debug::println(orig); + // 0 1 2 3 4 + + image1d<int> ima = orig; // 'ima' is 'orig'. + ima = ima + ima; // Point-wise sum of ima and itself. + debug::println(ima); + // 0 2 4 6 8 + + debug::println(orig); + // 0 1 2 3 4 + } + + // To explain this result, let us detail the point-wise sum line. + // The assignment "ima = ima + ima" is a two-step process: first + // "ima + ima" is computed, then the assignment is performed. This + // line can be rewritten as: + + // image1d<int> anon = ima + ima; // line (a) + // ima = anon; // line (b) + + // where 'anon' is the "anonymous" result of the sum. We can see + // that 'ima' is used in line (a) to compute a new image, namely + // 'anon'. At that precise line, 'ima' still designates 'orig'. At + // line (b) the definition of 'ima' changes: 'ima' was designating + // 'orig', now it designates 'anon', that is, the sum result. + // Eventually the data of 'orig' have never changed since it has + // been initialized. + + // In this example, two images with effective data have been + // created: the sum result, which is accessible through 'ima', and + // 'orig'. + + // The same explanation in pictures. When the sum "ima + ima" is + // computed, the images and their data look like: + // + // +-----------+ + // orig --> | 0 1 2 3 4 | + // +-----------+ + // ^ + // ima ______| + // + // +-----------+ + // anon --> | 0 2 4 6 8 | + // +-----------+ + // + // Then the assignment modifies this scheme into: + // + // +-----------+ + // orig --> | 0 1 2 3 4 | + // +-----------+ + // ima ______ + // | + // v + // +-----------+ + // anon --> | 0 2 4 6 8 | + // +-----------+ + // + // and the temporary 'anon' disappears. + + + + + + // Example 4: + + { + image1d<int> ima(5); + for (int i = 0; i < 5; ++i) + ima.at(i) = i; + debug::println(ima); + // 0 1 2 3 4 + + ima = ima + ima; + debug::println(ima); + // 0 2 4 6 8 + } + + // Let us re-write the assignment line: + + // image1d<int> anon = ima + ima; // line (a) + // ima = anon; // line (b) + + // A new image, 'anon', is created to store the sum result; this + // image is just like a temporary object since it is anonymous. In + // line (a) the definition of 'ima' changes: it was the original + // image with its data being "0 1 2 3 4" and it now designates the + // sum result. The original data becomes inaccessible by the user + // so it is automatically deallocated. + + // The same explanation In pictures. After the sum is computed, we + // have: + // +-----------+ + // ima ---> | 0 1 2 3 4 | + // +-----------+ + // + // +-----------+ + // anon --> | 0 2 4 6 8 | + // +-----------+ + // + // and the assignment of 'ima' leads to: + // + // +-----------+ + // ima | 0 1 2 3 4 | + // | +-----------+ + // |_________ + // | + // v + // +-----------+ + // anon --> | 0 2 4 6 8 | + // +-----------+ + // + // so the original data, unreachable, are deallocated and the + // temporary 'anon' disappears; we end up with: + // + // +-----------+ + // ima ---> | 0 2 4 6 8 | + // +-----------+ +} Index: doc/oldies/2006__tour_1.txt --- doc/oldies/2006__tour_1.txt (revision 0) +++ doc/oldies/2006__tour_1.txt (revision 0) @@ -0,0 +1,671 @@ + -*- outline -*- + + +* primary image types + + +** image2d<T> + +*** definition + +rectangular 2D image with a virtual border + +*** parameter + +T: value type + +*** related types + +image1d<T> and image3d<T> are similarly constructed + +*** sample code + +**** step 1 + +char[] values = { 'a', 'b', 'c', + 'd', 'e', 'f' }; +image2d<char> ima(2, 3); +level::fill(ima, values); +debug::print(ima.bbox()); + +// [(0, 0)..(1, 2)] + +debug::print(ima); + +// 0 1 2 +// - - - +// 0| a b c +// 1| d e f + +ima.set_name("ima"); +point2d p(0, 1); + +std::cout << ima(p) << std::endl; +// b + +debug::print(ima, p); +// ima.at(0, 1) == b + +ima.at(0, 1) = 'B'; +debug::print(ima, p); +// ima.at(0, 1) == B + + +bbox2d bb = set(from(1,5), to(2,7)); +image2d<char> ima2(bb); +ima2.set_name("ima2"); +level::fill(ima2, values); +debug::print(ima2); + +// 5 6 7 +// - - - +// 1| a b c +// 2| d e f + +**** step 2 + +ima2.set_border_thickness(1); +border::fill(ima2, '.'); + +debug::print(ima2.bbox()); +// [(1, 5)..(2, 7)] + +debug::print(ima2); +// 5 6 7 +// - - - +// 1| a b c +// 2| d e f + +debug::print(ima2.large_bbox()); +// [(0, 4)..(3, 8)] + +debug::print_large(ima2); +// 4 5 6 7 8 +// - - - - - +// 0| . . . . . +// 1| . a b c . +// 2| . d e f . +// 3| . . . . . + +ima2.at( 0, 6) = 'x'; // OK +ima2.at(-1, 6) = '?'; // error: out of bounds + +debug::print_large(ima2); +// 4 5 6 7 8 +// - - - - - +// 0| . . x . . +// 1| . a b c . +// 2| . d e f . +// 3| . . . . . + +debug::print(ima2); +// 5 6 7 +// - - - +// 1| a b c +// 2| d e f + +std::cout << ima2.npoints() << std::endl; +// 6 + +std::cout << ima2.bbox().npoints() << std::endl; +// 6 + +std::cout << ima2.large_bbox().npoints() << std::endl; +// 20 + +std::cout << ima2.n_value_cells() << std::endl; +// 20 + +**** step 3 + +a virtual border (at the outer boundary of the domain) is provided so +that there is no need for special code to handle pixels at the limit +of the domain. + +int[] values = { 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0 }; +image2d<bool> ima(from(1,1), to(4,4)); +level::fill(ima, values); +debug::print(ima); +// 1 2 3 4 5 +// - - - - - +// 1| 0 1 1 1 0 +// 2| 0 1 1 1 0 +// 3| 0 1 1 1 0 +// 4| 0 0 0 0 0 + +image2d<bool> ima2 = morpho::e_erosion(ima + c4); +debug::print(ima2); +// 1 2 3 4 5 +// - - - - - +// 1| 0 0 1 0 0 +// 2| 0 0 1 0 0 +// 3| 0 0 0 0 0 +// 4| 0 0 0 0 0 + +actually, after level::fill we have: + +// 0 1 2 3 4 5 6 +// - - - - - - - +// 0| ? ? ? ? ? ? ? +// 1| ? 0 1 1 1 0 ? +// 2| ? 0 1 1 1 0 ? +// 3| ? 0 1 1 1 0 ? +// 4| ? 0 0 0 0 0 ? +// 5| ? ? ? ? ? ? ? + +in morpho::e_erosion, the border is initialized: +// 0 1 2 3 4 5 6 +// - - - - - - - +// 0| 1 1 1 1 1 1 1 +// 1| 1 0 1 1 1 0 1 +// 2| 1 0 1 1 1 0 1 +// 3| 1 0 1 1 1 0 1 +// 4| 1 0 0 0 0 0 1 +// 5| 1 1 1 1 1 1 1 + +so we get the proper result: +// 0 1 2 3 4 5 6 +// - - - - - - - +// 0| 1 1 1 1 1 1 1 +// 1| 1 0 0 1 0 0 1 +// 2| 1 0 0 1 0 0 1 +// 3| 1 0 0 0 0 0 1 +// 4| 1 0 0 0 0 0 1 +// 5| 1 1 1 1 1 1 1 + + + + +** image_2dhex<T> + +*** definition + +image defined on a 2D triangular grid (hexagonal pixels) +it is equipped with a virtual border + +*** parameter + +T: value type + +*** sample code + +FIXME + + + +** igraph<T> + +FIXME: ... + + + +** image2d_without_border<T> + +*** definition + +same type as image2d<T> but without border + +*** parameter + +T: value type + +*** sample code + +**** step 1 + +image2d<char> ima = set(from(0,0), to(0,0)); +ima.at( 0, 0) = 'a'; // OK +ima.at(-1, 0) = 'a': // error + +std::cout << ima.npoints() << std::endl; +// 1 +std::cout << ima.n_value_cells() << std::endl; +// 1 + +**** step 2 + +int[] values = { 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0 }; +image2d_without_border<bool> ima(from(1,1), to(4,4)); +level::fill(ima, values); +debug::print(ima); +// 1 2 3 4 5 +// - - - - - +// 1| 0 1 1 1 0 +// 2| 0 1 1 1 0 +// 3| 0 1 1 1 0 +// 4| 0 0 0 0 0 + +image2d<bool> ima2 = morpho::e_erosion(ima + c4); +debug::print(ima2); +// 1 2 3 4 5 +// - - - - - +// 1| 0 0 0 0 0 +// 2| 0 0 1 0 0 +// 3| 0 0 0 0 0 +// 4| 0 0 0 0 0 + + + +** one_value_image<S, T> + +*** definition + +image defined by a point-set and a (single) value +this image has the same value for all pixels + +*** parameters + +S: point set type +T: value type + +*** sample code + +bbox2d bb = set(from(1,4), to(2,6)); +one_value_image<bbox2d, char> ima(bb, 'a'); +debug::print(ima); + +// 4 5 6 +// - - - +// 1| a a a +// 2| a a a + +ima.value() = 'b'; +debug::print(ima); + +// 4 5 6 +// - - - +// 1| b b b +// 2| b b b + +std::cout << ima.npoints() << std::endl; +// 6 + +std::cout << ima.nvalues() << std::endl; +// 1 + +std::cout << ima.n_value_cells() << std::endl; +// 1 + +ima.change_value('b', 'c'); +// 4 5 6 +// - - - +// 1| c c c +// 2| c c c + +std::cout << ima.at(1, 5) << std::endl; +// c + +ima.at(1, 5) = 'd'; // error + +ima.change_value('C', ' '); // error at run-time + // 'C' is not a value for this image + + + +** image_p2v<S, F> + +*** definition + +image defined by a point set and a function "point -> value" + +*** parameters + +S: point set type +F: function type + +*** sample code + +**** step 1 + +bool chess(point2d p) +{ + return (p.row() + p.col()) % 2; +} + +bbox2d bb(from(0,0), to(3,3)); +image2d<bool> ima = image(bb, chess); +debug::print(ima); +// 0 1 2 3 +// - - - - +// 0| 0 1 0 1 +// 1| 1 0 1 0 +// 2| 0 1 0 1 +// 3| 1 0 1 0 + +or: + +image_p2v<bbox2d, bool(*)(point2d)> ima(bb, chess); + +std::cout << ima.at(2, 3) << std::endl; // OK: image readable +// 1 + +bool val; +ima.at(2, 3) = val; // error: image not writable + +**** step 2 + +Likewise but with a function-object: + +struct chess_t +{ + bool operator()(point2d p) const + { + return (p.row() + p.col()) % 2; + } +}; + +bbox2d bb(from(0,0), to(3,3)); +image_p2v<bbox2d, chess_t> ima(bb); +debug::print(ima); +// 0 1 2 3 +// - - - - +// 0| 0 1 0 1 +// 1| 1 0 1 0 +// 2| 0 1 0 1 +// 3| 1 0 1 0 + +**** step 3 + +struct chess_t : oln::abstract::function_p2v<chess_t> +{ + bool value(point2d p) const + { + return (p.row() + p.col()) % 2; + } +}; +static chess_t chess; + +// ... + +debug::options opt; +opt.true_symbol('*').false_symbol(' '); +debug::print( chess(from(0,0), to(3,3)), opt ); +// 0 1 2 3 +// - - - - +// 0| * * +// 1| * * +// 2| * * +// 3| * * + + + +** rle_image<P, T> + +*** definition + +image with run-length encoding + +*** parameters + +P: point type +T: value type + +*** sample code + +**** step 1 + +rle_image<point2d, int> ima; +ima. + set_name("ima"). + insert(from(2,1), len(3), val(6)). + insert(from(1,2), len(2), val(8)); + +debug::print(ima); +// { ([(1, 2)..(1, 3)], 8), ([(2, 1)..(2, 3)], 6) } + +std::cout << ima.n_value_cells() << std::endl; +// 2 + +debug::options opt; +opt.no_symbol('x'); +debug::print( imagine(ima), opt ); +// 1 2 3 +// - - - +// 1| x 8 8 +// 2| 6 6 6 + +std::cout << ima.npoints() << std::endl; +// 5 + +image2d<int> ima2(from(1,1), to(2,3), val(4)); +debug::print(ima2); +// 1 2 3 +// - - - +// 1| 4 4 4 +// 2| 4 4 4 + +level::assign(ima2, ima); +debug::print(ima2); +// 1 2 3 +// - - - +// 1| 4 8 8 +// 2| 6 6 6 + +**** step 2 + +std::cout << ima.nrls() << std::endl; +// 2 + +debug::print(ima.rl(0)); +// ([(1, 2)..(1, 3)], 8) + +debug::print(ima.rl(1)); +// ([(2, 1)..(2, 3)], 6) + +std::cout << ima.rl(1).first_p() << std::endl; +// (2, 1) + +std::cout << ima.rl(1).last_p() << std::endl; +// (2, 3) + +std::cout << ima.rl(1).value() << std::endl; +// 6 + +std::cout << ima.rl(1).npoints() << std::endl; +// 3 + +FIXME: +std::cout << ima.rl(1).point(0) << ' ' + << ima.rl(1).point(1) << ' ' + << ima.rl_point(1, 2) << std::endl; +// (2, 1) (2, 2) (2, 3) + +**** step 3 + +// remember that for ima, in the rl #1, the point #1 is (2, 2) + +point2d p(2, 2); +std::cout << ima(p) << std::endl; // KO: does not compile + +std::cout << ima.rl_point(1, 1) << std::endl; +// (2, 2) + +rle_psite<point2d> p_(1, 1); +std::cout << ima(p_) << std::endl; // OK +// 6 + +debug::print(ima2); +// 1 2 3 +// - - - +// 1| 4 8 8 +// 2| 6 6 6 +rle_image<point2d, int> ima3 = rlencode(ima2); +debug::print(ima3); +// { ([(1, 1)..(1, 1)], 4), ([(1, 2)..(1, 3)], 8), ([(2, 1)..(2, 3)], 6) } +level::in_place::inc(ima3); + + +debug::print(ima3); +// { ([(1, 1)..(1, 1)], 5), ([(1, 2)..(1, 3)], 9), ([(2, 1)..(2, 3)], 7) } + +debug::print( imagine(ima3) ); +// 1 2 3 +// - - - +// 1| 5 9 9 +// 2| 7 7 7 + + +debug::print(ima); +// { ([(1, 2)..(1, 3)], 8), ([(2, 1)..(2, 3)], 6) } + +debug::print( imagine(ima), opt ); +// 1 2 3 +// - - - +// 1| x 8 8 +// 2| 6 6 6 + +std::cout << ima.rl_point(1, 1) << std::endl; +// (2, 2) + + +rl_psite<point2d> p3(ima3, 1, 1); // p3 is not bound to ima3 but to its topology + +std::cout << ima3.rl_point(1, 1) << std::endl; +// (1, 3) + +std::cout << ima3(p3) << std::endl; // OK +// 9 + +std::cout << ima(p3) << std::endl; // error at run-time + +// explanation: +// 1) p3, when created, clearly designates the 2D point (1, 3) through +// the couple (rl #1, pt #1) +// 2) when writing "ima(p3)" we want to access the value of "ima" at point (1, 3) +// 3) "ima" knows that the couple (rl #1, pt #1) does not designate the point (1, 3) +// but (2, 2) instead +// 4) this error is then identified at run-time + + +ima3.remove_rl(0); + +debug::print(ima3); +// { ([(1, 2)..(1, 3)], 9), ([(2, 1)..(2, 3)], 7) } + +debug::print( imagine(ima3), opt ); +// 1 2 3 +// - - - +// 1| x 9 9 +// 2| 7 7 7 + +rl_psite<point2d> p3_(ima3, 1, 1); +std::cout << ima3.rl_point(1, 1) << std::endl; // now same as ima.rl_point(1, 1) +// (2, 2) + +std::cout << ima(p3_) << std::endl; // now OK +// 7 + + + + +** sparse_image<P, T> + +*** definition + +FIXME: HERE + + +*** parameter + +P: point type +T: value type + +*** main features + +not point-wise accessible +not value-wise accessible + +*** data storage + +std::vector of couples (p_start, std::vector<T>) + + +** image_vec<P, T> + +*** parameters + +P: point type +T: value type + +*** main features + +not point-wise accessible +not value-wise accessible + +*** data storage + +std::vector< std::pair<P, T> > + + +** image_map<P, T> + +*** parameters + +P: point type +T: value type + +*** main features + +not point-wise accessible +not value-wise accessible + +*** data storage + +using std::map<P, T> + +*** related type + +slow::image_map<P, T> + + +** slow::image_bbmap<P, T> + +*** parameters + +P: point type +T: value type + +*** main features + +point-wise accessible and mutable +not value-wise accessible +bounding-boxed + +*** data storage + +some values are set in a std::map<P, T> +when not set, a default value is provided by an attribute + +*** related type + +image_map<P, T> + + + +** Morpher types + +** FIXME + +do (really!) nothing when writing at p when outside the image domain +error when reading at p when outside the image domain + +** FIXME + +add a default value when p is outside the image domain + +** sequence<I> + +FIXME: ... + + + +Local Variables: +mode: outline +ispell-local-dictionary: "american" +End: Index: doc/oldies/2006__tour_2.txt --- doc/oldies/2006__tour_2.txt (revision 0) +++ doc/oldies/2006__tour_2.txt (revision 0) @@ -0,0 +1,404 @@ + -*- outline -*- + +* primary image types + +** image2d<T> + +*** parameter + +T: value type + +*** main features + +- debase type for a rectangular 2D image + +- a virtual border (at the outer boundary of the domain) is provided + so that there is no need for special code to handle pixels at the + limit of the domain. + +*** sample code + +int[] values = { 'a', 'b', 'c', + 'd', 'e', 'f' }; +image2d<char> ima(2, 3); +level::fill(ima, values); +debug::print(ima.bbox()); + +// [(0, 0)..(1, 2)] + +debug::print(ima); + +// 0 1 2 +// - - - +// 0| a b c +// 1| d e f + +ima.set_name("ima"); +point2d p(0, 1); + +std::cout << ima(p) << std::endl; +// b + +debug::print(ima, p); +// ima.at(0, 1) == b + +ima.at(0, 1) = 'B'; +debug::print(ima, p); +// ima.at(0, 1) == B + + + +bbox2d bb = set(from(1,5), to(2,7)); +image2d<char> ima2(bb); +ima2.set_name("ima2"); +level::fill(ima2, values); +debug::print(ima2); + +// 5 6 7 +// - - - +// 1| a b c +// 2| d e f + + +ima2.border() = 1; +border::fill(ima2, '.'); + +debug::print(ima2.bbox()); +// [(1, 5)..(2, 7)] + +debug::print(ima2); +// 5 6 7 +// - - - +// 1| a b c +// 2| d e f + +debug::print(ima2.large_bbox()); +// [(0, 4)..(3, 8)] + +debug::print_large(ima2); +// 4 5 6 7 8 +// - - - - - +// 0| . . . . . +// 1| . a b c . +// 2| . d e f . +// 3| . . . . . + +ima2.at( 0, 6) = 'x'; // OK +ima2.at(-1, 6) = '?'; // error: out of bounds + +debug::print_large(ima2); +// 4 5 6 7 8 +// - - - - - +// 0| . . x . . +// 1| . a b c . +// 2| . d e f . +// 3| . . . . . + +debug::print(ima2); +// 5 6 7 +// - - - +// 1| a b c +// 2| d e f + +std::cout << ima2.npoints() << std::endl; +// 6 + +std::cout << ima2.bbox().npoints() << std::endl; +// 6 + +std::cout << ima2.large_bbox().npoints() << std::endl; +// 20 + +std::cout << ima2.n_memory_cells() << std::endl; +// 20 + + +FIXME: play with point2d and dpoint2d... + + +*** related types + +image1d<T> and image3d<T> are similarly constructed + + +*** level 2 + +*** main features + +has a virtual border (same thickness for every axis) +point-wise accessible and mutable +not value-wise accessible + +*** data storage + +relies on array2d<T> + +*** to-do + +should rely on array2d< T::storage > +add unambiguous ctors + +*** ideas + +point2d p(0, 1); +point2d p = set(row(0), col(1)); + +bbox2d bb = set(from(1,5), to(2,7)); +bbox2d bb = set(minrow(1), maxrow(2), mincol(5), maxcol(7)); +bbox2d bb = set(minrow(1), mincol(5), nrows(2), ncols(3)); +bbox2d bb = set(pmin(1,5), nrows(2), ncols(3)); +bbox2d bb = set(pmin(1,5), size(2,3)); + + +*** image_2dhex<T> + +on a 2D triangular grid (hexagonal pixels) +FIXME: ... + + +*** igraph<T> + +FIXME: ... + + +** image2d_without_border<T> + +*** level 1 + +*** main features + +same type as image2d<T> but without border + +*** sample code + +image2d<char> ima = set(from(0,0), to(0,0)); +ima.at( 0, 0) = 'a'; // OK +ima.at(-1, 0) = 'a': // error + +std::cout << ima.npoints() << std::endl; +// 1 +std::cout << ima.n_memory_cells() << std::endl; +// 1 + +** level 2 + +*** status + +to-do + +*** main features + +same as image2d<T> but without virtual border + +*** data storage + +relies on array2d<T> + + + +** one_value_image<S, T> + +*** level 1 + +*** main features + +image defined by a point-set and a (single) value +this image has the same value for all pixels + +*** sample code + +bbox2d bb = set(from(1,4), to(2,6)); +one_value_image<bbox2d, char> ima(bb, 'a'); +debug::print(ima); + +// 4 5 6 +// - - - +// 1| a a a +// 2| a a a + +ima.value() = 'b'; +debug::print(ima); + +// 4 5 6 +// - - - +// 1| b b b +// 2| b b b + +std::cout << ima.npoints() << std::endl; +// 6 + +std::cout << ima.n_memory_cells() << std::endl; +// 1 + +ima.change_value('b', 'c'); +// 4 5 6 +// - - - +// 1| c c c +// 2| c c c + +std::cout << ima.at(1, 5) << std::endl; +// c + +ima.at(1, 5) = 'd'; // error + + +*** level 2 + +*** parameters + +S: point set type +T: type of the value + +*** main features + +point-wise accessible but not mutable +value-wise accessible and mutable + +*** data storage + +a single attribute: the unique value + + +** image_p2v<S, F> + +*** parameters + +S: point set type +F: function point-site -> value + +*** main features + +point-wise accessible but not mutable +not value-wise accessible + +*** data storage + +combines the point set and the function to produce values +on the fly + + +** rle_image<P, T> + +*** parameter + +P: point type +T: value type + +*** main features + +not point-wise accessible +not value-wise accessible + +*** data storage + +std::vector of triplets (p_start, length, value) + +*** related type + +sparse_image<T> + + +** sparse_image<P, T> + +*** parameter + +P: point type +T: value type + +*** main features + +not point-wise accessible +not value-wise accessible + +*** data storage + +std::vector of couples (p_start, std::vector<T>) + +*** related type + +rle_image<P, T> + + +** image_vec<P, T> + +*** parameters + +P: point type +T: value type + +*** main features + +not point-wise accessible +not value-wise accessible + +*** data storage + +std::vector< std::pair<P, T> > + + +** image_map<P, T> + +*** parameters + +P: point type +T: value type + +*** main features + +not point-wise accessible +not value-wise accessible + +*** data storage + +using std::map<P, T> + +*** related type + +slow::image_map<P, T> + + +** slow::image_bbmap<P, T> + +*** parameters + +P: point type +T: value type + +*** main features + +point-wise accessible and mutable +not value-wise accessible +bounding-boxed + +*** data storage + +some values are set in a std::map<P, T> +when not set, a default value is provided by an attribute + +*** related type + +image_map<P, T> + + + +** Morpher types + +** FIXME + +do (really!) nothing when writing at p when outside the image domain +error when reading at p when outside the image domain + +** FIXME + +add a default value when p is outside the image domain + +** sequence<I> + +FIXME: ... + + + +Local Variables: +mode: outline +ispell-local-dictionary: "american" +End: