System designTroubleshooting

ROI offset parameter ignored at stream start: fit and order

Why an OffsetX parameter set at pipeline launch is ignored, how to fit the ROI inside the sensor, and the set order that makes width, height and offset stick.

LumaSpec Vision10 min read Share on LinkedIn

On this page 8 sections
  1. 01The symptom: caps apply, offsets do not
  2. 02Check the fit before blaming software
  3. 03Three causes, tested in order
    1. 1. Width and height are written after your offset
    2. 2. The offset left the sensor and was clamped
    3. 3. The write never reached the device
  4. 04The fix: one write block, offsets last
  5. 05Moving the window while the pipeline runs
  6. 06Prevention: one source of truth, one checked block
  7. 07The decision, and the hardware behind it
  8. 08Questions

You pass OffsetX and OffsetY to your capture pipeline at launch, the stream comes up at exactly the width and height you asked for, and the image still starts at the top-left corner of the sensor. The caps worked; the offsets are the only values that went missing. This page is for engineers driving GenICam cameras through pipeline-style source elements - GStreamer source plugins are the common case - and the failure has two root causes: the element writes your offset before it writes width and height, or your ROI never fit the sensor in the first place. Below: the two inequalities that decide the fit, a worked example with the arithmetic, the write order that sticks, and the readback that proves it.

The symptom: caps apply, offsets do not

A launch line that reproduces it:

gst-launch-1.0 yoursrc cam::OffsetX=1144 cam::OffsetY=1016 ! "video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1" ! queue ! waylandsink

yoursrc stands for the source element your camera family ships. The cam:: prefix addresses a feature on the device itself; OffsetX and OffsetY are the standard GenICam names for the horizontal and vertical shift of the readout window. The pipeline reaches PLAYING without a warning, the frames are 1920 × 1080 YUY2 at 30 fps as negotiated, and a readback of OffsetX and OffsetY shows 0. What you will typically observe:

  • Frames arrive at the negotiated size, so the caps path works.
  • No error on the bus at any state change.
  • OffsetX and OffsetY read 0 while Width and Height read the values you set.
  • Adding a saved settings file makes some parameters apply and not others, differently from session to session.

That last point is the second smell. A settings file and launch properties are applied in an order the element controls, so mixing them produces half-applied parameter sets.

Check the fit before blaming software

Do the geometry first, because it decides between the two causes. A readout window must fit the active array:

OffsetX + Width <= active sensor width OffsetY + Height <= active sensor height OffsetX_max = active sensor width - Width OffsetY_max = active sensor height - Height

If a requested offset breaks an inequality, the device clamps the offset to the limit - or rejects the write outright on stricter stacks - often without a line in your log. The numbers to use are the sensor's active pixel count, not your frame size: 5472 × 3648 on an IMX183, 4000 × 3000 on an IMX226.

The MV-CS200-10GC is a 20 MP GigE camera on the IMX183 sensor, a 1-inch rolling-shutter part behind a C-mount, and it is the hardware behind the worked example below.

Area scan camerasHikrobot MV-CS200-10GC 20 MP Area Scan Camera, GigE, IMX183, Color, BasicMV-CS200-10GC
Sensor format
1"
Lens mount
C-mount
Interface
GigE
Resolution
16-25 MP
$739.00View partBuild a system with it

The IMX183 class carries a 5472 × 3648 active array with 2.4 µm pixels. Say you stream 1920 × 1080 and want the window at OffsetX = 1144 px, OffsetY = 1016 px:

  • Horizontal: 1144 + 1920 = 3064 px, inside the 5472 px limit, with 2408 px of margin.
  • Vertical: 1016 + 1080 = 2096 px, inside the 3648 px limit, with 1552 px of margin.
  • Limits: OffsetX_max = 5472 - 1920 = 3552 px; OffsetY_max = 3648 - 1080 = 2568 px.

Both requested offsets are legal, so if the image still starts at the corner, geometry is not the cause - the write order is. Now push past the edge: OffsetX = 4000 px with the same 1920 px window gives 4000 + 1920 = 5920 px against a 5472 px array. The driver clamps OffsetX to 3552 px and streams anyway, landing 448 px short of what you asked, with no error message.

Boundary values are legal. The LS-CMR-0166, a 12 MP USB3 camera, reads a 4000 × 3000 array from its IMX226 sensor, a 1/1.7-inch rolling-shutter part running 30 fps. On that array, a window of Height = 1800 px at OffsetY = 1200 px sits exactly at the edge - 1200 + 1800 = 3000 - and is accepted; 1201 px clamps back to 1200 px.

Area scan cameras12 MP USB3 area scan camera, IMX226, 1/1.7" rolling shutter, 30 fps, 4000 × 3000 resolution (LS-CMR-0166)LS-CMR-0166
Sensor format
1/1.7"
Interface
USB3
Resolution
12 MP
Shutter
Rolling
$560.00View partBuild a system with it

Three causes, tested in order

With the fit confirmed legal, three causes remain. Each test below takes seconds.

1. Width and height are written after your offset

The most common cause, and the one behind most reports of launch parameters going missing. On the way to PLAYING, the element applies its device properties and then negotiates caps; the width and height write lands after your offset write. On GenICam cameras a width or height write can reset or re-clamp the offsets - exactly why SDK code shows offsets resetting to zero after any size change, as covered in Camera ROI offsets reset to zero: set width and height first. Your launch values were applied once, then overwritten.

Test: read OffsetX immediately after the state change. A 0 with a legal fit points here.

Fix: maintainers of these source plugins treat property-versus-caps ordering as a bug and have shipped releases that apply every device feature in one pass after negotiation. Upgrade first. Until you can, write the values yourself in the order given in the next section.

2. The offset left the sensor and was clamped

The requested OffsetX plus Width exceeds the array, so the device takes the largest legal value - 3552 px in the example above - and streams anyway.

Test: the readback shows a number smaller than you set.

Fix: reduce Width or move OffsetX inside OffsetX_max, unless the clamped position is acceptable. A stricter stack refuses the write and fails before the first frame with an invalid-value error - the better failure, because you see it.

3. The write never reached the device

The property name is not the feature name the element exposes, the child prefix before the :: does not match, or another application holds the camera. GenICam names are standardized, so OffsetX and OffsetY are the exact strings on any compliant device; what an element does with unrecognized properties ranges from a bus warning you missed to a silent drop.

Test: run gst-inspect-1.0 while the camera is free and confirm the features appear under the cam:: child as read-write. If the camera is already open in a viewer or a leaked pipeline, inspection reports the features read-only.

Fix: close the other session; correct the name and prefix.

The fix: one write block, offsets last

Apply the ROI yourself, in one block, while the device is stopped - the same discipline as setting manual exposure before streaming instead of mid-negotiation:

  1. Stop acquisition. Write nothing while the pipeline is live.
  2. Set binning or decimation first; they change the coordinate space every later number lives in.
  3. Set Width.
  4. Set Height.
  5. Set OffsetX inside OffsetX_max.
  6. Set OffsetY inside OffsetY_max.
  7. Read Width, Height, OffsetX and OffsetY back and compare with what you sent; log any mismatch as a hard error. The set-and-check pattern for camera parameters in a capture loop keeps this honest session after session.
  8. Start the stream and grab one frame; confirm the region of interest sits where the offsets say.

If the launch line must carry the values, pass Width and Height as device properties alongside the offsets and keep the caps equal to them:

gst-launch-1.0 yoursrc cam::Width=1920 cam::Height=1080 cam::OffsetX=1144 cam::OffsetY=1016 ! "video/x-raw,format=YUY2,width=1920,height=1080,framerate=30/1" ! queue ! waylandsink

On a current element this applies as one consistent set. If the readback still shows 0 with a legal fit and this order, the element is applying features out of order - upgrade it rather than working around it in your code.

Moving the window while the pipeline runs

Once the caps are negotiated, Width, Height and pixel format are fixed: downstream elements sized their buffers around 1920 × 1080, so changing any of them means stopping and renegotiating. Offsets are different. Where the stack accepts writes during streaming, the window slides inside the free area around the fixed capture size - the same inequalities, evaluated live: X from 0 to 3552 px and Y from 0 to 2568 px on the example sensor. Read the value back after every move. Payload does not change, so neither does bandwidth: a 1920 × 1080 YUY2 frame is 1920 × 1080 × 2 = 4,147,200 bytes, about 4.1 MB, and 124 MB/s at 30 fps whether the window sits at 0 or at 3552 px.

If the device refuses live offset writes - some only take them while acquisition is stopped - stop the stream, write, read back, restart. When you drive the ROI from SDK or OpenCV code instead of a pipeline, the same limits apply, and the ROI write-and-persist pattern in Python shows how to keep the values alive between captures.

Prevention: one source of truth, one checked block

  • Pick one source of truth per session: launch properties or a settings file loaded at open, never both for the same features. A file is written in one pass at open, which is deterministic; duplicated entries get overwritten in an order the element chooses. If the device restores defaults at open, clear them deliberately first - the reset-before-capture pattern exists for this.
  • Put the two inequalities in your initialization code as assertions, with the sensor's active pixel count as a constant. The check is three lines and catches the 5920-against-5472 class of error before it costs an afternoon.
  • Treat the readback as part of the write: set, read, compare, log.
  • Keep binning, width, height and offsets in one function, so the order is fixed by code rather than by call sites.
What you observe Cause Deciding test Fix
Readback 0, frame size correct, fit legal Offset written before the width/height write Read OffsetX right after the state change One write block, offsets last; upgrade the element
Readback smaller than requested Clamped to the sensor edge OffsetX + Width against array width Move the window inside OffsetX_max
Pipeline fails at PAUSED, invalid value Strict stack refused an out-of-range offset Same inequality, checked before launch Correct the numbers
Inspection lists features read-only Another application holds the device Close other sessions, re-run inspection Free the device before launch
Values differ from session to session Settings file and launch properties fight Diff the file against the launch line One source of truth

The decision, and the hardware behind it

Decide like this. Compute OffsetX_max and OffsetY_max from the sensor's active array and your frame size - 3552 px and 2568 px for a 1920 × 1080 window on the 5472 × 3648 example. Choose offsets inside both limits; 1144 px and 1016 px are safe, 4000 px horizontally is not and lands at 3552 px. Write binning, then width, then height, then the offsets, in one block while the device is stopped, and prove the write with a readback before the first frame. After the stream is up, move only the offsets, and only inside the free area. If the readback still reads 0 with a legal fit and the right order, upgrade the source element - the ordering bug is fixed in current releases, not something to code around.

If you are selecting hardware for windowed capture, buy for active pixels: frame size plus travel room. The area scan cameras category filters by resolution, sensor format and interface, and the camera finder narrows to the pixel count your ROI needs; the camera configurator then pairs the sensor with a lens. More launch-order and streaming-state fixes are collected on the systems topic page.

CamerasArea scan cameras

GigE, USB3, 10GigE and CoaXPress

1,287 models · from $149.00

Questions

Why does gst-inspect list the camera features as read-only?

Inspection fully opens the device, so it can only report what the current session allows. If another application already holds the camera, nothing can write and every feature is enumerated read-only with the values that session set. Close the viewer, the stray pipeline or the leaked process holding the device, then run the inspection again; the same features list as read-write.

Can I move OffsetX and OffsetY while the stream is running?

Often, yes - inside the free area around the negotiated frame size: 0 to sensor width minus Width horizontally, 0 to sensor height minus Height vertically. Some stacks accept the write live, others only while acquisition is stopped. The test is to write, read back, and check the next frame shifted by exactly the delta you asked for; if the readback keeps the old value, stop the stream, write, and restart.

Why does binning break an offset that used to fit?

Binning shrinks the coordinate space. A 5472 × 3648 array binned 2 × 2 reports 2736 × 1824, so the horizontal limit for the same 1920 px window drops from 3552 to 816, and the old value no longer fits. Set binning before width, height and offsets, and re-check both inequalities against the binned array rather than the native one.

Should I put the ROI in a saved settings file instead of launch properties?

Pick one source of truth. A settings file is written to the device in a single pass at open, which is deterministic and easy to audit; launch properties that duplicate its entries are applied around it in an order the element chooses, which is how half-applied parameter sets happen. Ship the file or the properties for a given feature, never both.