Frame
interface Frame extends HybridObjectRepresents a single Frame the Camera "sees".
Discussion
Typically, a CameraOutput like
CameraFrameOutput produces
instances of this type.
Discussion
The Frame is either planar or non-planar (isPlanar).
- A planar Frame's (often YUV) pixel data can be accessed on
the CPU via
Frame.getPlanes(), where eachFramePlanerepresents one plane of the pixel data. In YUV, this is often 1 Y Plane in full resolution, and 1 UV Plane in half resolution. In this case,Frame.getPixelBuffer()'s behaviour is undefined - sometimes it contains the whole pixel data in a contiguous block of memory, sometimes it just contains the data in the control block. - A non-planar Frame's (often RGB) pixel data can be accessed
on the CPU via
Frame.getPixelBuffer()as one contiguous block of memory. In this case,Frame.getPlanes()will return an empty array ([]).
It is recommended to not rely on Frame.getPixelBuffer()
for planar Frames.
Discussion
Frames have to be disposed (see dispose())
to free up the memory, otherwise the producing pipeline
might stall.
Properties
bytesPerRow
readonly bytesPerRow: numberGet the number of bytes per row of the underlying pixel buffer.
This may return 0 if the Depth
is planar, in which case you should get the
number of bytes per row of individual planes
using getPlanes() /
FramePlane.bytesPerRow.
See
cameraIntrinsicMatrix?
readonly optional cameraIntrinsicMatrix: number[]Gets the Camera intrinsic matrix for this Frame.
The returned array is a 3x3 matrix with column-major ordering. Its origin is the top-left of the Frame.
K = [ fx 0 cx ]
[ 0 fy cy ]
[ 0 0 1 ]fx,fy: focal length in pixelscx,cy: principal point in pixels
Note
The Frame only has a Camera intrinsic matrix attached
if enableCameraMatrixDelivery
is set to true on the CameraFrameOutput.
Example
const matrix = frame.cameraIntrinsicMatrix
const fx = matrix[0]
const fy = matrix[4]height
readonly height: numberGets the total height of the Frame.
If this is a planar Frame (see isPlanar),
the individual planes (see getPlanes())
will likely have different heights than the total height.
For example, a YUV Frame's UV Plane is half the size of its Y Plane.
Note
If this Frame is invalid (isValid), this just returns 0.
isMirrored
readonly isMirrored: booleanIndicates whether this Frame's pixel data must be
interpreted as mirrored along the vertical axis.
This value is always relative to the target output's mirror mode.
(see CameraOutputConfiguration.mirrorMode)
Frames are not automatically mirroring their pixels because physically mirroring buffers is expensive. The Camera streams frames in the hardware's native mirroring mode and adjusts presentation later using metadata (such as EXIF), transforms, or here; a flag.
- If the output is mirrored but the underlying pixel buffer is NOT,
isMirroredwill betrue. You must treat the pixels as flipped (for example, read them right-to-left). - If both the output and the pixel buffer are mirrored
(for example when
FrameOutputOptions.enablePhysicalBufferRotationis enabled),isMirroredwill befalsebecause the buffer already matches the output orientation. - If neither the output nor the pixel buffer are mirrored,
isMirroredwill befalse.
Discussion
If you process the Frame, you must interpret the
pixel data in this Frame as mirrored if
isMirrored is true.
In rendering libraries you would typically scale the
Frame by -1 on the X axis if it is mirrored
to cancel out the mirroring at render time.
Most ML libraries (for example Google MLKit) accept a mirroring flag for
input images — pass isMirrored directly in those cases.
isPlanar
readonly isPlanar: booleanReturns whether this Frame is planar or non-planar.
- If a Frame is planar (e.g. YUV), you should only access its pixel buffer
via
getPlanes()instead ofgetPixelBuffer(). - If a Frame is non-planar (e.g. RGB), you can access its pixel buffer
via
getPixelBuffer()instead ofgetPlanes().
Note
If this Frame is invalid (isValid), this just returns false.
isValid
readonly isValid: booleanGets whether this Frame is still valid, or not.
If the Frame is invalid, you cannot access its data anymore.
A Frame is automatically invalidated via dispose().
orientation
readonly orientation: OrientationThe rotation of this Frame relative to the
CameraOutput's target output orientation. (see
CameraOutput.outputOrientation)
Frames are not automatically rotated to 'up' because physically
rotating buffers is expensive. The Camera streams frames in the
hardware's native orientation and adjusts presentation later using
metadata (such as EXIF), transforms, or here; a flag.
Examples:
'up'— The Frame is already correctly oriented.'right'— The pixel data is rotated +90° relative to the desired orientation.'left'— The pixel data is rotated -90° relative to the desired orientation.'down'— The pixel data is rotated 180° upside down relative to the desired orientation.
Discussion
If you process the Frame, you must interpret the
pixel data in this Frame to be rotated by
this orientation.
In rendering libraries you would typically counter-rotate
the Frame by this orientation to get
it "up-right".
Most ML libraries (for example Google MLKit) accept an
orientation flag for input images - pass orientation
directly in those cases.
pixelFormat
readonly pixelFormat: PixelFormatGets the PixelFormat of this Frame's pixel data.
Common formats are 'yuv-420-8-bit-video'
for native YUV Frames, 'rgb-bgra-32-bit'
for processed RGB Frames, or 'private' for
zero-copy GPU-only Frames.
Note
Frames are usually not in Depth Pixel Formats (like
'depth-32-bit') unless they
have been manually converted from a Depth Frame to
a Frame.
Discussion
If the Frame is a GPU-only buffer, its pixelFormat
is 'private', wich allows zero-copy importing it
into GPU pipelines directly, however its pixel data is likely not accessible
on the CPU.
You can use getNativeBuffer() to get a handle
to the native GPU-based buffer, which can be used in GPU pipelines like
Skia, or custom implementations using OpenGL/Vulkan/Metal shaders with
external texture importers.
timestamp
readonly timestamp: numberGets the presentation timestamp this Frame was timed at.
Note
If this Frame is invalid (isValid), this just returns 0.
width
readonly width: numberGets the total width of the Frame.
If this is a planar Frame (see isPlanar),
the individual planes (see getPlanes())
will likely have different widths than the total width.
For example, a YUV Frame's UV Plane is half the size of its Y Plane.
Note
If this Frame is invalid (isValid), this just returns 0.
Methods
convertCameraPointToFramePoint()
Converts the given cameraPoint in
camera sensor coordinates into a Point
in Frame coordinates, relative to this Frame.
Note
Camera sensor coordinates are not necessarily normalized from 0.0 to 1.0. Some implementations may have a different opaque coordinate system.
Example
const cameraPoint = { x: 0.5, y: 0.5 }
const framePoint = frame.convertCameraPointToFramePoint(cameraPoint)
console.log(framePoint) // { x: 960, y: 360 }convertFramePointToCameraPoint()
Converts the given framePoint in
Frame coordinates relative to this Frame
into a Point in camera sensor coordinates.
Note
Camera sensor coordinates are not necessarily normalized from 0.0 to 1.0. Some implementations may have a different opaque coordinate system.
Example
const framePoint = { x: frame.width / 2, y: frame.height / 2 }
const cameraPoint = frame.convertFramePointToCameraPoint(framePoint)
console.log(cameraPoint) // { x: 0.5, y: 0.5 }getNativeBuffer()
getNativeBuffer(): NativeBufferGet a NativeBuffer that points to
this Frame.
This is a shared contract between libraries to pass native buffers around without natively typed bindings.
The NativeBuffer must be released
again by its consumer via release(),
otherwise the Camera pipeline might stall.
getPixelBuffer()
getPixelBuffer(): ArrayBufferGets the Frame's entire pixel data as a full contiguous ArrayBuffer,
if it contains one.
Discussion
- If the frame is planar (see
Frame.isPlanar, e.g. YUV), this might or might not contain valid data. - If the frame is not planar (e.g. RGB), this will contain the entire pixel data.
Discussion
This does not perform a copy, but since the Frame's data is stored
on the GPU, it might lazily perform a GPU -> CPU download.
Discussion
Once the Frame gets invalidated (isValid == false),
this ArrayBuffer is no longer safe to access.
Throws
If this Frame is invalid (isValid).
getPlanes()
getPlanes(): FramePlane[]Returns each plane of a planar Frame (see isPlanar).
If this Frame is non-planar, this method returns an empty array ([]).
Throws
If this Frame is invalid (isValid).