DGtal 1.4.0
|
Part of the Topology package.
This part of the manual describes how to represent and process arbitrary cubical complexes.
The following programs are related to this documentation: cubical-complex-collapse.cpp, cubical-complex-illustrations.cpp, testCubicalComplex.cpp, cubicalComplexThinning.cpp.
We define a cubical complex C as a collection of cells living in some Khalimsky space. Two cells of C are incident if and only if they are incident in the Khalimsky space. A cubical complex provides many services related to set of (unsigned) cells living in a Khalimsky space: incidence operations, closure, star, link, cell set operations, cell set relations, collapse operation.
To create a cubical complex, we need to specify in which Khalimsky space it lives and also, optionally, the type of container used for storing cells. By default it is std::map
but boost::unordered_map
or std::unordered_map
is also possible.
Cells may be inserted through methods CubicalComplex::insertCell and CubicalComplex::insertCells. Cells are easily created with a model of CCellularGridSpaceND, for instance by specifying Khalimsky coordinates (see Cellular grid space and topology, unoriented and oriented cells, incidence). The small piece of code below creates a ring around a pixel.
Method CubicalComplex::insertCells accepts any range of iterators on cells as input, hence you may for instance directly create a cubical complex from a digital surface (see Digital surfaces).
You have also methods to remove some cells (CubicalComplex::eraseCell, CubicalComplex::eraseCells) and methods to check if a cell belongs to the complex (CubicalComplex::belongs).
std::unordered_map
or boost::unordered_map
instead of std::map
. It is sometimes slightly faster. In this case you need to define a hash function for cells. Either you provide one, or you just include the file KhalimskyCellHashFunctions.h with:Last, there is a data associated with each cell of a complex. The data type must either be CubicalCellData or a type that derives from CubicalCellData. This data is used by the functions::collapse operation, a function that is used to make a homotopic thinning of the cubical complex. It may also be used by the user for other purposes, like storing flags, coordinates or anything else associated to a cell. Look at the documentation of CubicalCellData to see the default stored flags and data.
2D cubical complex are displayable on a Board (see Board2D: a stream mechanism for displaying 2D digital objects). You just have to output the complex with the stream operator of a board. You may adjust the default style before. The following snippet creates and outputs a 2D complex.
Two cells of a cubical complex are incident if and only if they are incident in the Khalimsky space. From this relation, one can define faces, co-faces, as well as the boundary and co-boundary of a cell.
You have methods to get the faces, the co-faces, the direct faces or co-faces of a cell c, which are outputed with an output iterator:
There are also versions of these methods that return the iterators on these cells, if you need to access them directly in the complex afterwards.
If you wish to get a vector of cells that contains all the proper faces of some cell, then CubicalComplex::cellBoundary does the job. It is in general slightly faster than using CubicalComplex::faces.
If you wish to get a vector of cells that contains all the proper co-faces of some cell, then CubicalComplex::cellCoBoundary does the job. Again it is in general slightly faster than using CubicalComplex::coFaces.
You have three methods to compute these "subcomplexes" of a complex X. In each case, you must give a subcomplex S of X which defines the cells for which you wish to compute the closure, star or link.
CubicalComplex::closure: returns the closure of the cells of S within this complex, i.e. the smallest subcomplex of X that contains each cell in S.
CubicalComplex::star: returns the star of the cells of S within this complex, i.e. the union of the star (co-faces + cell) in X of every cell of S.
CubicalComplex::link: returns the link of the cells of S within this complex, i.e. the closure of the star of S in X minus the star of the closure of S in X.
You have three methods for computing the interior or the boundary of a complex. Note that the boundary and the interior of a complex have an empty intersection:
CubicalComplex::interior: returns the (topological) interior of this complex X, i.e. the cells whose star are homeomorphic to a ball of maximal dimension.
CubicalComplex::boundary: returns the (topological) boundary of this complex X, i.e. the closure of X minus the interior of X. It is worthy to note that the boundary complex may contain cells that are not in X.
A cubical complex can be seen as a generic container (it is a model of boost::Container). Remember though that it is not a model of boost::DefaultConstructible, because it must know a cellular grid space to be valid. You could thus use operations defined in DGtal::functions::setops (see Set operations on arbitrary containers) to perform set operations and relations on complexes. However, to use the specificity of the templated parameter CellContainer of CubicalComplex, it is better to use operations and relations specifically defined for CubicalComplex, for instance as follows:
You have the following operations:
And the following relations:
All these operations are at most linear in the sum of the number of cells of input complexes, but they are generally exponential in the dimension of complexes.
You may close a complex (CubicalComplex::close), i.e. adding cells such that the faces of every cell belongs to the complex. The complex is then said to be closed. The updated complex corresponds to the smallest closed complex containing it.
You may open a complex (CubicalComplex::open), i.e. removing cells such that the co-faces of every cell belongs to the complex. The complex is then said to be open. The updated complex corresponds to the greatest open complex contained in it.
A more difficult topological operation is the collapse (functions::collapse). A collapse transforms a complex by removing cells, but at the end the complex has the same homotopy type as the initial complex. The topology is preserved since only free pairs are removed. The following snippet shows how to collapse complex X, while keeping two cells fixed.
As you can see, when collapsing a complex X, you precise:
Example topology/cubical-complex-collapse.cpp shows that it works also in 3D (in fact, it is nD).
Thinning of digital objects in 2D and 3D with the guarantee that a final skeleton is thin can be achieved directly only in \(\mathbb{Z}^2\) for so-called well-composed images [80]. Nevertheless, the problem can be solved for 3D objects represented by cubical complexes [24].
In [24] Chaussard and Couprie proposed a parallel directional thinning schemes in cubical complexes which allow to obtain a thin result. The implementation in DGtal works for both 2D and 3D cubical complexes.
The first scheme, so-called ParDirCollapse—parallel directional collapse—based on the idea such that free pairs of faces are collapsed with respect to a fixed order of directions and order of face dimensions. In other words, in each iteration we first collapse free pairs of a given direction starting with pairs of the highest dimension. When there is no more free pairs to remove for a given direction then another direction is considered unless the specified number of iterations is not reached . Note in the case of ParDirCollapse the thinness of the result depends on the number of iterations.
The steps below present how to use ParDirCollapse in DGtal:
a) add those includes:
b) create an instance of the class ParDirCollapse and initialize it with an instance of the Khalimsky space:
c) attach a cubical complex \(X\) to be thinned:
d) run the algorithm (here two iterations):
The second scheme, so-called SurfaceCollapse is similar to ParDirCollapse. The only difference is that after each iteration of ParDirCollapse, faces of dimension \(dim(X) - 1\) which are not included in faces of higher dimension are marked as non-collapsible. The final output is guaranteed to be thin i.e. there is no faces of \(dim(X)\).
To run SurfaceCollapse we apply steps a–c for ParDirCollapse and then we apply the step below:
d) run the algorithm:
The last scheme, so-called IsthmusCollapse is similar to SurfaceCollapse. The only difference is that after each iteration of ParDirCollapse, we mark as non-collapsible those faces which are of dimension dim \((X) - 1\) and not included in faces of higher dimension. Moreover, all sub-faces of dimension \(dim(X) - 2\) of those faces have to be not free – are included in other faces of dimension \(dim(X) - 1\). The final output is guarantee to be thin i.e. there is no faces of \(dim(X)\).
To run SurfaceCollapse we apply steps a–c for ParDirCollapse and then we apply the step below:
d) run the algorithm:
For more details see the example topology/cubicalComplexThinning.cpp.