Suppose you have a block of Maasdam, and want to know how many tunnels there are running through the cheese, and how many voids or 'bubbles' are contained therein.

Using highly sophisticated methods we can convert this three-dimensional object into a sequence of two-dimensional slices...




And then read these into the computer...



gap> X:=ReadImageSequence([threecheeseslices]);;
gap> Size(X);
441922
gap> ContX:=ContractedComplex(X);;time;
10 seconds
gap> Size(ContX);
6208
gap> ZZContX:=ZigZagContractedComplex(ContX);;time;
22 seconds
gap> Size(ZZContX);
1042
gap> Bettinumbers(ZZContX);time;
[1,9,1,0]
5 seconds
These Bettinumbers tell us that our initial block of cheese was a single connected object, had 9 tunnels running through it, and had 1 "bubble" inside it.
In this example, we are presented with a selection of nuts, bolts and washers, and use our software to distinguish between them.
gap> A:=ReadImage("bits.jpg",250);;
gap> View(A);
The "250" is an RGB threshold value, dictating which pixels should be deemed "present" in the data set.
Here we create a copy of the image without bolts, by creating an empty image of the same size and then adding in all of the objects with Euler characteristic 0. Any object which is homotopy equivalent to a circle will have Euler characteristic 0 - ie. this includes washers and nuts, but not bolts. The PathComponent(A,n) function selects the nth object in the data set A, except where n=0, in which case the function returns the number of separate objects in A.gap> B=PureComplex(A!.binaryArray*0);;
gap> for i in [1..PathComponent(A,0)] do
> if EulerCharacteristic(PathComponent(A,i))=0 then
> B:=PureComplexUnion(B,PathComponent(A,i));;
> fi; od;
Washers and nuts are homotopy equivalent - both contain exactly one hole. However, the nuts have some corners, which are not found on the smooth boundary of the washers. The function Singularities identifies these corners, with two threshold values (in this case 5 and 50) defining what we are regarding as a "corner".gap> C:=PureComplex(B!.binaryArray*0);;
gap> for i in [1..PathComponent(B,0)] do
> if Size(Singularities(PathComponent(B,i),5,50)=0 then
> C:=Union(C,PathComponent(B,i));;
> fi;od;
If we intuitively "thicken" the remaining objects, we might expect that the holes in some will disappear before others. We can thus, by monitoring the Euler characteristic of the objects, identify the washers by size. The "7" here was chosen because it gave the clearest results for this example, but is of no other particular significance.
gap> D:=PureComplex(C!.binaryArray*0);;
gap> for i in [1..PathComponent(C,0)] do
> p:=Thickened(PathComponent(C,i));
> for j in [1..7] do p:=Thickened(p);;
> if EulerCharacteristic(p)=0 then
> D:=Union(D,PathComponent(C,i));;
> fi;od;od;
See my "Computation of persistent Betti numbers using cubical and permutahedral complexes" presentation for a nice example illustrating homology estimation from a sample of data, and persistent homology.
My viva presentation explains the contraction methods the software uses.