DGtal  1.5.beta
Digital convexity and full digital convexity
Author(s) of this documentation:
Jacques-Olivier Lachaud

Part of the Geometry package.

This part of the manual describes tools associated to a new definition of digital convexity, called the full convexity [77] [78] . This new definition solves many problems related to the usual definition of digital convexity, like possible non connectedness or non simple connectedness, while encompassing its desirable features. Fully convex sets are digitally convex, but are also connected and simply connected. They have a morphological characterisation, which induces a simple convexity test algorithm. As an important example, arithmetic planes are fully convex too.

The following programs are related to this documentation: geometry/curves/exampleDigitalConvexity.cpp, geometry/curves/exampleRationalConvexity.cpp, testBoundedLatticePolytope.cpp, testBoundedLatticePolytopeCounter.cpp, testCellGeometry.cpp, testDigitalConvexity.cpp, testEhrhartPolynomial.cpp, geometry/volumes/exampleBoundedLatticePolytopeCount2D.cpp, geometry/volumes/exampleBoundedLatticePolytopeCount3D.cpp, geometry/volumes/exampleBoundedLatticePolytopeCount4D.cpp .

This module relies on module QuickHull algorithm in arbitrary dimension for convex hull and Delaunay cell complex computation for convex hull computations in arbitrary dimensions.

You may also look at Applications of full digital convexity to see some applications of full convexity.

See Fully convex envelope, relative fully convex envelope and digital polyhedra to see how to build fully convex hulls and digital polyhedra.

Introduction to full digital convexity

The usual definition for digital convexity is as follows. For some digital set \( S \subset \mathbb{Z}^d \), \( S \) is said to be digitally convex whenever \( \mathrm{Conv}(S) \cap \mathbb{Z}^d = S \). Otherwise said, the convex hull of all the digital points contains exactly these digital points and no other.

Although handy and easy to check, this definition lacks many properties related to (continuous) convexity in the Euclidean plane.

We extend this definition as follows (see [77] [78] ). Let \( C^d \) be the usual regular cubical complex induced by the lattice \( \mathbb{Z}^d \), and let \( C^d_k \) be its k-cells, for \( 0 \le k \le d \). We have that the 0-cells of \( C^d_0 \) are exactly the lattice points, the 1-cells of \( C^d_1 \) are the open unit segment joining 2 neighboring lattice points, etc.

Finally, for an arbitrary subset \( Y \subset \mathbb{R}^d \), we denote by \( C^d_k \lbrack Y \rbrack \) the set of k-cells of \( C^d \) whose closure have a non-empty intersection with \( Y \), i.e. \( C^d_k \lbrack Y \rbrack := \{ c \in C^d_k,~\text{s.t.}~ \bar{c} \cap Y \neq \emptyset \} \).

A digital set \( S \subset \mathbb{Z}^d \) is said to be digitally k- convex whenever \( C^d_k \lbrack \mathrm{Conv}(S) \rbrack = C^d_k \lbrack S \rbrack \). \( S \) is said to be fully (digitally) convex whenever it is digitally k- convex for \( 0 \le k \le d \).

A fully convex set is always \( 3^d-1 \)-connected (i.e. 8-connected in 2D, 26-connected in 3D). Furthermore its axis-aligned slices are connected (with the same kind of connectedness). It is also clear that digitally 0-convexity is the usual digital convexity.

Examples of non fully digitally convex triangles in Z2. Missing 1-cells for 1-digital convexity are in blue, Missing 2-cells for 2-digital convexity are in green.

A last useful notion is the subconvexity, or tangency. Let \( X \subset \mathbb{Z}^d \) some arbitrary digital set. Then the digital set \( S \subset \mathbb{Z}^d \) is said to be digitally k- subconvex to \( X \) whenever \( C^d_k \lbrack \mathrm{Conv}(S) \rbrack \subset C^d_k \lbrack X \rbrack \). And \( S \) is said to be fully (digitally) subconvex to \( X \) whenever it is digitally k- subconvex to \( X \) for \( 0 \le k \le d \).

Subconvexity is a useful for notion for digital contour and surface analysis. It tells which subsets of these digital sets are tangent to them.

Classes and functions related to digital convexity

Three classes help to check digital convexity.

  • BoundedLatticePolytope is the class that is used to build polytopes, i.e. intersections of half-spaces, which are a way to represent convex polyhedra.
  • CellGeometry is used to store sets of cells and provides methods to build the set of cells that intersect a polytope or the set of cells that touch a set of digital points.
  • DigitalConvexity provides many helper methods to build BoundedLatticePolytope and CellGeometry objects and to check digital convexity and subconvexity.

Lattice polytopes

Construction. You have different ways to build the lattice polytope:

  • from a full dimensional simplex: you may build a polytope in dimension \( d \le 3 \) from a range of \( n \le d + 1 \) points in general position. The polytope is then a simplex. For dimensions higher than 3, you may only build the polytope from a full dimensional simplex, i.e. \( n = d + 1 \) in general position.
  • from a domain and a range of half-spaces: they define obviously a bounded H-polytope.
  • from an arbitrary set of points (full dimensional is the dimension is greater or equal to 4) using DigitalConvexity::makePolytope or ConvexityHelper::computeLatticePolytope.

other operations. You may also cut a polytope by a new halfspace (BoundedLatticePolytope::cut), count the number of lattice points inside, interior or on the boundary (BoundedLatticePolytope::count, BoundedLatticePolytope::countInterior, BoundedLatticePolytope::countBoundary) or enumerate them.

In versions before 1.3, lattice point counting was done in a naive way, by domain enumeration and constraints check. If m is the number of constraints and n the number of lattice points in the polytope domain, then complexity was in \( O(mn) \).
Since 1.3, inside/interior point counting and retrieval have been considerably optimized and are between 5x to 200x faster. The code uses line intersection to compute the inside points as intervals. See examples exampleBoundedLatticePolytopeCount2D.cpp , exampleBoundedLatticePolytopeCount3D.cpp , exampleBoundedLatticePolytopeCount4D.cpp .
#include "DGtal/geometry/volumes/BoundedLatticePolytope.h"
using namespace DGtal::Z3i;
typedef BoundedLatticePolytope< Space > Simplex;
Simplex S( { Point(0,0,0), Point(3,0,0), Point(1,5,0), Point(-3,2,4) } );
std::cout << S.count() << std::endl;
simplex += Simplex::UnitSegment( 0 ); // Extend it along x
std::cout << S.count() << std::endl;
Z3i this namespace gathers the standard of types for 3D imagery.
MyPointD Point
Definition: testClone2.cpp:383

Last, you may compute Minkowski sums of a polytope with axis-aligned segments, squares or (hyper)-cubes (BoundedLatticePolytope::operator+=).

You can check if the result of a Minkowski sum will be valid by calling BoundedLatticePolytope::canBeSummed before. The support is for now limited to polytopes built as simplices in 2D and 3D.
See also

Point check services:

Standard polytope services:

Enumeration services:

Lattice point retrieval services:

The class ConvexityHelper also provides several static methods related to BoundedLatticePolytope:

Building a set of lattice cells from digital points

The class CellGeometry can compute and store set of lattice cells of different dimensions. You specify at construction a Khalimsky space (any model of concepts::CCellularGridSpaceND), as well as the dimensions of the cells you are interested in. Internally it uses a variant of unordered set of points (see UnorderedSetByBlock) to store the lattice cells in a compact manner.

#include "DGtal/geometry/volumes/CellGeometry.h"
using namespace DGtal::Z3i;
K.init( Point(-5,-5,-5), Point(15,15,15) );
CellGeometry< KSpace > cell_geometry( K, 1, 2 ); // only 1-cells and 2-cells
KSpace K

Then you may add cells that touch a range of points, or cells intersected by a polytope, or cells belonging to another CellGeometry object.

With respect to full digital convexity, CellGeometry::addCellsTouchingPolytope is very important since it allows to compute \( C^d_k \lbrack P \rbrack \) for an arbitrary polytope \( P \) and for any \( k \).

Checking digital convexity

Class DigitalConvexity is a helper class to build polytopes from digital sets and to check digital k-convexity. It provides methods for checking if a simplex is full dimensional, building the corresponding polytope, methods for getting the lattice points in a polytope, computing the cells touching lattice points or touching a polytope, and a set of methods to check k-convexity or k-subconvexity (i.e. tangency).

Since 1.3, it can check full convexity of an arbitrary digital set in arbitrary dimension, using its morphological characterization and a generic convex hull algorithm (QuickHull algorithm in arbitrary dimension for convex hull and Delaunay cell complex computation).

Here are two ways for checking full convexity. The first is the simplest (but hides some details):

#include "DGtal/geometry/volumes/DigitalConvexity.h"
using namespace DGtal;
using namespace DGtal::Z3i;
typedef DigitalConvexity< KSpace > DConvexity;
// Create DigitalConvexity object with a domain.
DConvexity dconv( Point( -5, -5 ), Point( 10, 10 ) );
// Specify the vertices of the simplex.
std::vector<Point> V = { Point(0,0), Point(4,-1), Point(2,5) };
// Create the (fat) simplex with all its inner points.
auto fat_simplex = dconv.makeSimplex ( V.begin(), V.end() );
// it is indeed a fully convex set
bool ok = dconv.isFullyConvex ( fat_simplex ); // true
static LatticePolytope makeSimplex(PointIterator itB, PointIterator itE)
bool isFullyConvex(const PointRange &X, bool convex0=false) const
Space::Point Point
Definition: StdDefs.h:168
DGtal is the top-level namespace which contains all DGtal functions and types.

Second way to do it, where we see the intermediate computations of points and cells.

#include "DGtal/geometry/volumes/DigitalConvexity.h"
using namespace DGtal;
using namespace DGtal::Z3i;
typedef DigitalConvexity< KSpace > DConvexity;
// Create DigitalConvexity object with a domain.
DConvexity dconv( Point( -5, -5 ), Point( 10, 10 ) );
// Specify the vertices of the simplex.
std::vector<Point> V = { Point(0,0), Point(4,-1), Point(2,5) };
// Create the (fat) simplex
auto fat_simplex = dconv.makeSimplex ( V.begin(), V.end() );
// Get all the points in the simplex, i.e. creates the digital set Z.
auto inside_pts = dconv.insidePoints ( fat_simplex );
// Get the lattice cells intersected by the simplex i.e. C^d[ Conv(Z) ]
auto simplex_cover = dconv.makeCellCover( fat_simplex );
// Get the lattice cells intersected by the lattice points in the simplex i.e. C^d[ Z ]
auto point_cover = dconv.makeCellCover( inside_pts.begin(), inside_pts.end() );
// Checks that C^d[ Conv(Z) ] is a subset of C^d[ Z ], i.e. Z is fully convex.
bool ok = simplex_cover.subset( point_cover ); // true
bool subset(const CellGeometry &other) const
static PointRange insidePoints(const LatticePolytope &polytope)
CellGeometry makeCellCover(PointIterator itB, PointIterator itE, Dimension i=0, Dimension k=KSpace::dimension) const

Other convexity services, like digital subconvexity (or tangency)

Morphological services (since 1.3):

The following snippet shows that a 4D ball is indeed fully convex.

typedef KhalimskySpaceND<4,int> KSpace;
typedef DigitalConvexity< KSpace > DConvexity;
typedef DigitalSetBySTLSet< Domain > DigitalSet;
Point lo = Point::diagonal( -7 );
Point hi = Point::diagonal( 7 );
Point c = Point::zero;
Domain domain( lo, hi );
DConvexity dconv ( lo, hi );
DigitalSet ball ( domain );
std::vector<Point> V( ball.begin(), ball.end() );
bool cvx0 = dconv.is0Convex( V ); // checks digital 0-convexity
bool fcvx = dconv.isFullyConvex( V ); // checks full convexity
bool is0Convex(const PointRange &X) const
static void addNorm2Ball(TDigitalSet &aSet, const Point &aCenter, UnsignedInteger aRadius)
Domain domain
HyperRectDomain< Space > Domain
Z2i::DigitalSet DigitalSet
This method for checking full convexity is slightly slower than the method using the Minkowski sum on the polytope contraints, but it works in arbitrary dimension and for arbitrary digital set.

Simplex services:

Polytope services:

Lattice cell geometry services:

Convexity services:

Ehrhart polynomial of a lattice polytope

Any lattice polytope has a unique Ehrhart polynomial that encodes the relationship between the volume of the polytope and the number of integer points the polytope contains. It is a kind of extension of 2D Pick's theorem. More precisely, if \( P \) is a (bounded) polytope in \( \mathbb{R}^d \) with vertices lying in \( \mathbb{Z}^d \), and for any positive integer \( t \), let \( tP \) denotes the dilation of \( P \) by a factor \( t \).

We denote \( L(P,t) := \#( tP \cap \mathbb{Z}^d ) \), i.e. the number of lattice points included in the dilation of this polytope.

Then \( L(P,t) \) is a polynomial in \( t \) of degree \( f \). Its monomial coefficients \( L_k(P) \) are rational numbers, and some coefficients have a clear geometric meaning, e.g.:

  • \( L_0(P) \) is the Euler characteristic of the polytope, which is 1.
  • \( L_d(P) \) is the volume of the polytope.

Note also that, by Ehrhart-MacDonald reciprocity, the polynomial \( (-1)^d L(P,-t) \) counts the number of interior lattice points to \( tP \).

Class EhrhartPolynomial provides an elementary method to determine the Ehrhart polynomial of any bounded lattice polytope.

See testEhrhartPolynomial.cpp for examples.

#include "DGtal/geometry/volumes/EhrhartPolynomial.h"
using namespace Z2i;
std::vector< Point > T = { Point(0,0), Point(1,0), Point(2,1) };
DigitalConvexity< KSpace > dconv( Point::diagonal( -100 ), Point::diagonal( 100 ) );
auto P = dconv.makeSimplex( T.cbegin(), T.cend() );
EhrhartPolynomial< Space, int64_t > E( P );
// Its Ehrhart polynomial is 1/2( 2 + 3t + t^2 )
auto expP = mmonomial<Integer>( 2 ) + 3 * mmonomial<Integer>( 1 ) + 2;
REQUIRE( E.numerator() == expP );
REQUIRE( E.denominator() == 2 );
// number of lattice points of its 4-fold dilated version
auto P4 = 4 * P;
auto n4 = E.count( 4 );
REQUIRE( P4.count() == n4 );
// number of interior lattice points of its 4-fold dilated version
auto P4 = 4 * P;
auto n4 = E.countInterior( 4 );
REQUIRE( P4.countInterior() == n4 );
The Ehrhart polynomial is determined by taking a series of dilation of the polytope (1,2,...,d) and counting by brute-force the number of lattice points within these dilated polytopes. We then compute the Lagrange interpolating polynomial (using class LagrangeInterpolation) and we know that this must be the Ehrhart polynomial. There exists faster ways to compute it, which are however much more complex. See for instance LattE software: https://www.math.ucdavis.edu/~latte .

Rational polytopes

You can also create bounded rational polytopes, i.e. polytopes with vertices with rational coordinates, with class BoundedRationalPolytope. You must give a common denominator for all rational coordinates.

#include "DGtal/geometry/volumes/BoundedRationalPolytope.h"
typedef SpaceND<2,int> Space;
typedef BoundedRationalPolytope< Space > Polytope;
// A thin triangle P at (4/4,2/4), (2/4,4/4), (9/4,9/4)
Point a( 4, 2 );
Point b( 2, 4 );
Point c( 9, 9 );
Polytope P { Point(4,4), a, b, c };

Then the interface of BoundedRationalPolytope is almost the same as the one of BoundedLatticePolytope (see Lattice polytopes ).

The classs BoundedRationalPolytope offers dilatation by an arbitrary rational, e.g. as follows

Polytope Q = Polytope::Rational( 10, 3 ) * P; // 10/3 * P

You may also check digital convexity and compute cell covers with bounded rational polytopes, exactly in the same way as with BoundedLatticePolytope.

Big denominators increase with the same factor coefficients of half space constraints, hence the integer type should be chosen accordingly.

Further notes

The class BoundedLatticePolytope is different from the class LatticePolytope2D for the following two reasons:

There are no simple conversion from one to the other. Class LatticePolytope2D is optimized for cuts and lattice points enumeration, and is very specific to 2D. Class BoundedLatticePolytope is less optimized than the previous one but works in nD and provides Minkowski sum and dilation services.