3D visionTroubleshooting

Monocular vSLAM error: fix the loop closure intrinsics type

What breaks when monocular vSLAM throws a dot-indexing type error on intrinsics at loop closure, and the fix: a cameraParameters object passed to every helper.

LumaSpec Vision9 min read Share on LinkedIn

On this page 9 sections
  1. 01What the error actually says
    1. Why the crash waits for loop closure
  2. 02Four causes, ranked
    1. Overwritten names and positional outputs
    2. Old saves and mismatched releases
  3. 03Test the variable in four lines
  4. 04The fix: one calibration object for the whole run
  5. 05Prove the object matches your camera
  6. 06If the frames come from your own rig
  7. 07When monocular is the wrong tool
  8. 08Prevent the next crash
  9. 09Questions

Your monocular vSLAM pipeline tracks cleanly through the first twenty-eight frames and dies at frame 29, the moment loop closure starts: Dot indexing is not supported for variables of this type, raised at imageSize = intrinsics.ImageSize; inside helperAddLoopConnections. Nothing is wrong with your features, your map or your loop candidates. The intrinsics variable that reaches that helper is not the cameraParameters object your calibration step produced — between calibration and the crash it was overwritten, replaced by a stale load, or never bound to the object at all. This guide shows you how to read the error, rank the four causes by probability, test the variable's real type in one line, restore one calibration object that survives the whole run, and decide when monocular SLAM is the wrong tool for the job.

What the error actually says

Dot indexing is the object.field syntax, and it only works on structs and objects. The message names the fault precisely: whatever intrinsics holds at that moment is neither. In practice it is almost always a numeric array, a hand-built struct of a different shape, or a cell wrapped around the object. The helper was written to receive the cameraParameters object that the calibration step returns — an object that carries ImageSize as a field, exactly what line 11 of helperAddLoopConnections asks for.

The attempted patch, replacing the dot access with intrinsics(ImageSize), cannot work, and the follow-up message explains why: there is no variable called ImageSize in that function's scope. ImageSize lives on the object, not beside it. The variable lost its type; the line of code did not.

Why the crash waits for loop closure

Frame 29 is not when the data went wrong — it is when the type first mattered. Frame-to-frame tracking never reads the ImageSize field, so a wrongly typed variable sails through motion estimation untouched. Loop closure is the first stage that uses the object as an object: on its way to building the image index, helperAddLoopConnections reads intrinsics.ImageSize, and that dot access is the run's first contact with the variable's real type. Whether that contact lands on frame 29 or frame 300 depends on when the first loop candidate appears; the run stops at the first contact either way.

Four causes, ranked

Rank Cause One-line test
1 The name was overwritten after calibration — a numeric array, a plain struct or a cell now holds it Print the variable's class just above the failing line
2 The calibration call's outputs were captured by position, so the name never received the cameraParameters object Read the line that creates the variable
3 The calibration came from an old MAT-file and arrives as an outdated type Check the class immediately after the load
4 Helper files and toolbox are from different releases Run the example page written for your installed release

Overwritten names and positional outputs

MATLAB assignment is last-write-wins: any later line that assigns something else to the name intrinsics replaces the object for good. The classic silent case is a multi-output call whose results were captured by position, so the name ends up holding the wrong output — an error array where the parameters object should be. Assign calibration outputs by name, one output per name, and the position mix-up cannot recur.

Old saves and mismatched releases

A MAT-file calibrated years ago can deliver a type your current code does not expect. Check the class right after loading; if it is not cameraParameters, do not adapt the example to the old type — recalibrate from the saved board images. The release problem cuts the other way too: the example has been revised release by release, so helpers copied from a newer page can expect a different variable type than an older toolbox produces. Open the monocular visual SLAM example page in the documentation for your installed release — the vendor's documentation home for the Computer Vision Toolbox lists it, and the release-specific documentation archive keeps the page as written for older releases such as R2020b — and take its helper folder as one set. This is the software half of a familiar hardware rule: keep both sides of an interface from the same generation, as with camera firmware and SDK headers.

Test the variable in four lines

  1. One line above the failing call, print the variable's class with the core class() function. It must print cameraParameters.
  2. If it prints double, struct or cell, search the script for every line that assigns the name and look at the last assignment before the call. That line is the overwrite.
  3. Compare sizes while you are there: the object's ImageSize must equal your frame size in pixels. A mismatch means the object belongs to another camera, another resolution or a cropped stream.
  4. If the class prints correctly and the size matches but the helper still fails, replace the whole helper folder with the copy that ships with your installed release's example.

If ImageSize matches and the type is right but tracking quality is poor, the values inside the object are suspect — the hand-eye calibration pose fix covers the calibration side of that problem.

The fix: one calibration object for the whole run

  1. Delete the stale variable from the workspace so no old type survives under the same name.
  2. Recreate it once: run cameraCalibration and bind its cameraParameters output to one name.
  3. Pass that same variable everywhere the pipeline expects intrinsics — into the SLAM object at start-up and into helperAddLoopConnections and any other helper that takes it. Never rebuild or reassign it mid-run.
  4. If the calibration travels in a MAT-file, load it, print its class, confirm it is cameraParameters, and only then start the run.
  5. Keep the example, its helpers and your toolbox release in step, as described above.

Prove the object matches your camera

Before rerunning, spend two minutes confirming the object really describes the camera. The calibration reports focal length in pixels; the pinhole model ties it to the lens:

fx (px) = f (mm) × image width (px) / sensor width (mm)

Illustrative camera: 2448 × 2048 frames, 3.45 µm pixels, 12 mm lens.

  • Sensor width: 2448 px × 3.45 µm = 8445.6 µm ≈ 8.45 mm
  • Expected fx: 12 mm × 2448 px / 8.45 mm = 29,376 / 8.45 ≈ 3476 px

An object that reports 3480 px sits (3480 − 3476) / 3476 ≈ 0.1 % from the prediction — a sane calibration of this camera. An object that reports a few hundred pixels, or an ImageSize that does not read back your frame dimensions, is a calibration of something else, and that object is what reached the loop-closure helper. The relation assumes square pixels and holds best near the image centre; the calibration's distortion coefficients absorb the edge behaviour. If predicted and reported focal lengths differ by tens of percent, rebuild the calibration, starting from the checkerboard calibration error fix.

If the frames come from your own rig

Most runs of this example process a prepared dataset. When you calibrate your own camera instead, the object is only as good as its frames: capture the board in a dozen to twenty poses that tilt and rotate across the whole field of view, with focus locked and exposure fixed. A global shutter keeps a hand-tilted board motion-free, so a 2.5 MP global-shutter camera such as the MV-CU020-90GC, built on a 1/1.7-inch sensor with a GigE link, is a natural fit for a desktop SLAM rig.

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
Interface
GigE
Resolution
Up to 3 MP
$309.00View partBuild a system with it

Browse the area scan cameras category, or filter the camera finder by shutter type and interface. The camera configurator pairs the sensor with a lens once you know your working distance.

When monocular is the wrong tool

A single camera reconstructs the scene up to one unknown scale factor: the shape is right, the metres are not, and loop closure corrects drift but not scale. If the task needs metric output — pick distances, drop heights, volumes — or the scene is texture-poor enough to break corner tracking, add a sensor that measures instead of infers. A projected structured-light 3D camera such as the MV-DPS200P-02 returns metric depth per frame and removes the scale question entirely. The 2D or 3D camera decision guide walks the same trade for pick and place.

3D sensorsHikrobot MV-DPS200P-02 Projected structured-light 3D cameraMV-DPS200P-02
Type
Stereo / structured light
$8,829.00 · quotedView and quote

Compare the depth families in the 3D sensors category: laser profilers for surfaces on a conveyor, stereo and structured-light cameras for scenes like these.

Prevent the next crash

Before the run:

  • Bind intrinsics once. One assignment from the calibration step, one name, no reassignment — every helper then receives the same object.
  • Assert type and size at start-up. The class must print cameraParameters and ImageSize must equal the frame size; five lines of checking is cheaper than a run lost at frame 29.

During maintenance:

  • Move helpers with the toolbox. When the release changes, re-copy the whole helper folder from the release-matched example page, not one file at a time.
  • Keep the calibration with its images. Store the MAT-file beside the board frames that produced it, so a stale object can be regenerated rather than adapted.

Across the pipeline:

  • Give every view of the same camera the same intrinsics — the multiview triangulation fix applies the same rule to camera arrays.
  • Start every test from a known state — the flaky-test fix is the same habit on the hardware side.

More fixes in this family sit on the 3D vision topic page. If you are equipping the capture side rather than debugging a dataset, work from the camera finder to the configurator, and let the calibration step — one object, one name — finish the job.

Questions

Why did the run survive 28 frames and die at frame 29?

Tracking never reads the ImageSize field, so a wrongly typed intrinsics variable passes through frame-to-frame motion estimation untouched. Loop closure is the first stage that uses the object as an object, and the reported run simply reached its first loop candidate at frame 29. The crash marks the first use of the variable, not the moment the data went wrong — a rerun with different data crashes at a different frame for the same reason.

How do I check what type the intrinsics variable really is?

Print its class one line above the failing call with the core class() function. cameraParameters is the expected answer. double means a numeric array took over the name, struct usually means a hand-built container or an old saved calibration, and cell means the object is wrapped inside a bracketed multi-output result. Any of the three confirms the variable, not the helper, is at fault.

Can I reuse a calibration MAT-file saved in an earlier release?

Only after loading it and confirming the variable arrives as a cameraParameters object in your current toolbox. If the load delivers an outdated type, do not bend the example helpers to accept it: rerun the calibration on the saved board images so the regenerated object carries the ImageSize and pixel focal length your pipeline expects.

Is anything wrong with the map points built before the crash?

Nothing is corrupted; the run stops at the first bad access and the in-memory map ends with that session. Still, rerun the whole sequence from frame 1 after fixing the variable, so every key frame, loop candidate and image index is built by one consistent pipeline instead of stitched across two runs with different variables.

When should I replace monocular vSLAM with a 3D camera?

When the output must be in metres — pick distances, drop heights, volumes — because one camera reconstructs up to an unknown scale factor that loop closure cannot fix. Texture-poor scenes and motion blur break corner tracking for the same capture. A stereo or projected structured-light 3D camera measures depth directly and sidesteps both problems.