This page was generated from docs/tutorials/cga/clustering.ipynb. Interactive online version: .

# Example 2 Clustering Geometric Objects¶

In this example we will look at a few of the tools provided by the clifford package for (4,1) conformal geometric algebra (CGA) and see how we can use them in a practical setting to cluster geometric objects via the simple K-means clustering algorithm provided in clifford.tools

As before the first step in using the package for CGA is to generate and import the algebra:

:

from clifford.g3c import *
print('e1*e1 ', e1*e1)
print('e2*e2 ', e2*e2)
print('e3*e3 ', e3*e3)
print('e4*e4 ', e4*e4)
print('e5*e5 ', e5*e5)

e1*e1  1
e2*e2  1
e3*e3  1
e4*e4  1
e5*e5  -1


The tools submodule of the clifford package contains a wide array of algorithms and tools that can be useful for manipulating objects in CGA. In this case we will be generating a large number of objects and then segmenting them into clusters.

We first need an algorithm for generating a cluster of objects in space. We will construct this cluster by generating a random object and then repeatedly disturbing this object by some small fixed amount and storing the result:

:

from clifford.tools.g3c import *
import numpy as np

def generate_random_object_cluster(n_objects, object_generator, max_cluster_trans=1.0, max_cluster_rot=np.pi/8):
""" Creates a cluster of random objects """
ref_obj = object_generator()
cluster_objects = []
for i in range(n_objects):
r = random_rotation_translation_rotor(maximum_translation=max_cluster_trans, maximum_angle=max_cluster_rot)
new_obj = apply_rotor(ref_obj, r)
cluster_objects.append(new_obj)
return cluster_objects

/home/docs/checkouts/readthedocs.org/user_builds/clifford/envs/latest/lib/python3.8/site-packages/pyganja/__init__.py:2: UserWarning: Failed to import cef_gui, cef functions will be unavailable
from .script_api import *


We can use this function to create a cluster and then we can visualise this cluster with pyganja.

:

from pyganja import *
clustered_circles = generate_random_object_cluster(10, random_circle)
sc = GanjaScene()
for c in clustered_circles:
draw(sc, scale=0.05)


This cluster generation function appears in clifford tools by default and it can be imported as follows:

:

from clifford.tools.g3c import generate_random_object_cluster


Now that we can generate individual clusters we would like to generate many:

:

def generate_n_clusters( object_generator, n_clusters, n_objects_per_cluster ):
object_clusters = []
for i in range(n_clusters):
cluster_objects = generate_random_object_cluster(n_objects_per_cluster, object_generator,
max_cluster_trans=0.5, max_cluster_rot=np.pi / 16)
object_clusters.append(cluster_objects)
all_objects = [item for sublist in object_clusters for item in sublist]
return all_objects, object_clusters


Again this function appears by default in clifford tools and we can easily visualise the result:

:

from clifford.tools.g3c import generate_n_clusters

all_objects, object_clusters = generate_n_clusters(random_circle, 2, 5)
sc = GanjaScene()
for c in all_objects:
draw(sc, scale=0.05)


Given that we can now generate multiple clusters of objects we can test algorithms for segmenting them.

The function run_n_clusters below generates a lot of objects distributed into n clusters and then attempts to segment the objects to recover the clusters.

:

from clifford.tools.g3c.object_clustering import n_clusters_objects
import time

def run_n_clusters( object_generator, n_clusters, n_objects_per_cluster, n_shotgunning):
all_objects, object_clusters = generate_n_clusters( object_generator, n_clusters, n_objects_per_cluster )
[new_labels, centroids, start_labels, start_centroids] = n_clusters_objects(n_clusters, all_objects,
initial_centroids=None,
n_shotgunning=n_shotgunning,
averaging_method='unweighted')
return all_objects, new_labels, centroids


Lets try it!

:

def visualise_n_clusters(all_objects, centroids, labels,
color_1=np.array([255, 0, 0]),
color_2=np.array([0, 255, 0])):
"""
Utility method for visualising several clusters and their respective centroids
using pyganja
"""
alpha_list = np.linspace(0, 1, num=len(centroids))
sc = GanjaScene()
for ind, this_obj in enumerate(all_objects):
alpha = alpha_list[labels[ind]]
cluster_color = (alpha * color_1 + (1 - alpha) * color_2).astype(np.int)

for c in centroids:

return sc

object_generator = random_circle

n_clusters = 3
n_objects_per_cluster = 10
n_shotgunning = 60
all_objects, labels, centroids = run_n_clusters(object_generator, n_clusters,
n_objects_per_cluster, n_shotgunning)

sc = visualise_n_clusters(all_objects, centroids, labels,
color_1=np.array([255, 0, 0]),
color_2=np.array([0, 255, 0]))
draw(sc, scale=0.05)

/tmp/ipykernel_651/1063371436.py:12: DeprecationWarning: np.int is a deprecated alias for the builtin int. To silence this warning, use int by itself. Doing this will not modify any behavior and is safe. When replacing np.int, you may wish to use e.g. np.int64 or np.int32 to specify the precision. If you wish to review your current use, check the release note link for additional information.
Deprecated in NumPy 1.20; for more details and guidance: https://numpy.org/devdocs/release/1.20.0-notes.html#deprecations
cluster_color = (alpha * color_1 + (1 - alpha) * color_2).astype(np.int)