Basic routines

Routine nameDescription
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.





Fill

First, create an image:
  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.

Note

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');




Paste

We then define below a second image to play with. As you can see this image has data for the sites (5, 5) to (14, 14) (so it has 100 sites).

  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);
Output:
98 98 98 98 
98 98 98 98 
98 98 98 98 

Note

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.




Blobs

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:

labeling-compute-1.png

Then label this image thanks to labeling::blobs():

  label_8 nlabels;
  image2d<label_8> lbl = labeling::blobs(ima, c4(), nlabels);

Output:

labeling-compute-2.png

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.




Logical not

Headermln/logical/not.hh
Full namespacemln::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);

logical-not-1.png

Return the result in a new image:

  image2d<bool> ima_neg = logical::not_(ima);

logical-not-1.png
logical-not-2.png
ima (left) and imaneg (right) after having called logical::not_().

Or, work in place:

  logical::not_inplace(ima);
Then, ima looks like:

logical-not-3.png




Compute

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.

Accumulators

An accumulator is a special object accumulating data while iterating all over the image values or sites. Hereby follows a list of accumulators available in Olena.

Accumulators on sites

NameDescription
bboxBounding boxes
count_adjacent_verticesCount adjacent vertices
countCount the number of sites
height 
volume 

Accumulators on values

NameDescription
histoHistogram
maxMax value
max_hMax value (Hexa)
meanMean value
median_altMedian
median_hMedian (Hexa)
minMin value
min_hMin value (Hexa)
min_maxMin and Max value
rank_bool 
rank 
rank_high_quant 
sumSum the values

Special accumulators

NameDescription
pairPair of accumulators
tuplen-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);
Instead, you must write:
  data::compute(accu::meta::stat::max(), ima);

Example with labeling::compute()

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():

  label_8 nlabels;
  image2d<label_8> lbl = labeling::blobs(ima, c4(), nlabels);
Output:
labeling-compute-2.png

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;
Output:
[(0,1)..(1,2)]
[(3,0)..(5,1)]
[(3,2)..(4,4)]

0.0.1  Routines based on accumulators and *::compute()

In order to make the code cleaner, small routines are available for the most used accumulators.

Currently there are the following routines:

NameDescription
nsitesReturn the number of sites of an image or a site set.
meanReturn the mean of the values of an image.
min_maxReturn the min and max values of the values of an image.
sumReturn 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);




Working with parts of an image

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:

PrototypeComments
Image | Sub DomainCreate a new image.
Image | Function_p2bDo not create a new image but create a morpher.
Function_p2v | Sub DomainDo 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);
Of course, you just need to change the point type if you use another image type. For instance, you would use point3d with 3D images. The returned value type V for Function_p2v depends on the image value type. With image2d<int>, V would be int.

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);
Output:

fill-subdomain-1.png

Restrict an image with a site set

A simple example is to fill only a part of an image with a specific value:
  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:

  - 
    
    
    
-   

  - 
    
    
    
-   

Restrict an image with a predicate

In the following example, we aim at extracting a component from an image and create a new image with it.

First, find and label the components.

  label_8 nlabels;
  image2d<label_8> lbl = labeling::blobs(ima, c4(), nlabels);
Output:

fill-subdomain-2.png

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:

fill-subdomain-3.png

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);
Output:

fill-subdomain-4.png

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);

Restrict an image with a C function

In this example, the image is restricted to its odd lines. A new image is created in which odd lines are in red and others in black.

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:

fill-imageif-cfun-1.png


Important note

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)));
Then, imab1 looks like:
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