Five cameras, one matched point set per view, and triangulateMultiview refuses to start: "Expected input to be one of these types: cameraIntrinsics, cameraParameters. Instead its type was cell." The function is not asking for one calibration to cover the rig. Its interface specifies a cameraIntrinsics object or an M-element vector of them, with M equal to the number of camera poses — one object per view. This how-to builds that vector from per-camera calibrations with square-bracket concatenation, aligns the element order with your point sets and poses, and adds the checks that catch a silent mis-order before production. It is written for integrators and machine builders who drive a multi-camera capture rig from code.
What the type error means
The message lists every accepted type for the intrinsics argument: a single cameraIntrinsics object, an M-element vector of cameraIntrinsics objects, or a cameraParameters object. A cell array appears nowhere on that list, so argument validation stops the call. Nothing has been computed at that point — no points, no depths, no partial results — which is why the failure is immediate.
The M in that vector is the number of camera poses. Five cameras that each recorded one view give M = 5, so the function expects five objects, not one object reused five ways. The reference example passes a single object because every view there comes from the same camera, which is exactly the case your rig is not in.
What you need
- One calibration per camera: capture a planar checker target in each camera's view at the focus and distance production will use, and keep the returned
cameraIntrinsicsobject per camera. For a rig covering a meter-wide field, the large calibration grid guide shows how to build a target big enough to fill the frame. - The matched point sets, one per view, held in a view order you write down.
- The camera poses, one per view, in that same order.
- One physically measured 3D point inside the volume, kept for the verification step.
Build the intrinsics vector in five steps
- Calibrate each camera separately and name the objects in view order —
intrinsics1,intrinsics2, up tointrinsics5for five cameras. Each object carries its own focal length, principal point and image size; nothing is shared. - Concatenate with square brackets, not a cell:
intrinsics = [intrinsics1, intrinsics2, intrinsics3, intrinsics4, intrinsics5];. Brackets join same-class objects into one object array of typecameraIntrinsics; the cell you had built is the type named in the error. - Check the count against the interface contract: one element per camera pose. Five views, five poses, five matched point sets, five elements.
- Pass the vector as the third input, after the matched point sets and the camera poses, so element i describes view i.
- Re-run the call, then triangulate the measured point and compare the result to the measurement before the rig runs again.
Worked example: five different cameras, one object each
Inputs, in pixels because a calibration reports focal length and principal point in pixels. The first two cameras use the reference example's two-camera values; cameras 3 to 5 bring their own values in the same fields:
- Camera 1: focal length [800, 800] px, principal point (320, 240) px, image size [480, 640].
- Camera 2: focal length [600, 600] px, principal point (300, 200) px, image size [480, 640].
- Cameras 3, 4, 5: each unit's own calibration, same three fields, one object each.
Rule in words: one object per camera pose, joined with square brackets into an M-element vector, M = 5 here.
focalLength = [800, 800];
principalPoint = [320, 240];
imageSize = [480, 640];
focalLength2 = [600, 600];
principalPoint2 = [300, 200];
imageSize2 = [480, 640];
intrinsics1 = cameraIntrinsics(focalLength, principalPoint, imageSize);
intrinsics2 = cameraIntrinsics(focalLength2, principalPoint2, imageSize2);
intrinsics = [intrinsics1, intrinsics2]; % 2 elements
intrinsics = [intrinsics1, intrinsics2, intrinsics3, intrinsics4, intrinsics5]; % M = 5
Objects intrinsics3 to intrinsics5 are built the same way from each camera's own calibration and stand in here as names only.
Arithmetic, line by line:
- Element count: the two-object concatenation yields 2 elements, and each further camera adds exactly one, so 2 + 3 = 5 elements for the five-camera rig.
- Camera 1's center check: 640 / 2 = 320 and 480 / 2 = 240, so this principal point sits exactly at the image center — idealized example values; a real calibration lands close, not exact.
- Camera 2's offset: 320 − 300 = 20 px horizontally and 240 − 200 = 40 px vertically off center, which is normal for a calibrated lens. The extreme case — a principal point outside the frame — is covered in the principal-point calibration guide.
- Reuse bias: passing camera 1's object for camera 2's points scales every depth by 800 / 600 = 1.33, about 33% too far. The basis is the stereo relation in the next section.
Result: a five-element cameraIntrinsics vector, one object per camera, aligned element-for-element with the five matched point sets and five camera poses.
Decision: call the function with that vector as the third input. The pass threshold is mechanical — the argument's type must read cameraIntrinsics (a cell stops the call with the type error above) and the element count must equal the pose count. Fewer elements leave views unpaired; more leave objects with no view to describe.
Why different cameras cannot share one object
Multi-view triangulation reconstructs a 3D point from its projections in two or more images, per the standard reference definition. With two cameras the geometry reduces to:
Z is the point's depth, f the focal length in pixels, B the baseline between the cameras and d the disparity in pixels. Depth scales one-to-one with focal length, so an f that is x% wrong returns depth x% wrong. In the worked numbers, camera 1's 800 px object used for camera 2's 600 px camera gives Δf / f = (800 − 600) / 600 ≈ 33% — the bias computed above. A principal point that is off shifts the epipolar geometry sideways and tilts the reconstruction, the signature a tilted laser triangulation cloud shows after calibration drift. And a calibration made at one resolution does not survive another: binning or decimation rescales focal length and principal point, as the binning intrinsics guide works through.
Same-model rigs deserve the same treatment. Manufacturing spread moves focal length and principal point between units by an amount you can measure in minutes: calibrate two identical cameras, compare the two objects, and that measured spread is what a one-object shortcut would accept across the rig. Per-camera calibration costs one target capture per unit and removes the question. The poses that pair with these intrinsics come from extrinsic calibration — the shared-coordinate-frame method keeps every camera in one world frame.
Common mistakes that bend the reconstruction
- The cell array you started with: any packaging that produces type
cellfails before compute. Rebuild the line with square brackets and move on. - One object for all cameras. With different lenses the depth bias equals the Δf / f ratio — 33% in the worked numbers, smaller on near-identical rigs, never zero.
- Element order that follows calibration history instead of view order. The call succeeds and the geometry bends silently; the measured-point test is the only quick catch.
- Counts that disagree: five objects against four poses cannot pair, and the failure surfaces late or not at all.
- Coordinates from a resized or cropped frame: an object calibrated on [480, 640] images is invalid for point sets at any other resolution. Recalibrate at production resolution.
Cameras for a code-driven multi-view rig
Any camera you can trigger and calibrate individually fits the pattern, which is why area scan cameras dominate these rigs: one network switch feeds several GigE units and the host, and each camera's calibration lives next to its model number in your code. Short-list with the camera finder, filtering by interface and shutter first — global shutter for anything that moves.
A typical rig member: the MV-CU020-90GC combines a 2.5 MP global-shutter color sensor in 1/1.7-inch format with GigE output and a C-mount.
- Sensor format
- 1/1.7"
- Lens mount
- C-mount
- Interface
- GigE
- Resolution
- Up to 3 MP
Mixed resolutions are not an obstacle; the M-element vector exists precisely so each view contributes matched points under its own calibration. The pair below shows the spread one vector absorbs: MV-CS200-10UM, 20 MP, USB3.0, mono, IMX183, C-mount, rolling shutter, 1-inch sensor; MV-CH120-11UC, 12 MP, USB3, color, IMX304, C-mount, global shutter, 1.1-inch sensor.
- Sensor format
- 1"
- Lens mount
- C-mount
- Sensor format
- 1.1"
- Lens mount
- C-mount
When the task is depth of a whole surface rather than sparse matched points, the 3D sensors category shortens the path: a laser profiler or structured-light unit returns calibrated depth directly, with placement rules in the 3D camera working distance guide.
Before you call the function
Run the list on every code change, not once per project:
- The intrinsics argument is an object array of type
cameraIntrinsics, never typecell. - Element count = pose count = matched point-set count: five cameras mean five and five.
- Element i describes camera i, in the same order as the poses and the point sets.
- Each object's image size matches the frames its camera's points come from, at production resolution.
- One measured point triangulates to its measured coordinates, read in the world-origin convention your downstream stage expects — see choosing where Z is measured from.
To spec the rig itself, start the camera configurator at the camera step, browse the 3D vision topic page for the neighboring calibration guides, or reach us through the contact page — expert support is free with every order.
Questions
Does the type error mean my calibration values are wrong?
No. Validation runs before any triangulation, so the values are never read — the packaging is the problem. The function accepts a single cameraIntrinsics object, an M-element vector of them, or a cameraParameters object, and a cell array is none of those types. Rebuild the line with square brackets and the same values pass unchanged.
How close do identical cameras have to be before one object works for both?
Measure it instead of guessing: calibrate two identical units and compare the focal lengths and principal points of the two objects. In two-view stereo the depth error tracks the focal-length error one-to-one, so a 1% spread means a 1% depth bias on the shared unit's points. Inside your measurement tolerance, one object is defensible; outside it, calibrate each camera.
Does a moving camera need one intrinsics object or one per position?
One per pose. M equals the number of camera poses, so a static five-camera rig needs five objects, while a camera that triangulates from several of its own positions needs one entry per position, all built from that camera's calibration. Each entry still carries the same focal length, principal point and image size — the poses change, the intrinsics do not.
Can I mix a 2.5 MP camera and a 12 MP camera in one triangulation call?
Yes. The M-element vector exists so each view carries its own calibration: focal length, principal point and image size are per object, and nothing assumes the views share a sensor or resolution. Calibrate each camera at its production resolution and keep each object's image size equal to the frames its matched points come from.
Where do the cameraIntrinsics objects come from in practice?
From a per-camera calibration: capture a planar checker target in that camera's view at production focus and working distance, run your library's single-camera calibration routine, and store the returned object under the camera's view-order name. Recalibrate after any change to focus, zoom or resolution, because focal length and principal point move with them.


