Filtering the Noise

Clouds of points are often composed of raw data. Furthermore, the lack of sensor precision can lead to some very noisy point clouds. Some investigations should be done to avoid such bad cases.

../../../_images/bk_pc_filtering_01.jpg

An example of isolated useless point

Removing Isolated Points

Isolated points, when processed by the size calculation algorithm (see the doc Preparing the Point Shape Data under the section Filling the Empty Space), lead to giant shapes. Indeed, the huge amount of empty space around them needs to be covered. In fact, these points are useless for the rendering of the point cloud. They need to be removed.

To define if a point is isolated or not, we have to compare the distance from the point to all its neighbours with the average distance between all the neighbours.

Two values need to be computed:

  • The average distance from the point to its neighbours

  • For each neighbour, the average distance between it to the other neighbours. Then the average value of these distances

If the first one is higher than the second (with a certain factor), we can say that the point is isolated.

These two values are computed during the shapes preparation time. In order to let the user the ability to change the multiplication factor at runtime, the comparison to discard the point is done at the shader stage.

// Compute the average distance between all the neighbours.
double distance = 0.0;
int nb = 0;
for( int n1 = 0; n1 < neighbours.size() - 1; ++n1 )
{
  for( int n2 = n1 + 1; n2 < neighbours.size() - 1; ++n2 )
  {
    distance += ( neighbours[n1].position - neighbours[n2].position ).length();
    ++nb;
  }
}
double average_distance = distance / nb;

The first value was already sent to the shader using the first coordinate of the RED::MCL_TEX0 channel (see in the doc Preparing the Point Shape Data under the section Filling the Empty Space). The second one will be sent in exactly the same way using the second coordinate of the RED::MCL_TEX0 channel.

The vertex shader code gets the data like this:

vertex_out.discardPoint = ( gl_MultiTexCoord0.y * discard_threshold < gl_MultiTexCoord0.x ) ? 1.0 : 0.0;

‘gl_MultiTexCoord0.y’ contains the computed total average distance.

‘discard_threshold’ is the multiplication factor passed as a shader variable.

‘gl_MultiTexCoord0.x’ still contains the distance from the point to its neighbours. It is used to size the shape.

If the point is isolated, it will be discarded in the geometry stage. The geometry shader function does nothing if the ‘discardPoint’ boolean is true.

../../../_images/bk_pc_filtering_02.jpg

The same scene after removing the isolated points

By increasing the ‘discard_threshold’ value, more and more unwanted isolated shapes will be drawn. On the other hand, the threshold should not be decreased too much because it could remove useful points. A good compromise have to be found depending on the scene.

Limiting the Point Size

Another solution to remove the giant shapes is to simply fix an upper limit to the shape size. The upper limit could be a given amount of the average point size in the entire scene.

As usual, the operation is done in the shader stage and more precisely in the vertex shader to allow the user to change the values at runtime.

// Transfer the color and the point size.
vertex_out.vColor = gl_Color;
vertex_out.pointSize = min( gl_MultiTexCoord0.x, average_distance * capped_point_size ) * point_size_scale;

The point size (given by the ‘gl_MultiTexCoord0.x’ variable) is clamped and never exceeds the total average point size of the scene (given by the ‘average_distance’ variable) multiplied by a user factor (given by the ‘capped_point_size’ variable). The ‘point_size_scale’ variable is a simple scale to the overall point size.