$extrastylesheet
One of Milena’s main features is its genericity. In order to understand how to take benefit of it, let’s see what genericity really means for us and how it is illustrated in the library.
A Generic algorithm is written once, without duplicates, and works on different kinds of input.
Let’s have a look to a small example. In any image processing library, we may be interested in a small routine to fill an image with data. A common implementation would look like this one:
See milena/doc/examples/tuto3/fill_non_generic.cc.
In this example, there are a lot of implicit assumptions about the input:
So, what would happen if we would like to use it for 3D images, use rgb8 as value or even work on a region of interest?
This implementation would require to be re-implemented and the user would have to deal with the various versions of the fill routine. For the developer, it is error prone, redundant and hard to maintain. For the user, it is confusing and forces to always think about what he is manipulating. According to our definition, this algorithm is clearly not generic.
This is not acceptable and that’s why Milena is developed considering genericity and user/developer friendliness.
With Milena, the previous example would be written as follow:
In this version, the routine can take any kind of image types as arguments. So it is for the values: the expected type depends on the value used in the given image. The for_all loop is also significantly generic to support any kind of images since the iterator guarantees it will pass through every sites.
This code is more generic and remains really close to the common description of the generic algorithm.
As a result, with this algorithm we can fill an image,...
... Or fill only a region of interest (a set of points).
Here is the full example:
The output image is declared here. Like any variable, it must be initialized at some point. To do so, initialize() is provided. It is a generic routine which can initialize the geometry of any image kind with another image of the same kind. After this call, output has a valid domain and is valid. It can be used in an algorithm, here data::fill, to have its values modified. Note that the value passed to data::fill is also generic. The library includes few generic common values from which any value type can convert to. literal::one is one of them. It is a generic one value which can convert to every value type in the library.
If Milena has been installed in a custom directory, e.g. not /usr/include or /usr/local/include, the path to the library headers must be passed to the compiler.
With g++ and MinGW, the option is -I<path> .
$ g++ -Ipath/to/mln my_program.cc
For other compilers, please look at the documentation and search for “include path”.
If you use specific input/output you may need to link your program with the right graphic library. For more information, please refer to section Input / Output in the Quick Reference Guide.
In some cases, one might want to use Olena in a more complex program compiled through several independant files.
Let’s consider the program is composed of two files both including Milena headers:
Content of main.cc
#include <mln/core/image/image2d.hh> // Main.cc // Forward declaration. void f(); int main() { mln::image2d<int> ima; }
Content of task.cc
#include <mln/core/image/image2d.hh> // task.cc void f() { mln::image2d<int> ima; }
The following call to the compiler will fail:
$ g++ -Ipath/to/mln main.cc task.cc /tmp/ccRaVKO2.o:(.data+0x0): multiple definition of `mln::trace::quiet' /tmp/ccAArtQl.o:(.data+0x0): first defined here /tmp/ccRaVKO2.o:(.bss+0x0): multiple definition of `mln::trace::tab' /tmp/ccAArtQl.o:(.bss+0x0): first defined here /tmp/ccRaVKO2.o:(.bss+0x4): multiple definition of `mln::trace::full_trace' /tmp/ccAArtQl.o:(.bss+0x4): first defined here /tmp/ccRaVKO2.o:(.bss+0x8): multiple definition of `mln::trace::internal::max_tab' [...] collect2: ld returned 1 exit status
This issue is due to the “header only” architecture of the library and to global variables. global variable symbols are compiled for each files and it leads to symbols conflicts.
The solution is to force the compiler to compile these symbols only once. Olena provided a mechanism to do this.
In one of the files including olena headers, at the very top of the file (before any includes), add the following line:
#undef MLN_WO_GLOBAL_VARS
For instance, we would have in main.cc:
#undef MLN_WO_GLOBAL_VARS #include <mln/core/image/image2d.hh> // Main.cc // Forward declaration. void f(); int main() { f(); }
The compile the sources again with the define:
$ g++ -DMLN_WO_GLOBAL_VARS -Ipath/to/mln main.cc task.cc
The previous duplicate symbols will then be compiled thanks to main.cc which force the effective declaration of the global symbols. Because of the default define at compile time, any .cc files including headers from the library will just declare but not define global symbols.
For that reason, sometimes one may also encounter the following error after defining MLN_WO_GLOBAL_VARS.
g++ -DMLN_WO_GLOBAL_VARS -I$PWD/milena/ main.cc task.cc /tmp/cc4Ys1xX.o: In function `f()': task.cc:(.text+0x6): undefined reference to `mln::border::thickness' collect2: ld returned 1 exit status
In our case, it would be because task.cc includes and uses global symbols which are not included in main.cc. Therefore, they are defined but never compiled. Here, the file main.cc should include the corresponding headers.
$ g++ -DNDEBUG -Ipath/to/mln my_program.cc
In this section you will find remarks about the compiler optimization flags and their impact on the compilation and execution time.
Currently, we have not tested different optimization flags with other compilers. If you did, please report us your results.
$ g++ -ggdb -Ipath/to/mln my_program.cc
Note that you MUST NOT compile with −DNDEBUG since assertions will be disabled. Once compiled, restart the program with GDB.
$ gdb ./my_program
In the GDB console, run it again.
(gdb) run <any parameter you may want to pass to the program>
When an assertion fails, in the GDB console simply type:
(gdb) bt
The full backtrace will be printed out and you will be able to find from where the error come from. The filenames, the line numbers and the parameters values are printed out in the backtrace as you can see in the following example:
#0 0xffffe410 in __kernel_vsyscall () #1 0xb7d00640 in raise () from /lib/i686/cmov/libc.so.6 #2 0xb7d02018 in abort () from /lib/i686/cmov/libc.so.6 #3 0xb7cf95be in __assert_fail () from /lib/i686/cmov/libc.so.6 #4 0x0804e094 in mln::image2d<bool>::has (this=0xbff32f34, p=../../../../doc/0xbff32f3c) at /lrde/stockholm/lazzara/svn/olena/git/oln/milena/mln/core/image/image2d.hh:442 #5 0x0804e6d7 in mln::image2d<bool>::operator() (this=0xbff32f34, p=../../../../doc/0xbff32f3c) at /lrde/stockholm/lazzara/svn/olena/git/oln/milena/mln/core/image/image2d.hh:460 #6 0x080490b0 in main () at test.cc:18
This trace allows to follow the stack trace at runtime. It also provides the time passed in each function call if the call last at least 10ms.
In order to enable traces in a program, set the related global variable to true:
... trace::quiet = true; ...
Since it’s a global variable, at anytime in the source code, the trace can be enabled/disabled.
Traces are enabled:
The previous code will produce the following trace:
labeling::blobs { core::initialize {} data::fill { data::fill_with_value { data::impl::fill_with_value_one_block { data::memset_ { data::impl::memset_ {} } data::memset_ } data::impl::fill_with_value_one_block } data::fill_with_value } data::fill } labeling::blobs - 0.08s
As you can see, labeling::blobs is located just after having set trace::quiet to true so its trace is part of the output. geom::bbox’s trace is not part of the output though since traces have been disabled just before it is called.
![]() | → |
![]() |