Public API

Note, that not all exported functions are considered as part of the public API. The private API is not mature yet, expect it to change.

Representing a point cloud

RANSAC.PointCloudType
mutable struct PointCloud{A<:AbstractArray, B<:AbstractArray, C<:AbstractArray}

A struct to wrap a point cloud. Stores the vertices, the normals, the subsets (as an array of arrays of vertice indexes), an array to indicate if a point is part of an already extracted primitive, the size of the point cloud (number of vertices), the weight of each octree level (populated during the construction of the octree, by copying the given element), an array to store the sum of the score of already extracted primitives for each octree level.

source
RANSAC.PointCloudMethod
PointCloud(vertices, normals, subsets)

Construct a PointCloud, filling it's other fields.

size=length(vertices), levelweight=[1.0], levelscore=[1.0], isenabled=trues(length(vertices))

source
RANSAC.PointCloudMethod
PointCloud(vertices, normals, subsets, isenabled)

Construct a PointCloud with filling it's size, isenabled and levelweight fields.

size=length(vertices), levelweight=[1.0], levelscore=[1.0]

source
RANSAC.PointCloudMethod
PointCloud(vertices, normals, subsets, isenabled, size, levelweight, levelscore)

Constructor that converts vertices and normals to array of SVector{3,Float64}.

Arguments

  • vertices::AbstractArray: an array of vertices.
  • normals::AbstractArray: an array of surface normals.
  • subsets::AbstractArray: an array consisting of index-arrays, that describe which point is in which subset.
  • isenabled::BitArray{1}: an array indicating if the given point is enabled (true) or already has been extracted (false).
  • size::Int: number of points in the cloud.
  • levelweight::Vector{Float}: the weight of every octree level.
  • levelscore::Vector{Float}: for every octree level, store the sum of the scores of primitives that have been extracted.
source

Parameters

With the help of Parameters.jl it's easy to parameterize the algorithm. The RANSACParameters type collects all the parameters, though its fields are subject to change, the current fields and default values are listed below. Note that fields that are float types must use float literals (so 1.0 is ok, but 1 is not).

Constructing from code

@with_kw struct RANSACParameters{R<:Real} @deftype R
    ϵ_plane = 0.3
    α_plane = deg2rad(5)

    ϵ_sphere = 0.3
    α_sphere = deg2rad(5)

    ϵ_cylinder = 0.3
    α_cylinder = deg2rad(5)

    ϵ_cone = 0.3
    α_cone = deg2rad(5)
    # filter those cones, whose opening angle is less than `minconeopang` radians
    minconeopang = deg2rad(2)

    ϵ_torus = 0.3
    α_torus = deg2rad(5)

    # number of points to be sampled (length of a minimal subset)
    drawN::Int = 3; @assert drawN>2
    # number of minimal sets sampled in one iteration
    minsubsetN::Int = 15; @assert minsubsetN>0
    # probability of detection
    prob_det = 0.9
    # minimal shape size
    τ::Int = 900
    # maximum number of iteration
    itermax::Int = 1000

    # threshold of two vectors being parallel (in degrees)
    parallelthrdeg = 1.
    # threshold of points being collinear
    collin_threshold = 0.2
    # parameter in sphere fitting
    sphere_par = 0.02

    # shapes that are fitted to the point cloud
    shape_types::Array{Symbol,1} = [:sphere, :plane, :cylinder, :cone]
end

You can use the following functions to set every $\alpha$ or $\epsilon$ parameters to a certain value:

RANSAC.setalphasFunction
setalphas(p::RANSACParameters, α)

Set all α_* fields to α.

Sets α_plane, α_sphere, α_cylinder, α_cone.

source
RANSAC.setepsilonsFunction
setepsilons(p::RANSACParameters, ϵ)

Set all ϵ_* fields to ϵ.

Sets ϵ_plane, ϵ_sphere, ϵ_cylinder, ϵ_cone.

source

Parse from YAML file

With the help of YAML.jl one can easily read the parameters from a YAML file. As shown below, you can specify which parameters you want to change (the others are going to be the default ones). Note, that in the YAML file, you must specify fields with float types as float-literals.

An example file (config.yml):

# ϵ_plane is float, so it can't be `1`
ϵ_plane: 1.0
α_plane: 0.0872

# maximum number of iteration
itermax: 100

# threshold of two vectors being parallel (in degrees)
parallelthrdeg: 1.2
# threshold of points being collinear
collin_threshold: 0.22
# parameter in sphere fitting
sphere_par: 0.025

# shapes that are fitted to the point cloud
# must be an array
shape_types:
  - plane
  - cone

Then you can use the readconfig function to read the file:

julia> readconfig("config.yml")
RANSACParameters{Float64}
  ϵ_plane: Float64 1.0
  α_plane: Float64 0.0872
  ϵ_sphere: Float64 0.3
  α_sphere: Float64 0.08726646259971647
  ϵ_cylinder: Float64 0.3
  α_cylinder: Float64 0.08726646259971647
  ϵ_cone: Float64 0.3
  α_cone: Float64 0.08726646259971647
  minconeopang: Float64 0.03490658503988659
  ϵ_torus: Float64 0.3
  α_torus: Float64 0.08726646259971647
  drawN: Int64 3
  minsubsetN: Int64 15
  prob_det: Float64 0.9
  τ: Int64 900
  itermax: Int64 100
  parallelthrdeg: Float64 1.2
  collin_threshold: Float64 0.22
  β: Float64 1.0
  sphere_par: Float64 0.025
  transl_conn: Symbol eight
  shape_types: Array{Symbol}((2,))
  extract_s: Symbol nofminset
  terminate_s: Symbol nofminset

You can also specify the type of the parameter struct (can be Float32 as well).

julia> readconfig("config.yml", RANSACParameters{Float32})
RANSACParameters{Float32}
  ϵ_plane: Float32 1.0f0
  α_plane: Float32 0.0872f0
  ϵ_sphere: Float32 0.3f0
  α_sphere: Float32 0.08726646f0
  ϵ_cylinder: Float32 0.3f0
  α_cylinder: Float32 0.08726646f0
  ϵ_cone: Float32 0.3f0
  α_cone: Float32 0.08726646f0
  minconeopang: Float32 0.034906585f0
  ϵ_torus: Float32 0.3f0
  α_torus: Float32 0.08726646f0
  drawN: Int64 3
  minsubsetN: Int64 15
  prob_det: Float32 0.9f0
  τ: Int64 900
  itermax: Int64 100
  parallelthrdeg: Float32 1.2f0
  collin_threshold: Float32 0.22f0
  β: Float32 1.0f0
  sphere_par: Float32 0.025f0
  transl_conn: Symbol eight
  shape_types: Array{Symbol}((2,))
  extract_s: Symbol nofminset
  terminate_s: Symbol nofminset
RANSAC.readconfigFunction
readconfig(fname, paramtype=RANSACParameters)

Read a config file to a parameter sruct type of paramtype (RANSACParameters is the default).

Arguments:

  • fname: file name.
  • paramtype=RANSACParameters: type of the parameter struct. One must be able to instantiate it with keyword arguments only (eg. paramtype() should work).
source

Primitives

RANSAC.FittedPlaneType
struct FittedPlane{A<:AbstractArray} <: FittedShape

Plane primitive, defined by one of its point, and its normalvector.

source
RANSAC.FittedSphereType
struct FittedSphere{A<:AbstractArray, R<:Real} <: FittedShape

Sphere primitive, defined by its center and radius. Also stored, if the normals point outwards of the shape.

source
RANSAC.FittedCylinderType
struct FittedCylinder{A<:AbstractArray, R<:Real} <: FittedShape

Cylinder primitive, defined by its axis direction, a point that lies on its axis, and its radius. Also stored, if the normals point outwards of the shape.

source
RANSAC.FittedConeType
struct FittedCone{A<:AbstractArray, R<:Real} <: FittedShape

Cone primitive, defined by its apex, its axis (that points from the apex towards the opening), and its opening angle in radians. Also stored, if the normals point outwards of the shape.

source

Iteration

The ransac() function does the iteration.

RANSAC.ransacFunction
ransac(pc, params, setenabled; reset_rand = false)

Run the RANSAC algorithm on a pointcloud with the given parameters.

Returns the candidates (as they are at the end of the iteration), the extracted primitives and the time it took to run the algorithm (in seconds).

Arguments

  • pc::PointCloud: the point cloud.
  • params::RANSACParameters: parameters.
  • setenabled::Bool: if true: set every point to enabled.
  • reset_rand::Bool=false: if true, resets the random seed with Random.seed!(1234)
source
ransac(pc, params; reset_rand = false)

Run the RANSAC algorithm on a pointcloud with the given parameters.

Returns the candidates (as they are at the end of the iteration), the extracted primitives and the time it took to run the algorithm (in seconds).

Arguments

  • pc::PointCloud: the point cloud.
  • params::RANSACParameters: parameters.
  • reset_rand::Bool=false: if true, resets the random seed with Random.seed!(1234)
source

Exporting the results

With the help of JSON.jl, the resulted shapes can be easily saved to JSON files. For this purpose, the exportJSON() function can be used. Note that io must be specified, the "default" fallback to stdout is not implemented.

RANSAC.exportJSONFunction
printJSON(io::IO, s, indent)

Print a FittedShape, ShapeCandidate, ScoredShape or a vector of them to io as a JSON string. With indent given, it prints a representation with newlines and indents.

Arguments:

  • io::IO: must be specified, use stdout for interactive purposes.
  • s: a FittedShape, ShapeCandidate, ScoredShape or a vector of one of them.
  • indent::Int: indentation level.
source
printJSON(io::IO, s)

Print a FittedShape, ShapeCandidate, ScoredShape or a vector of them to io as a compact JSON string.

Arguments:

  • io::IO: must be specified, use stdout for interactive purposes.
  • s: a FittedShape, ShapeCandidate, ScoredShape or a vector of one of them.
source

A few examples:

julia> using RANSAC, StaticArrays

julia> s1 = FittedPlane(SVector(0,0,1.), SVector(12.5, 7, 24))
FittedPlane{SArray{Tuple{3},Float64,1,3}}
normal: [12.5, 7.0, 24.0], point: [0.0, 0.0, 1.0]

julia> exportJSON(stdout, s1)
{"point":[0.0,0.0,1.0],"normal":[12.5,7.0,24.0],"type":"plane"}

julia> exportJSON(stdout, s1, 2)
{
  "point": [
    0.0,
    0.0,
    1.0
  ],
  "normal": [
    12.5,
    7.0,
    24.0
  ],
  "type": "plane"
}

It is advised to export shapes in an array for easier processing (though I'm not a JSON expert):

julia> exportJSON(stdout, [s1])
{"primitives":[{"point":[0.0,0.0,1.0],"normal":[12.5,7.0,24.0],"type":"plane"}]}

julia> exportJSON(stdout, [s1], 2)
{
  "primitives": [
    {
      "point": [
        0.0,
        0.0,
        1.0
      ],
      "normal": [
        12.5,
        7.0,
        24.0
      ],
      "type": "plane"
    }
  ]
}

Works of course for different primitives:

julia> s2 = FittedSphere(SVector(1.2, 3., 5.), 1.5, true)
FittedSphere{SArray{Tuple{3},Float64,1,3}, Float64}
center: [1.2, 3.0, 5.0], R: 1.5, outwards

julia> exportJSON(stdout, [s1, s2], 2)
{
  "primitives": [
    {
      "point": [
        0.0,
        0.0,
        1.0
      ],
      "normal": [
        12.5,
        7.0,
        24.0
      ],
      "type": "plane"
    },
    {
      "outwards": true,
      "radius": 1.5,
      "center": [
        1.2,
        3.0,
        5.0
      ],
      "type": "sphere"
    }
  ]
}

As can be seen above, in these cases an array of "primitives" is printed. Under the hood, the toDict() function does the job of converting the primitives to Dicts.

RANSAC.toDictMethod
toDict(s::FittedShape)

Convert s to a Dict{String,Any}. It's "type" is defined by strt. All fields of the struct is saved to the dict.

source
RANSAC.toDictMethod
toDict(a::Vector{T}) where {T<:Union{FittedShape,ShapeCandidate,ScoredShape}}

Convert a vector of shapes to a Dict{String,Any}. The top key is a "primitive", whose value is the array of the shapes. See the documentation for examples.

source