docs: public void pruneObservationsByErrorRank(double inlierFraction) {
  
  		Point2D_F64 observation = new Point2D_F64();
  		Point2D_F64 predicted = new Point2D_F64();
  		Point3D_F64 X3 = new Point3D_F64();
  		Point4D_F64 X4 = new Point4D_F64();
  
  		// Create a list of observation errors
  		List<Errors> errors = new ArrayList<>();
  		for (int viewIndex = 0; viewIndex < observations.views.length; viewIndex++) {
  			SceneObservations.View v = observations.views[viewIndex];
  			SceneStructureProjective.View view = structure.views[viewIndex];
  
  			for (int indexInView = 0; indexInView < v.point.size; indexInView++) {
  				int pointID = v.point.data[indexInView];
  				SceneStructureMetric.Point f = structure.points[pointID];
  
  				// Get observation in image pixels
  				v.get(indexInView, observation);
  
  				// Get feature location in world and predict the pixel observation
  				if( structure.homogenous ) {
  					f.get(X4);
  					PerspectiveOps.renderPixel(view.worldToView,X4,predicted);
  				} else {
  					f.get(X3);
  					PerspectiveOps.renderPixel(view.worldToView,X3,predicted);
  				}
  
  				Errors e = new Errors();
  				e.view = viewIndex;
  				e.pointIndexInView = indexInView;
  				e.error = predicted.distance2(observation);
  				errors.add(e);
  			}
  		}
  
  		errors.sort(Comparator.comparingDouble(a -> a.error));
  
  		// Mark observations which are to be removed. Can't remove yet since the indexes will change
  		int index0 = (int) (errors.size() * inlierFraction+0.5);
  		for (int i = index0; i < errors.size(); i++) {
  			Errors e = errors.get(i);
  
  			SceneObservations.View v = observations.views[e.view];
  			v.set(e.pointIndexInView, Float.NaN, Float.NaN);
  		}
  
  		// Remove all marked features
  		removeMarkedObservations();
  	}