Plane Detection Overview

Introduction

With the 3D information of the environment the ZED camera can estimate the position of the planes present in the scene.

To detect a plane the position tracking needs to be enabled with zed.enablePositionalTracking(). Also a plane can be detected only if the tracking state of the camera is OK.

Therefore the procedure for detecting a plane is as follow:

  • Enable the positional tracking module
  • Grab an image from the ZED
  • Check that the tracking state is OK
  • Estimate a plane position.

Detecting Planes

To estimate a plane position, you will need to use the findPlaneAtHit function and pass the 2D coordinates of a target pixel where you want to find its support plane. Below is an example of code that can be used to estimate the plane passing by the depth value of pixel coord = (u,v):

sl::Plane plane;
sl::uint2 coord; // Fill it with the coordinates taken from the full size image  
while(zed.grab() == ERROR_CODE::SUCCESS) {
  tracking_state = zed.getPosition(pose); // Get the tracking state of the camera
  if(tracking_state == TRACKING_STATE::OK) {  
    // Detect the plane passing by the depth value of pixel coord
    find_plane_status = zed.findPlaneAtHit(coord, plane);
  }
}
plane = sl.Plane() # Structure that stores the estimated plane
coord = sl.uint2() # Fill it with the coordinates taken from the full size image
while zed.grab() == sl.ERROR_CODE.SUCCESS :
  tracking_state = zed.get_position(pose) # Get the tracking state of the camera
  if tracking_state == sl.TRACKING_STATE.OK) :  
    # Detect the plane passing by the depth value of pixel coord
    find_plane_status = zed.find_plane_at_hit(coord, plane)
PlaneData plane = new sl.PlaneData();
Vector2 coord = new Vector2();
sl.RuntimeParameters runtimeParameters = new sl.RuntimeParameters();
while (zed.Grab(ref runtimeParameters) == ERROR_CODE.SUCCESS){
    sl.Pose pose = new sl.Pose();
    sl.POSITIONAL_TRACKING_STATE tracking_state = zed.GetPosition(ref pose);
    if (tracking_state == sl.POSITIONAL_TRACKING_STATE.OK)
    {
       sl.ERROR_CODE e = zed.findPlaneAtHit(ref plane, coord);
    }
}

If it succeeds the function stores the detected plane in a sl::Plane object, which includes useful information such as 3D position, normal, polygon boundaries and plane type (vertical / horizontal).

Accessing Plane Data

The sl::Plane class contains all the information for defining the plane in space such as normal, center and equation. To access this information use the getter of the class. For example:

if(find_plane_status == ERROR_CODE::SUCCESS){
  sl::float3 normal = plane.getNormal(); // Get the normal vector of the detected plane
  sl::float4 plane_equation = plane.getPlaneEquation(); // Get (a,b,c,d) where ax+by+cz=d
}
if find_plane_status == sl.ERROR_CODE.SUCCESS :
  normal = plane.get_normal() # Get the normal vector of the detected plane
  plane_equation = plane.get_plane_equation() # Get (a,b,c,d) where ax+by+cz=d
if(find_plane_status == ERROR_CODE.SUCCESS){
  Vector3 normal = plane.PlaneNormal;// Get the normal vector of the detected plane
  Vector4 plane_equation = plane.PlaneEquation;// Get (a,b,c,d) where ax+by+cz=d
}

For AR purposes it can be useful to get the Transform of the detected plane according to the global reference frame. For example to place an object close to a wall or to transform the global reference frame center to the plane center. This transform is accessible as follow:

if(find_plane_status == ERROR_CODE::SUCCESS){
  // Get the transform of the plane according to the global reference frame
  sl::Transform plane_transform = plane.getPose();
}
if find_plane_status == sl.ERROR_CODE.SUCCESS :
  # Get the transform of the plane according to the global reference frame
  plane_transform = sl.Transform()
  plane_equation = plane.get_transform()
if(find_plane_status == ERROR_CODE.SUCCESS){
  // Get the transform of the plane according to the global reference frame
  public Quaternion rotation;
  public Vector3 translation;
  translation = plane.PlaneTransformPosition;
  rotation = plane.PlaneTransformOrientation;
}

Convert Plane to Mesh

The detected plane can be converted to a Mesh. Converting it to a mesh can be useful when highlighting a plane in the scene. To get the mesh of the plane, call:

 sl::Mesh mesh = plane.extractMesh();
mesh = sl.Mesh()
mesh = plane.extract_mesh()
Vector3[] planeMeshVertices = new Vector3[65000];
int[] planeMeshTriangles = new int[65000];
int numVertices = 0;
int numTriangles = 0;
zed.convertFloorPlaneToMesh(planeMeshVertices, planeMeshTriangles, out numVertices, out numTriangles);

Detecting Floor Plane

With the 3D information of the environment, ZED cameras can estimate where is the ground floor in a scene.

Getting Floor Plane

The floor plane can be automatically detected by calling findFloorPlane instead of findPlaneAtHit. Apart from storing the plane in a sl::Plane, this function will also store the transform between the floor plane frame and the camera frame.

This transform can be used to reset the tracking and align it with the floor plane frame as follow:

sl::Plane plane;
sl::Transform resetTrackingFloorFrame;
find_plane_status = zed.findFloorPlane(plane, resetTrackingFloorFrame);
if(find_plane_status == ERROR_CODE::SUCCESS){
  // Reset positional tracking to align it with the floor plane frame
  zed.resetPositionalTracking(resetTrackingFloorFrame);
}
plane = sl.Plane()
resetTrackingFloorFrame = sl.Transform()
find_plane_status = zed.find_floor_plane(plane, resetTrackingFloorFrame)
if find_plane_status == sl.ERROR_CODE.SUCCESS :
  # Reset positional tracking to align it with the floor plane frame
  zed.reset_positional_tracking(resetTrackingFloorFrame)
PlaneData plane = new PlaneData();
float playerHeight = 0;
Quaternion priorQuat = Quaternion.Identity;
Vector3 priorVec = Vector3.Zero;
ERROR_CODE find_plane_status = zedCamera.findFloorPlane(ref plane, out playerHeight, priorQuat, priorVec);
if (find_plane_status == ERROR_CODE.SUCCESS){
    // Reset positional tracking to align it with the floor plane frame
    zed.ResetPositionalTracking(plane.PlaneTransformOrientation, plane.PlaneTransformPosition);
}

This code can be simplified by using PositionalTrackingParameters::set_floor_as_origin to aligned the positional tracking reference frame on the ground floor.

PositionalTrackingParameters positional_tracking_parameters;
positional_tracking_parameters.set_floor_as_origin = true;
zed.enablePositionalTracking(positional_tracking_parameters);

RuntimeParameters runtime_parameters;
runtime_parameters.measure3D_reference_frame = REFERENCE_FRAME::WORLD;
sl::Mat cloud;
while(zed.grab(runtime_parameters) == ERROR_CODE::SUCCESS) {
  zed.retrieveMeasure(cloud, MEASURE::XYZRGBA);
  // The point cloud is aligned on the floor plane.
  // A threshold on the height could then be used as a simple object detection method
}
positional_tracking_parameters = sl.PositionalTrackingParameters()
positional_tracking_parameters.set_floor_as_origin = True
zed.enable_positional_tracking(positional_tracking_parameters)

runtime_parameters = sl.RuntimeParameters()
runtime_parameters.measure3D_reference_frame = sl.REFERENCE_FRAME::WORLD
cloud = sl.Mat()

while zed.grab(runtime_parameters) == sl.ERROR_CODE.SUCCESS :
  zed.retrieveMeasure(cloud, sl.MEASURE::XYZRGBA)
  # The point cloud is aligned on the floor plane.
  # A threshold on the height could then be used as a simple object detection method
PositionalTrackingParameters trackingParams = new PositionalTrackingParameters();
trackingParams.setFloorAsOrigin = true;
zed.EnablePositionalTracking(ref trackingParams);

RuntimeParameters runtimeParameters = new RuntimeParameters();
runtimeParameters.measure3DReferenceFrame = REFERENCE_FRAME.WORLD;
Mat cloud = new Mat();
while(zed.Grab(ref runtimeParameters) == ERROR_CODE.SUCCESS)
{
    zed.RetrieveMeasure(cloud, MEASURE.XYZRGBA);
    // The point cloud is aligned on the floor plane.
    // A threshold on the height could then be used as a simple object detection method
}

The estimation of the floor plane can be refined by passing prior parameters (such as prior height or orientation) to the findFloorPlane function. For additional details on these parameters, please check out the API Reference.