Routine name | Description |
duplicate() | creates a deep copy of an object. Any shared data is duplicated. |
data::fill() | fill an object with a value. |
data::paste() | paste object data to another object. |
labeling::blobs() | find and label the different components of an image. |
logical::not_() logical::not_inplace() | Point-wise "logical not" |
*::compute() | compute an accumulator on specific elements. |
image2d<char> imga(5, 5);
Memory has been allocated so data can be stored but site values have not been initialized yet. So we fill imga with the value ’a’:
data::fill(imga, 'a');
The fill() algorithm is located in the sub-namespace "mln::data" since this algorithm deals with the site values.
The full name of this routine is mln::data::fill(). To access to a particular algorithm, the proper file shall be included. The file names of algorithms strictly map their C++ name; so mln::data::fill is defined in the file mln/data/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 "read/write". The algorithm call shall be data::fill(ima.rw(), val). When forgetting the rw() call, it does not compile.
data::fill((imga | box2d(1,2)).rw(), 'a');
image2d<unsigned char> imgb(make::box2d(5,5, 7,8)); // Initialize imga with the same domain as imgb. image2d<unsigned char> imga(imgb.domain()); // Initialize the image values. data::fill(imgb, 'b'); // Paste the content of imgb in imga. data::paste(imgb, imga); debug::println(imga);
98 98 98 98 98 98 98 98 98 98 98 98
With this simple example we can see that images defined on different domains (or set of sites) can interoperate. The set of sites of an image is defined and can be accessed and printed. The following code:
image2d<int> ima1(5, 5); image2d<int> ima2(10, 10); std::cout << "ima1.domain() = " << ima1.domain() << std::endl; std::cout << "ima2.domain() = " << ima2.domain() << std::endl;
Gives:
image2d<int> ima1(5, 5); image2d<int> ima2(10, 10); std::cout << "ima1.domain() = " << ima1.domain() << std::endl; std::cout << "ima2.domain() = " << ima2.domain() << std::endl;
The notion of site 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 data::paste() tests that the set of sites of imgb (whose values are to be pasted) is a subset of the destination image.
labeling::blobs() is used to label an image. It returns a new image with the component id as value for each site. The background has 0 as id therefore the component ids start from 1.
Consider the following image:
bool vals[6][5] = {
{0, 1, 1, 0, 0},
{0, 1, 1, 0, 0},
{0, 0, 0, 0, 0},
{1, 1, 0, 1, 0},
{1, 0, 1, 1, 1},
{1, 0, 0, 0, 0}
};
image2d<bool> ima = make::image(vals);
Output:
Then label this image thanks to labeling::blobs():
Output:
Note that this routine returns the number of components in its third parameter. This parameter must be of the same type as the returned image value.
Header | mln/logical/not.hh |
Full namespace | mln::logical |
Routine(s) | not_(), not_inplace() |
This small routine only works on binary images. It performs a point-wise "logical not" and therefore "negates" the image. There are two versions of that algorithm: a version which returns a new image and another which works in place. Example:
Make a binary image:
bool vals[5][5] = {
{1, 0, 1, 0, 0},
{0, 1, 0, 1, 0},
{1, 0, 1, 0, 0},
{0, 1, 0, 1, 0},
{0, 1, 0, 1, 0}
};
image2d<bool> ima = make::image(vals);
Return the result in a new image:
image2d<bool> ima_neg = logical::not_(ima);
![]() |
![]() |
ima (left) and imaneg (right) after having called logical::not_(). |
Or, work in place:
logical::not_inplace(ima);
There are several flavour of the compute routine, depending on what the kind of elements it computes.
labeling::compute() | compute an accumulator for each component in a labeled image. |
data::compute() | compute an accumulator on the values of an image. |
Name | Description |
bbox | Bounding boxes |
count_adjacent_vertices | Count adjacent vertices |
count | Count the number of sites |
height | |
volume |
Name | Description |
histo | Histogram |
max | Max value |
max_h | Max value (Hexa) |
mean | Mean value |
median_alt | Median |
median_h | Median (Hexa) |
min | Min value |
min_h | Min value (Hexa) |
min_max | Min and Max value |
rank_bool | |
rank | |
rank_high_quant | |
sum | Sum the values |
Name | Description |
pair | Pair of accumulators |
tuple | n-uplets of accumulators |
Each accumulator can be used in *::compute(). It exists two versions of each accumulator.
Note that when an accumulator is passed to *::compute(), it must be instanciated.You cannot write:
data::compute(accu::meta::stat::max, ima);
data::compute(accu::meta::stat::max(), ima);
In this example we will try to retrieve the bounding box of each component in an image.
Consider the following image:
bool vals[6][5] = {
{0, 1, 1, 0, 0},
{0, 1, 1, 0, 0},
{0, 0, 0, 0, 0},
{1, 1, 0, 1, 0},
{1, 0, 1, 1, 1},
{1, 0, 0, 0, 0}
};
image2d<bool> ima = make::image(vals);
Then label this image thanks to labeling::blobs():
Output:Then, use labeling::compute() with the bbox accumulator:
util::array<box2d> boxes = labeling::compute(accu::meta::shape::bbox(), lbl, nlabels);
labeling::compute() holds an accumulator for each component, which means it returns an array of accumulator results. In this case, it returns an array of box2d.
Important note: since labeling::blobs() labels the component from 1 and set the background to 0, we will want to iterate from 1 to nlabels included.
for (unsigned i = 1; i <= nlabels; ++i) std::cout << boxes[i] << std::endl;
[(0,1)..(1,2)] [(3,0)..(5,1)] [(3,2)..(4,4)]
In order to make the code cleaner, small routines are available for the most used accumulators.
Currently there are the following routines:
Name | Description |
nsites | Return the number of sites of an image or a site set. |
mean | Return the mean of the values of an image. |
min_max | Return the min and max values of the values of an image. |
sum | Return the sum of the values of an image. |
These routines can be found in mln/geom and in mln/estim. For example, with geom::nsites() simply write:
unsigned nsites = geom::nsites(ima);
Sometimes it may be interesting to work only on some parts of the image or to extract only a sub set of that image. Olena enables that through the operator ’|’.
Three kinds of that operator exist:
Prototype | Comments |
Image | Sub Domain | Create a new image. |
Image | Function_p2b | Do not create a new image but create a morpher. |
Function_p2v | Sub Domain | Do not create a new image but create a morpher. |
A Sub Domain can be a site set, an image or any value returned by this
operator.
For a given site, Function_p2v returns a value and Function_p2b returns a
boolean. These functions are actually a sort of predicate. A common
Function_p2v is pw::value(Image). It returns the
point to value function used in the given image. C functions can also be used as
predicate by passing the function pointer.
You can easily get a Function_p2b by comparing the value returned by a Function_p2v to another Value. The following sample codes illustrate this feature.
In order to use C functions as predicate, they must have one of the following prototype if you work on 2D images:
//function_p2b bool my_function_p2b(mln::point2d p); //function_p2v //V is the value type used in the image. template <typename V> V my_function_p2v(mln::point2d p);
In this section, all along the examples, the image ima will refer to the following declaration:
bool vals[6][5] = {
{0, 1, 1, 0, 0},
{0, 1, 1, 0, 0},
{0, 0, 0, 0, 0},
{1, 1, 0, 1, 0},
{1, 0, 1, 1, 1},
{1, 0, 0, 0, 0}
};
image2d<bool> ima = make::image(vals);
p_array<point2d> arr; // We add two points in the array. arr.append(point2d(0, 1)); arr.append(point2d(4, 0)); // We restrict the image to the sites // contained in arr and fill these ones // with 0. // We must call "rw()" here. data::fill((ima | arr).rw(), 0); debug::println((ima | arr)); mln_VAR(ima2, ima | arr); // We do not need to call "rw()" here. data::fill(ima2, 0);
Output:
- - - -
First, find and label the components.
Output:Then, restrict the image to the sites being part of component 2.
mln_VAR(lbl_2, lbl | (pw::value(lbl) == pw::cst(2u)));
lbl_2 is a new image. lbl_2 looks like:
Finally, create a new color image, fill it with black and fill the sites part of component 2 with red.
image2d<rgb8> ima2; initialize(ima2, ima); data::fill(ima2, literal::black); data::fill((ima2 | lbl_2.domain()).rw(), literal::red);
The previous example can be written more quickly:
label_8 nlabels; image2d<label_8> lab = labeling::blobs(ima, c4(), nlabels); image2d<rgb8> ima2; initialize(ima2, ima); data::fill(ima2, literal::black); data::fill((ima2 | (pw::value(lab) == pw::cst(2u))).rw(), literal::red);
Here is the simple C function used as predicate:
bool row_oddity(mln::point2d p) { return p.row() % 2; }
Restrict the image with it:
image2d<rgb8> ima2; initialize(ima2, ima); data::fill(ima2, literal::black); data::fill((ima2 | row_oddity).rw(), literal::red);
Output:
When writing:
ima | sub_D
sub_D must be included in ima.domain().
Let’s have an image, imab, like this:
0 1 0 1 1 1
Extract a sub image from imab with sites having their value set to 1.
mln_VAR(imab1, ima | (pw::value(ima) == pw::cst(1u)));
1 1 1 1
Now, if we want to extract a sub image it may fail, depending on the site set used:
box2d b1(1,0, 1, 2); mln_VAR(imac, imab1 | b1); // Print: // 1 1 1 debug::prinln(imac); box2d b2(0,0, 1, 1); // Will fail at runtime. // ima.domain().has((0,0)) is false. mln_VAR(imad, imab1 | b2); debug::prinln(imad);
If you do not want this constraint, you may want to use an alternative operator:
ima / sub_D