3D visionHow-to

Depth camera disparity shift: set minimum range in your script

Set the disparity shift on a stereo depth camera from Python: read the depth-table group, write it back, halve the minimum range and verify the new band.

LumaSpec Vision10 min read Share on LinkedIn

On this page 10 sections
  1. 01What disparity shift moves
  2. 02Compute the band before you write code
  3. 03What you need
  4. 04Set the shift from your script
  5. 05Prove the new floor on the bench
  6. 06Common mistakes
  7. 07When shifting is the wrong fix
  8. 08Cameras for short minimum ranges
  9. 09Before you commit the setting
  10. 10Questions

A stereo depth camera refuses everything nearer than its minimum range. A part held at 180 mm comes back as zeros, holes in the point cloud, or a sheet of noise clamped to the range floor, and no exposure change moves it. The disparity-shift control moves the floor itself: on an engine with a 128-disparity search span, raising the shift from 0 to 128 halves the minimum range, and that ratio is stereo geometry rather than a vendor option. This guide gives you the numbers first, then the script: read the depth-table control group, set the shift field, write the group back, and prove the new close limit on the bench with one frame and a tape measure.

What disparity shift moves

Every depth value in a stereo pair comes from a disparity. The engine searches a window of disparity values per pixel, and depth follows the pinhole relation Z = f_px × B / d, with f_px the focal length in pixels and B the baseline. Near objects produce large disparities, so the top of the search window sets the closest measurable point. If the engine searches d_span disparities starting at 0, the floor is Z_min = f_px × B / d_span, and geometry alone sets no far limit; texture and ambient light do.

Disparity shift translates that window. A shift S makes the engine search S to S + d_span instead of 0 to d_span. The largest usable disparity becomes d_span + S, so the floor drops; the smallest becomes S, so the ceiling comes down too. At S = d_span the largest disparity doubles and the minimum range halves. The band moves nearer; it never widens.

Z = f_px × B / d Z_min(shift) = f_px × B / (d_span + shift) Z_max(shift) = f_px × B / shift (for shift > 0)

The control is not an ordinary camera property. It lives in the depth-table control group behind the SDK's advanced mode, and the group reads as read-only until advanced mode is switched on for the device. For the reverse conversion, disparity values to XYZ coordinates, see stereo disparity to depth conversion: set the depth window; for what the un-shifted floor looks like in an image, see why close objects look like noise.

Compute the band before you write code

Two numbers decide the outcome: the minimum range your camera reports at shift 0, and the engine's disparity span. Read the first from the camera's own range read-back or its viewer; get the second from the depth-table group's fields or the SDK manual. Say the read-back shows 300 mm at shift 0 and the span is 128:

f_px × B = 300 mm × 128 = 38,400 mm·px

Every band follows from that one constant:

  • Shift 64: nearest = 38,400 / (128 + 64) = 200 mm; farthest = 38,400 / 64 = 600 mm
  • Shift 128: nearest = 38,400 / (128 + 128) = 150 mm; farthest = 38,400 / 128 = 300 mm
Disparity shift Nearest measurable Farthest measurable
0 300 mm No geometry limit
64 200 mm 600 mm
128 150 mm 300 mm

Decisions fall straight out of the table. A part plane at 160-250 mm takes shift 128: the 150 mm near edge and the far edge at the old floor value cover it with margin on both ends. A part at 350-550 mm takes shift 64. A part beyond 600 mm needs no shift at all; the floor is not your problem.

Precision improves at the near edge. Relative quantization error is about one disparity over the measured disparity, δZ / Z ≈ 1 / d: at 150 mm the disparity is 256, so about 0.4%; at the shift-0 floor it is 128, so about 0.8%. In millimetres that is roughly 0.6 mm of one-disparity depth error at the halved floor against 2.3 mm at the original one. The systematic side of quantization — height overestimation on close parts — is worked through in stereo depth height overestimation from disparity quantization.

Real engines crop or round the search window, so treat the table as the model and the post-write read-back as the truth.

What you need

  • A stereo depth camera whose SDK exposes an advanced-mode or extended-controls object containing the depth-table group. Without that group, the shift is not reachable from code.
  • The SDK's Python bindings installed, with the camera enumerated on the bus.
  • Enough bandwidth to stream depth while you test; test on the same stream your production station will run.
  • A target of known distance inside the intended band: a block on a rule at 200 mm serves the example below.
  • The read-first habit from exposure work: query the camera's range from the API before you set it. The depth-table group deserves the same treatment.

Set the shift from your script

  1. Stop the stream and open the device, then wrap it in the SDK's advanced-mode object. Ordinary controls stay put; the depth table becomes writable.
  2. Read the depth-table control group into a variable. The group writes as one unit, so this read protects every other field in it.
  3. Set the disparity shift on that variable: an integer count of disparities, taken from the band table, not guessed.
  4. Write the group back, read it again, and assert the read-back equals the value you wrote.
  5. If the SDK separates applying a setting from storing it in the camera, send the store command too; if not, make the write part of every start-up, before the first frame.
# Illustrative names: your SDK's advanced-mode API has this same shape --
# an advanced-mode object, a depth-table group, and read/write calls.

device = depth_cameras()[0]                # first depth camera on the bus
advanced = advanced_mode(device)           # extended controls for this device

depth_table = advanced.read_depth_table()  # step 2: the whole group
depth_table.disparity_shift = 128          # step 3: int, window offset
advanced.write_depth_table(depth_table)    # step 4: write the group back

# step 4 continued: prove the device took it
assert advanced.read_depth_table().disparity_shift == 128

The shift takes effect on frames captured after the write; frames already in flight keep the old window. Write the group with acquisition stopped, or capture one frame after the write and check its range before you trust it.

Prove the new floor on the bench

Put the target at 200 mm — below the un-shifted floor, inside the new 150 mm to far-edge band — and capture one frame. Pixels on the target should now carry plausible depth where the un-shifted camera returned nothing. The test is one line of output:

depth_mm = capture_depth_mm(advanced)   # illustrative capture, camera units
valid = depth_mm[depth_mm > 0]          # 0 marks no measurement
print(int(valid.min()), "mm")           # prints about 200 on the target

Depth values stay in the camera's own units and scale; if your pipeline reads raw 16-bit range values, the conversion steps are in depth gray value to mm conversion for 16-bit range images. Then check validity, not just depth: the confidence or validity image says which pixels the engine trusts, and its bit flags decode into a pixel mask in a few lines. Sparse or speckled valid pixels on the close target mean the window is right but disparity quality is not — a different fault, covered in troubleshooting noisy stereo disparity output.

Common mistakes

  • Writing a group you never read. A freshly constructed depth table carries defaults in every field, and the camera takes the whole object. The symptom looks unrelated: depth settings you never touched quietly revert.
  • Choosing the shift in a viewer by eye and never asserting it in code. The next script writes 0, and the station silently loses its close range.
  • Leaving maximum shift in place when the part moves back. At shift 128 the example band's far edge sits at the old floor value; a part re-fixtured at 400 mm returns no depth at all, and the fault looks like a broken camera.
  • Trusting the frame right after a mid-stream write. The new window applies to later frames, and how much later differs per SDK; stop acquisition or verify by read-back.
  • Expecting units or scale to change. Only the measurable band moves; your depth-to-millimetre conversion stays as it was.

When shifting is the wrong fix

The band translates; it never widens. Three cases need something else:

  • Near and far in one frame. A 150 mm part against a 2 m backdrop cannot fit a band that closes at the old floor. Choose hardware whose fixed minimum range already clears the part — the 3D sensors category spans laser profilers, structured-light stereo and time-of-flight cameras with different fixed bands.
  • The part is fuzzy, dark or thin at close range. Dropout at 200 mm on foam or black rubber is a signal problem, not a range limit; work through close-range depth dropout on fuzzy surfaces and thin parts before touching the window.
  • Guidance on a moving conveyor. Depth at the wrong moment is a timing and triggering question; start from 2D or 3D camera for conveyor pick and place.

The decision rule: shift when the working distance is fixed and close; change hardware when the scene spans more than one band.

Cameras for short minimum ranges

If you are specifying hardware rather than tuning it, match the sensor class to the band first, then confirm the data sheet's minimum range clears the part. The shop's depth cameras for close-range inspection work include:

3D sensorsHikrobot MV-DPS200P-02 Projected structured-light 3D cameraMV-DPS200P-02
Type
Stereo / structured light
$8,829.00 · quotedView and quote
3D sensorsHikrobot MV-DLS300P-04 Laser galvanometer stereo cameraMV-DLS300P-04
Type
Stereo / structured light
$16,579.00 · quotedView and quote

Projected structured light covers the whole scene with a pattern in one capture; a laser galvanometer steers the measurement across the part. The 3D sensors category page collects the range, and the shop's camera finder filters it by type.

Cameras3D sensors

Laser profilers and stereo 3D cameras

170 models · from $699.00

Before you commit the setting

  • Band from your own camera. Compute f_px × B from the shift-0 read-back, not from another model's numbers; the constant differs per camera.
  • Margin at both ends. Pick the shift that keeps the part's position tolerance inside the band, near edge and far edge.
  • Read, modify, write, assert. No unwritten groups and no unverified writes in the script.
  • Cold-start test. Power-cycle, read the shift back, and re-apply at start-up if it reverted.
  • Bench proof. A target below the old floor returns depth, validity flags on it are clean, and the far end of the scene still measures where you need it.

Each item costs minutes now and hours later: an unasserted write or an untested cold start is how a tuned station silently regresses on the production floor.

Ready to spec the camera around the band instead of the workaround? Start at the camera step of the system configurator, or reach us through the contact page. The 3D topic page gathers the depth guides, including the stereo conversion arithmetic behind this one.

Questions

Does changing the disparity shift affect depth precision or the depth units?

Units and scale stay the same; only the measurable band moves. Per-disparity quantization error stays tied to disparity, so relative error is about one disparity over the measured disparity: roughly 0.4% at 150 mm and 0.8% at the shift-0 floor in the worked example, because the near edge of the band carries the largest disparity. Absolute depth error in millimetres therefore shrinks as you measure closer, from about 2.3 mm at the floor to about 0.6 mm at 150 mm.

Can I write the disparity shift while the camera is streaming?

Write the depth-table group when acquisition is stopped, or between frames, then read the value back to confirm the camera accepted it. A write during active streaming can land on a later frame or be dropped, which makes a bench measurement look flaky. If your SDK applies control writes on a frame boundary, capture one frame after the write and check the read-back before you trust any close-range data from the stream.

Why did several depth settings reset to defaults after my script ran?

The depth table is a control group: the camera takes every field in the group from the write, not only the one you changed. Writing a freshly constructed, default-filled object resets the rest. Read the current group first, assign the disparity shift on the object you read, and write that same object back. Keep one reference to it in your code so nothing constructs a second, empty group.

How much far range do I lose at a given shift?

Under the band model the far limit is f_px × B divided by the shift, using the constant you already derived: multiply the shift-0 minimum range by the disparity span, then divide by the shift. A 300 mm floor with a 128 span gives 38,400 mm·px, so shift 128 closes the band at the floor value and shift 64 at 600 mm. Read the camera's range back after the write, and use the largest shift that still covers the far end of your scene.

Does the disparity shift survive a power cycle?

That depends on the SDK. Some cameras keep advanced controls only for the current session and revert at power-up unless you send the SDK's store-to-camera command; others hold the depth table in memory once written. Read the disparity shift back after a cold start before you plan production around a halved floor, and if the value reverts, make the read-modify-write the first lines of your start-up sequence, before the first frame is captured.