Quiddity.Frame (ExQuiddity v0.1.2)

Copy Markdown View Source

One camera frame of a part: what each pixel landed on.

Four answers per pixel, all of them exact, because the ray met the surface rather than a depth buffer that then had to be un-projected:

  • the point on the part (millimetres, the file's own coordinates);
  • the outward normal there — and for a pixel that met nothing, the ray stopped at the far plane and the normal points back at the sensor, which is what "a surface would face me here" means;
  • the face that claimed it (nil for a pixel that met nothing);
  • the distance along the ray.

Points and normals are kept as the flat binaries the engine answered; a caller asks for rows/1 when it wants lists, and pays for the walk then rather than on every frame.

Summary

Functions

The centre pixel's row: {point, normal, face}.

The share of pixels that met the part, from 0 to 1.

The distance to the nearest and furthest pixel that met the part.

Every pixel's distance along its ray, in millimetres.

Every pixel's face, and nil where the ray met nothing.

The families present in the frame, and how many pixels each covers.

How many pixels met the part.

The frame built from what the engine answered.

Every pixel's outward normal.

The pixel's point, in millimetres.

Every pixel's point, in millimetres.

A shaded image of the frame, [r, g, b, a] per pixel, 0 to 255.

One {[x, y, z], [nx, ny, nz], face} row per pixel, row-major.

The rows a sensor observation carries: [x, y, z, semantic_id] per pixel.

Types

t()

@type t() :: %Quiddity.Frame{
  camera: Quiddity.Camera.t(),
  depth: binary(),
  faces: binary(),
  height: pos_integer(),
  normals: binary(),
  points: binary(),
  width: pos_integer()
}

Functions

centre(frame)

@spec centre(t()) :: {[float()], [float()], non_neg_integer() | nil}

The centre pixel's row: {point, normal, face}.

A patch sensor reads its location from the middle pixel, which is why this is the one pixel with a name.

coverage(frame)

@spec coverage(t()) :: float()

The share of pixels that met the part, from 0 to 1.

depth_range(frame)

@spec depth_range(t()) :: {float() | :none, float() | :none}

The distance to the nearest and furthest pixel that met the part.

{:none, :none} for a frame that met nothing, which is a different answer from a frame whose surface is at zero distance.

depths(frame)

@spec depths(t()) :: [float()]

Every pixel's distance along its ray, in millimetres.

face_ids(frame)

@spec face_ids(t()) :: [non_neg_integer() | nil]

Every pixel's face, and nil where the ray met nothing.

families(frame, recognition)

@spec families(t(), Quiddity.Recognition.t()) :: [
  {String.t() | :body | :unexplained, pos_integer()}
]

The families present in the frame, and how many pixels each covers.

This is the answer to "what am I looking at": the frame's faces are looked up in the part's recognition, so a patch on a counterbore says so, and a patch on plain stock says nothing at all.

hits(frame)

@spec hits(t()) :: non_neg_integer()

How many pixels met the part.

new(map, camera)

@spec new(map(), Quiddity.Camera.t()) :: t()

The frame built from what the engine answered.

points, normals and faces are the engine's own binaries: 3 * w * h little-endian f64, 3 * w * h f64, w * h u32 — and depth, w * h f64.

normals(frame)

@spec normals(t()) :: [[float()]]

Every pixel's outward normal.

point(frame, index)

@spec point(t(), non_neg_integer()) :: [float()]

The pixel's point, in millimetres.

points(frame)

@spec points(t()) :: [[float()]]

Every pixel's point, in millimetres.

rgba(frame, options \\ [])

@spec rgba(t(), keyword()) :: [[non_neg_integer()]]

A shaded image of the frame, [r, g, b, a] per pixel, 0 to 255.

A B-rep has no colour: quiddity reads geometry, and a STEP file that carries no appearance is grey. So this is shading — one light from the upper left of the camera — and it is honest about what it is: a hsv feature read from these pixels says how the surface turns, not what it is painted.

rows(frame)

@spec rows(t()) :: [{[float()], [float()], non_neg_integer() | nil}]

One {[x, y, z], [nx, ny, nz], face} row per pixel, row-major.

semantic_3d(frame, options \\ [])

@spec semantic_3d(t(), keyword()) :: [[float()]]

The rows a sensor observation carries: [x, y, z, semantic_id] per pixel.

A pixel that met the part carries semantic_id; a pixel that met nothing carries 0.0, the background id. A sensor model reads that fourth column to decide which pixels are on the object, and what fraction of the patch it covers.