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.RANSACCloudType
struct RANSACCloud{A<:AbstractArray, B<:AbstractArray, C<:AbstractArray}

A struct to wrap a point cloud. Stores the vertices, the normals, the octree of the vertices, 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.RANSACCloudMethod
RANSACCloud(vertices, normals, numofsubsets::Int)

Construct a RANSACCloud with numofsubsets number of random subsets. Vertices and normals are converted to array of SVectors.

Arguments

  • vertices: an array of vertices.
  • normals: an array of surface normals.
  • numofsubsets::Int: number of subsets.
  • force_eltype::Union{Nothing,DataType}=nothing: an element type can be forced for the normals and vertices (in practice Float64 or Float32). If not specified, the element type of the passed vertices and normals will be used.
source
RANSAC.RANSACCloudMethod
RANSACCloud(vertices, normals, subsets; force_eltype::Union{Nothing,DataType}=nothing)

Construct a RANSACCloud with the given subsets. Vertices and normals are converted to array of SVectors.

Arguments

  • vertices: an array of vertices.
  • normals: an array of surface normals.
  • subsets::Vector{Vector{Int}}: a list of indexes for each subset.
  • force_eltype::Union{Nothing,DataType}=nothing: an element type can be forced for the normals and vertices (in practice Float64 or Float32). If not specified, the element type of the passed vertices and normals will be used.
source

The above constructors copy the vertices and normals and convert them to arrays of SVectors. If you want to pass the arrays directly, without modification, you can use the following function:

RANSAC.nomodRANSACCloudFunction
nomodRANSACCloud(vertices, normals, subsets)

Construct a RANSACCloud without touching the vertices, normals and subsets. Other fields are computed.

Arguments

  • vertices: an array of vertices.
  • normals: an array of surface normals.
  • subsets::Vector{Vector{Int}}: a list of indexes for each subset.
source

Parameters

For parameters nested named tuples are used, because it's easy to construct them, change their values or extend them. Earlier Parameters.jl was used, but I could not solve the extension part, then came the named tuples. You can construct it by hand, use the exported ransacparameters() function or load from a YAML file.

Structure of the parameters

The easiest way to construct the desired named tuple is to use the ransacparameters() function.

RANSAC.ransacparametersFunction
ransacparameters(p::T=DEFAULT_PARAMETERS; kwargs...) where {T<:NamedTuple}

Construct a NamedTuple based on a previous one, defaulting to DEFAULT_PARAMETERS and override it with the kwargs. Check the docs and examples for more.

Examples

julia> p1 = ransacparameters()
(iteration = (drawN = 3, minsubsetN = 15, prob_det = 0.9),
plane = (ϵ = 0.3, α = 0.08726646259971647),
cone = (ϵ = 0.3, α = 0.08726646259971647, minconeopang = 0.03490658503988659),
cylinder = (ϵ = 0.3, α = 0.08726646259971647),
sphere = (ϵ = 0.3, α = 0.08726646259971647, sphere_par = 0.1))

julia> p2 = ransacparameters(p1; sphere=(ϵ=0.9, α=deg2rad(1),), plane=(ϵ=1.0,))
(iteration = (drawN = 3, minsubsetN = 15, prob_det = 0.9),
plane = (ϵ = 1.0, α = 0.08726646259971647),
cone = (ϵ = 0.3, α = 0.08726646259971647, minconeopang = 0.03490658503988659),
cylinder = (ϵ = 0.3, α = 0.08726646259971647),
sphere = (ϵ = 0.9, α = 0.017453292519943295, sphere_par = 0.1))
source
ransacparameters(p::AbstractArray; kwargs...)

Construct a NamedTuple for a given types of shapes using defaultparameters and override it with the kwargs. Check the docs and examples for more.

Examples

julia> p1 = ransacparameters([FittedSphere, FittedCylinder])
(iteration = (drawN = 3, minsubsetN = 15, prob_det = 0.9,
shape_types = UnionAll[FittedSphere, FittedCylinder], τ = 900, itermax = 1000,
extract_s = :nofminset, terminate_s = :nofminset),
common = (collin_threshold = 0.2, parallelthrdeg = 1.0),
sphere = (ϵ = 0.3, α = 0.08726646259971647, sphere_par = 0.02),
cylinder = (ϵ = 0.3, α = 0.08726646259971647))

julia> p2 = ransacparameters([FittedSphere, FittedCylinder], sphere=(ϵ=0.01,), cylinder=(α=0.02,))
(iteration = (drawN = 3, minsubsetN = 15, prob_det = 0.9,
shape_types = UnionAll[FittedSphere, FittedCylinder], τ = 900, itermax = 1000,
extract_s = :nofminset, terminate_s = :nofminset),
common = (collin_threshold = 0.2, parallelthrdeg = 1.0),
sphere = (ϵ = 0.01, α = 0.08726646259971647, sphere_par = 0.02),
cylinder = (ϵ = 0.3, α = 0.02))
source

As the docstring shows, you can construct a new one based on an old one, and give keyword arguments that will overwrite the old values. Note, that the key-values that are in the keyword arguments will be overwritten, not the named tuple itself (so the values not listed in the keyword argument will not change).

As you can see in the above examples, the parameter must have two fields:iteration, common and the primitive types that you want to fit (sphere, plane, etc.). Note, that p.iteration.shape_types field controls which primitives are fitted. Another important thing regarding the ransacparameter() function, that the keyword named tuple must have a trailing comma, so this is good:

p2 = ransacparameters([FittedSphere, FittedCylinder], sphere=(ϵ=0.01,))

, but the following is NOT:

p2 = ransacparameters([FittedSphere, FittedCylinder], sphere=(ϵ=0.01))

The ransacparameters() function uses the not exported default...parameters() function, whose docstrings describes which parameters control what:

RANSAC.defaultcommonparametersFunction
defaultcommonparameters()

Construct a NamedTuple with the default common parameters.

Examples

julia> defaultcommonparameters()
(common = (collin_threshold = 0.2, parallelthrdeg = 1.0),)

Implementation

This section describes the role of the common parameters.

  • collin_threshold: 3 points can be nearly collinear, in some cases they must be filtered. See the code of: fit(::Type{FittedPlane}, p, n, params).
  • parallelthrdeg: threshold for two vectos being parallel, in degrees. If abs(dot(a,b))>cosd(parallelthrdeg), a and b are considered to be parallel.
source
RANSAC.defaultiterationparametersFunction
defaultiterationparameters(shape_types)

Construct a named tuple with the default iteration parameters. shape_types is an array of FittedShapes, that controls which primitives you want to fit to the point cloud.

Examples

julia> RANSAC.defaultiterationparameters([FittedPlane])
(iteration = (drawN = 3, minsubsetN = 15, prob_det = 0.9,
shape_types = UnionAll[FittedPlane], τ = 900, itermax = 1000,
extract_s = :nofminset, terminate_s = :nofminset),)

julia> RANSAC.defaultiterationparameters([FittedPlane, FittedSphere, FittedCone])
(iteration = (drawN = 3, minsubsetN = 15, prob_det = 0.9,
shape_types = UnionAll[FittedPlane, FittedSphere, FittedCone], τ = 900, itermax = 1000,
extract_s = :nofminset, terminate_s = :nofminset),)

Implementation

  • drawN: number of points to be sampled (length of a minimal subset).
  • minsubsetN: number of minimal sets sampled in one iteration.
  • prob_det: probability of detection.
  • τ: minimal shape size.
  • itermax: maximum number of iteration.
  • shape_types: shapes that are fitted to the point cloud (array of types).
  • extract_s, terminate_s: they are for easier testing, do not delete or modify it.
source

Check RANSAC.defaultshapeparameters as well.

The defaultparameters function joins these together:

RANSAC.defaultparametersFunction
defaultparameters(shape_types::Vector{T}) where {T}

Construct a NamedTuple with the given shape types and the default parameters.

Examples

julia> defaultparameters([FittedSphere, FittedPlane])
(iteration = (drawN = 3, minsubsetN = 15, prob_det = 0.9,
shape_types = UnionAll[FittedSphere, FittedPlane], τ = 900, itermax = 1000,
extract_s = :nofminset, terminate_s = :nofminset),
common = (collin_threshold = 0.2, parallelthrdeg = 1.0),
sphere = (ϵ = 0.3, α = 0.08726646259971647, sphere_par = 0.02),
plane = (ϵ = 0.3, α = 0.08726646259971647))
source

If you wish to change the type of floating point values from the default Float64 to anything else, you can use the setfloattype function.

RANSAC.setfloattypeFunction
setfloattype(nt, T)

Convert every Real but not Integer value to type T in a NamedTuple recursively.

source

Construct by hand

As parameters are plain named tuples, one can easily construct their own. The above functions make heavy use of the merge function. Check the code, if you wish.

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).

An example file (config.yml):

plane:
  - ϵ: 0.1
  - α: 0.01

sphere:
  - ϵ: 0.2
  - α: 0.05
  # parameter in sphere fitting
  - sphere_par: 0.01

cone:
  - ϵ: 1.
  - α: 3.14
  # filter those cones, whose opening angle is less than `minconeopang` radians
  - minconeopang: 1.

iteration:
  # number of points to be sampled (length of a minimal subset)
  - drawN: 9
  # number of minimal sets sampled in one iteration
  - minsubsetN: 2
  # probability of detection
  - prob_det: 0.999
  # minimal shape size
  - τ: 10000
  # maximum number of iteration
  - itermax: 100000
  # shapes that are fitted to the point cloud
  - shape_types:
    - plane
    - sphere
    - cone

common:
  # threshold of two vectors being parallel (in degrees)
  - parallelthrdeg: 0.5
  # threshold of points being collinear
  - collin_threshold: 0.3

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

julia> readconfig("config.yml")
(iteration = (drawN = 9, minsubsetN = 2, prob_det = 0.999,
shape_types = UnionAll[FittedPlane, FittedSphere, FittedCone], τ = 10000,
itermax = 100000, extract_s = :nofminset, terminate_s = :nofminset),
common = (collin_threshold = 0.3, parallelthrdeg = 0.5),
plane = (ϵ = 0.1, α = 0.01), cone = (ϵ = 1.0, α = 3.14, minconeopang = 1.0),
cylinder = (ϵ = 0.3, α = 0.08726646259971647),
sphere = (ϵ = 0.2, α = 0.05, sphere_par = 0.01))
RANSAC.readconfigFunction
readconfig(fname; toextend=DEFAULT_PARAMETERS, shapedict=DEFAULT_SHAPE_DICT)

Read a config file to a NamedTuple. A "base" ntuple is expected, that gets overwritten/extended with the values in the config file. A Dict{String,FittedShape} dictionary is also expected, that translates the string primitive types to julia types.

Arguments

  • fname: name of the config file.
  • toextend=DEFAULT_PARAMETERS: a named tuple, that will be overwritten/extended with the values from the config file.
  • shapedict=DEFAULT_SHAPE_DICT: a dictionary that translates the string primitive names to julia types.
source
RANSAC.DEFAULT_SHAPE_DICTConstant

const DEFAULT_SHAPE_DICT = Dict("plane"=>FittedPlane, "cone"=>FittedCone, "cylinder"=>FittedCylinder, "sphere"=>FittedSphere)

source

Primitives

RANSAC.FittedPlaneType
struct FittedPlane{A<:AbstractVector,B<:AbstractVector} <: 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.

Return the extracted primitives and the time it took to run the algorithm (in seconds).

Arguments

  • pc::RANSACCloud: the point cloud.
  • params::NamedTuple: 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.

Return the extracted primitives and the time it took to run the algorithm (in seconds).

Arguments

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

The iteration returns an array of ExtractedShape:

RANSAC.ExtractedShapeType
ExtractedShape{S<:FittedShape}

Store an extraced primitive (FittedShape) and the points that belong to the shape as Vector{Int}.

Implementation

When constructing in a refit function, just call the constructor with: ExtractedShape(shape, compatible_points).

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, ExtractedShape 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, ExtractedShape or a vector of one of them.
  • indent::Int: indentation level.
source
printJSON(io::IO, s)

Print a FittedShape, ExtractedShape 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, ExtractedShape 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,ExtractedShape}}

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