Compare commits

..

5 Commits

Author SHA1 Message Date
yair
89f904cd71 align with linescan example defaults 2025-11-22 09:55:33 +02:00
yair
a81587e8c6 slow and debug works 2025-11-22 09:52:45 +02:00
yair
d2c1030354 wteak settings 2025-11-21 17:46:53 +02:00
yair
45fa4c07b4 Add brightness-deadband property to intervalometer to prevent oscillation
- Add new brightness-deadband property (default 10.0, range 0.0-50.0)
- Implements deadband/tolerance zone around target brightness
- When brightness is within ±deadband, no adjustments are made
- Prevents oscillation at fast update rates (10-100ms)
- Allows fast corrections when brightness significantly deviates
- Enables fast ramp rates without flicker/oscillation
- Updated README with detailed deadband documentation and usage examples
- Solves exposure fluctuation issue at high frame rates with fast updates
2025-11-21 13:46:19 +02:00
yair
93d8da45e6 rem prints 2025-11-21 13:40:29 +02:00
7 changed files with 242 additions and 54 deletions

View File

@@ -35,7 +35,7 @@ udpsink host=10.81.2.183 port=5000
now moving to automatic exposure (see [gstintervalometer](gst\intervalometer\gstintervalometer.c)) now moving to automatic exposure (see [gstintervalometer](gst\intervalometer\gstintervalometer.c))
```pwsh ```pwsh
uv run .\scripts\launch-ids.py ` uv run .\scripts\launch-ids.py `
--config .\ini\2456x4pix-500top-cw.ini ` --config .\ini\2456x4pix-500top-cw-extragain.ini `
--device-id 1 ` --device-id 1 `
--framerate 750 ` --framerate 750 `
--gain 52 ` --gain 52 `
@@ -49,11 +49,27 @@ uv run .\scripts\launch-ids.py `
intervalometer enabled=true camera-element=cam ` intervalometer enabled=true camera-element=cam `
ramp-rate=vslow ` ramp-rate=vslow `
update-interval=1000 ` update-interval=1000 `
gain-ma52 ` gain=52 `
log-file=timelapse.csv ! ` log-file=timelapse.csv ! `
videocrop bottom=3 ! queue ! udpsink host=10.81.2.183 port=5000 videocrop bottom=3 ! queue ! udpsink host=10.81.2.183 port=5000
``` ```
```pwsh
$env:GST_DEBUG="linescan:5";
gst-launch-1.0 idsueyesrc config-file=ini/roi-night.ini `
exposure=5.25 framerate=200 gain=42 name=cam device-id=2 ! `
intervalometer enabled=true camera-element=cam `
ramp-rate=vslow `
update-interval=1000 `
gain-max=52 `
log-file=timelapse.csv ! `
videocrop bottom=3 ! `
queue ! `
linescan direction=vertical output-size=1900 ! `
videoconvert ! autovideosink
```
#### Receive and Display #### Receive and Display
```pwsh ```pwsh
uv run .\scripts\recv_raw_rolling.py --display-fps 60 uv run .\scripts\recv_raw_rolling.py --display-fps 60

View File

@@ -73,6 +73,7 @@ make install
| Property | Type | Range | Default | Description | | Property | Type | Range | Default | Description |
|----------|------|-------|---------|-------------| |----------|------|-------|---------|-------------|
| `brightness-smoothing` | double | 0.0-1.0 | 0.1 | Temporal smoothing factor (EMA alpha) | | `brightness-smoothing` | double | 0.0-1.0 | 0.1 | Temporal smoothing factor (EMA alpha) |
| `brightness-deadband` | double | 0.0-50.0 | 10.0 | Deadband zone to prevent oscillation (0=disabled) |
### Logging ### Logging
@@ -138,6 +139,7 @@ gst-launch-1.0 \
ramp-rate=vslow \ ramp-rate=vslow \
update-interval=1000 \ update-interval=1000 \
brightness-smoothing=0.1 \ brightness-smoothing=0.1 \
brightness-deadband=10.0 \
log-file=timelapse.csv ! \ log-file=timelapse.csv ! \
videocrop bottom=3 ! queue ! videoconvert ! autovideosink videocrop bottom=3 ! queue ! videoconvert ! autovideosink
``` ```
@@ -146,6 +148,7 @@ gst-launch-1.0 \
- `ramp-rate=vslow`: 5% exposure steps per update (smooth transitions) - `ramp-rate=vslow`: 5% exposure steps per update (smooth transitions)
- `update-interval=1000`: Updates every 1 second (not too aggressive) - `update-interval=1000`: Updates every 1 second (not too aggressive)
- `brightness-smoothing=0.1`: Filters out moving objects (cars, people, birds) - `brightness-smoothing=0.1`: Filters out moving objects (cars, people, birds)
- `brightness-deadband=10.0`: Prevents oscillation by creating a stable zone
### Complete Time-Lapse Recording ### Complete Time-Lapse Recording
@@ -166,14 +169,15 @@ gst-launch-1.0 idsueyesrc name=cam framerate=1 ! \
### Exposure Control Algorithm ### Exposure Control Algorithm
The filter uses a YASS-inspired algorithm: The filter uses a YASS-inspired algorithm with deadband control:
1. **Brightness Analysis**: Calculates average brightness of each frame 1. **Brightness Analysis**: Calculates average brightness of each frame
2. **Error Calculation**: Compares to target brightness (with compensation) 2. **Deadband Check**: If brightness is within deadband zone, skip adjustments (prevents oscillation)
3. **Ramping Priority**: 3. **Error Calculation**: Compares to target brightness (with compensation)
4. **Ramping Priority**:
- When too bright: Decreases exposure first, then gain - When too bright: Decreases exposure first, then gain
- When too dark: Increases exposure first (up to max), then gain - When too dark: Increases exposure first (up to max), then gain
4. **Smooth Ramping**: Changes are gradual based on ramp-rate setting 5. **Smooth Ramping**: Changes are gradual based on ramp-rate setting
### Typical Behavior ### Typical Behavior
@@ -246,6 +250,29 @@ smoothed_brightness = (alpha × current_brightness) + ((1 - alpha) × previous_s
**Effect:** With `brightness-smoothing=0.1`, the algorithm effectively averages brightness over ~10 frames, filtering out cars, people, and birds while still tracking slow lighting trends. **Effect:** With `brightness-smoothing=0.1`, the algorithm effectively averages brightness over ~10 frames, filtering out cars, people, and birds while still tracking slow lighting trends.
### Brightness Deadband
The `brightness-deadband` property creates a tolerance zone around the target brightness where no adjustments are made. This prevents oscillation caused by continuous micro-adjustments.
**How it works:**
- When brightness is within ±deadband of target, no changes are made
- When brightness exceeds the deadband, normal adjustments resume
- Allows fast corrections when needed, prevents hunting when stable
| Value | Behavior | Best For |
|-------|----------|----------|
| 0.0 | No deadband (disabled) | Maximum responsiveness (may oscillate) |
| 5.0 | Narrow deadband | Slow update rates (>500ms) |
| **10.0** | **Standard deadband (default)** | **Fast update rates (10-100ms), prevents oscillation** |
| 20.0 | Wide deadband | Very stable, less responsive |
**Example:**
- With `target-brightness=128` and `brightness-deadband=10.0`
- No adjustments when brightness is between 118-138
- Adjustments resume when brightness < 118 or > 138
**Important:** Higher deadband = more stability but less precision. Lower deadband = more precision but potential oscillation at fast update rates.
## Tips for Best Results ## Tips for Best Results
### Dawn/Dusk Time-Lapse (Recommended Configuration) ### Dawn/Dusk Time-Lapse (Recommended Configuration)
@@ -254,6 +281,7 @@ smoothed_brightness = (alpha × current_brightness) + ((1 - alpha) × previous_s
ramp-rate: vslow (5% steps - very gradual) ramp-rate: vslow (5% steps - very gradual)
update-interval: 1000 (1 second between updates) update-interval: 1000 (1 second between updates)
brightness-smoothing: 0.1 (filter moving objects) brightness-smoothing: 0.1 (filter moving objects)
brightness-deadband: 10.0 (prevent oscillation)
exposure-min: 0.85 (or camera minimum) exposure-min: 0.85 (or camera minimum)
exposure-max: 1.24 (or 1/framerate) exposure-max: 1.24 (or 1/framerate)
gain-min: 0 (cleanest image) gain-min: 0 (cleanest image)
@@ -267,15 +295,18 @@ target-brightness: 128
- Brightness smoothing filters transient changes (cars, people) - Brightness smoothing filters transient changes (cars, people)
- Results in smooth, flicker-free time-lapse - Results in smooth, flicker-free time-lapse
### Fast Changing Conditions ### Fast Changing Conditions (with Fast Update Rates)
``` ```
ramp-rate: fast or vfast ramp-rate: fast or vfast
update-interval: 100-500 update-interval: 10-100 (very fast updates)
brightness-smoothing: 0.3-1.0 (more responsive) brightness-smoothing: 0.3-1.0 (more responsive)
brightness-deadband: 10.0-15.0 (ESSENTIAL to prevent oscillation)
compensation: Adjust to preference (-1.0 for darker, +1.0 for brighter) compensation: Adjust to preference (-1.0 for darker, +1.0 for brighter)
``` ```
**Critical:** When using fast update rates (10-100ms), `brightness-deadband` is ESSENTIAL to prevent oscillation. Without it, the algorithm will continuously overshoot and create flickering.
### Maximum Image Quality ### Maximum Image Quality
``` ```
@@ -284,13 +315,15 @@ ramp-rate: slow or vslow (smoother transitions)
update-interval: 1000-2000 update-interval: 1000-2000
``` ```
### Avoiding Flickering ### Avoiding Flickering and Oscillation
If you experience flickering or oscillation: If you experience flickering or oscillation:
1. **Increase update-interval**: Start with 1000ms 1. **Enable deadband (MOST IMPORTANT)**: Set `brightness-deadband=10.0` or higher
2. **Decrease ramp-rate**: Use `vslow` or `slow` 2. **Increase update-interval**: Start with 1000ms for slow changes, or keep at 10-100ms with deadband for fast response
3. **Enable brightness-smoothing**: Set to 0.1 or lower 3. **Decrease ramp-rate**: Use `vslow` or `slow`
4. **Check your settings**: At 50fps, 100ms updates = every 5 frames (too fast!) 4. **Enable brightness-smoothing**: Set to 0.1 or lower
**The New Solution:** With the `brightness-deadband` parameter, you can now use fast update rates (10ms) with fast ramp rates without oscillation! The deadband creates a stable zone that prevents continuous micro-adjustments.
## Troubleshooting ## Troubleshooting
@@ -300,10 +333,11 @@ If you experience flickering or oscillation:
- Ensure `enabled=true` is set - Ensure `enabled=true` is set
**Flickering or oscillating exposure:** **Flickering or oscillating exposure:**
- **Primary cause:** Update interval too fast for your frame rate - **Primary cause:** No deadband zone at fast update rates
- **Solution:** Increase `update-interval` to 1000ms - **Solution:** Set `brightness-deadband=10.0` (or higher)
- **Alternative:** Increase `update-interval` to 1000ms
- **Also try:** Set `ramp-rate=vslow` and `brightness-smoothing=0.1` - **Also try:** Set `ramp-rate=vslow` and `brightness-smoothing=0.1`
- **At 50fps:** Never use update intervals < 500ms - **New capability:** With deadband enabled, you CAN use fast update intervals (10-100ms) for rapid response without oscillation!
**Changes too fast/slow:** **Changes too fast/slow:**
- Adjust `ramp-rate` property - Adjust `ramp-rate` property
@@ -359,6 +393,18 @@ The original implementation used GObject property specs to query exposure limits
Added Exponential Moving Average (EMA) filtering to handle transient brightness changes from moving objects (cars, people, birds). This prevents exposure oscillation while maintaining responsiveness to actual lighting changes. Added Exponential Moving Average (EMA) filtering to handle transient brightness changes from moving objects (cars, people, birds). This prevents exposure oscillation while maintaining responsiveness to actual lighting changes.
### Brightness Deadband (Anti-Oscillation)
Added deadband control to prevent continuous micro-adjustments that cause oscillation. When brightness is within the deadband zone (default ±10 units), no adjustments are made. This allows:
- Fast update rates (10-100ms) without oscillation
- Rapid response when changes exceed deadband
- Stable operation at any ramp rate
**Implementation in [`gstintervalometer.c:698-707`](gst/intervalometer/gstintervalometer.c:698-707):**
- Checks absolute error against deadband before making adjustments
- Skips exposure/gain changes when within tolerance
- Allows full-speed corrections when brightness significantly deviates
## License ## License
This filter is part of gst-plugins-vision and released under the GNU Library General Public License (LGPL). This filter is part of gst-plugins-vision and released under the GNU Library General Public License (LGPL).

View File

@@ -62,7 +62,8 @@ enum
PROP_LOG_FILE, PROP_LOG_FILE,
PROP_CAMERA_ELEMENT, PROP_CAMERA_ELEMENT,
PROP_UPDATE_INTERVAL, PROP_UPDATE_INTERVAL,
PROP_BRIGHTNESS_SMOOTHING PROP_BRIGHTNESS_SMOOTHING,
PROP_BRIGHTNESS_DEADBAND
}; };
#define DEFAULT_PROP_ENABLED TRUE #define DEFAULT_PROP_ENABLED TRUE
@@ -77,6 +78,7 @@ enum
#define DEFAULT_PROP_CAMERA_ELEMENT "" #define DEFAULT_PROP_CAMERA_ELEMENT ""
#define DEFAULT_PROP_UPDATE_INTERVAL 100 /* Update every 100ms (10 Hz) */ #define DEFAULT_PROP_UPDATE_INTERVAL 100 /* Update every 100ms (10 Hz) */
#define DEFAULT_PROP_BRIGHTNESS_SMOOTHING 0.1 /* 10% new, 90% history - heavy smoothing for time-lapse */ #define DEFAULT_PROP_BRIGHTNESS_SMOOTHING 0.1 /* 10% new, 90% history - heavy smoothing for time-lapse */
#define DEFAULT_PROP_BRIGHTNESS_DEADBAND 10.0 /* ±10 brightness units deadband zone */
/* GStreamer boilerplate */ /* GStreamer boilerplate */
#define gst_intervalometer_parent_class parent_class #define gst_intervalometer_parent_class parent_class
@@ -242,6 +244,14 @@ gst_intervalometer_class_init (GstIntervalometerClass * klass)
0.0, 1.0, DEFAULT_PROP_BRIGHTNESS_SMOOTHING, 0.0, 1.0, DEFAULT_PROP_BRIGHTNESS_SMOOTHING,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BRIGHTNESS_DEADBAND,
g_param_spec_double ("brightness-deadband", "Brightness Deadband",
"Deadband zone around target brightness where no adjustments are made (0=disabled). "
"Creates a stable zone to prevent oscillation. "
"Recommended: 10.0 for fast update rates, 5.0 for slower rates, 0.0 to disable",
0.0, 50.0, DEFAULT_PROP_BRIGHTNESS_DEADBAND,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/* Set element metadata */ /* Set element metadata */
gst_element_class_add_pad_template (gstelement_class, gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&gst_intervalometer_sink_template)); gst_static_pad_template_get (&gst_intervalometer_sink_template));
@@ -278,6 +288,7 @@ gst_intervalometer_init (GstIntervalometer * filter)
filter->camera_element_name = g_strdup (DEFAULT_PROP_CAMERA_ELEMENT); filter->camera_element_name = g_strdup (DEFAULT_PROP_CAMERA_ELEMENT);
filter->update_interval = DEFAULT_PROP_UPDATE_INTERVAL; filter->update_interval = DEFAULT_PROP_UPDATE_INTERVAL;
filter->brightness_smoothing = DEFAULT_PROP_BRIGHTNESS_SMOOTHING; filter->brightness_smoothing = DEFAULT_PROP_BRIGHTNESS_SMOOTHING;
filter->brightness_deadband = DEFAULT_PROP_BRIGHTNESS_DEADBAND;
/* Initialize internal state */ /* Initialize internal state */
filter->camera_src = NULL; filter->camera_src = NULL;
@@ -350,6 +361,9 @@ gst_intervalometer_set_property (GObject * object, guint prop_id,
case PROP_BRIGHTNESS_SMOOTHING: case PROP_BRIGHTNESS_SMOOTHING:
filter->brightness_smoothing = g_value_get_double (value); filter->brightness_smoothing = g_value_get_double (value);
break; break;
case PROP_BRIGHTNESS_DEADBAND:
filter->brightness_deadband = g_value_get_double (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -399,6 +413,9 @@ gst_intervalometer_get_property (GObject * object, guint prop_id,
case PROP_BRIGHTNESS_SMOOTHING: case PROP_BRIGHTNESS_SMOOTHING:
g_value_set_double (value, filter->brightness_smoothing); g_value_set_double (value, filter->brightness_smoothing);
break; break;
case PROP_BRIGHTNESS_DEADBAND:
g_value_set_double (value, filter->brightness_deadband);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@@ -690,6 +707,7 @@ gst_intervalometer_update_camera_settings (GstIntervalometer * filter,
{ {
gdouble error, adjusted_target, exposure_range, gain_range; gdouble error, adjusted_target, exposure_range, gain_range;
gdouble ramp_multiplier; gdouble ramp_multiplier;
gdouble abs_error;
if (!filter->camera_src) { if (!filter->camera_src) {
return; return;
@@ -699,6 +717,14 @@ gst_intervalometer_update_camera_settings (GstIntervalometer * filter,
error = (filter->target_brightness - brightness) * error = (filter->target_brightness - brightness) *
pow (2.0, filter->compensation); pow (2.0, filter->compensation);
/* Check deadband zone - if enabled and brightness is within tolerance, skip adjustments */
abs_error = fabs(filter->target_brightness - brightness);
if (filter->brightness_deadband > 0.0 && abs_error < filter->brightness_deadband) {
GST_DEBUG_OBJECT (filter, "Within deadband zone (error=%.2f < %.2f), skipping adjustment",
abs_error, filter->brightness_deadband);
return;
}
/* Adjust target brightness based on error */ /* Adjust target brightness based on error */
adjusted_target = filter->target_brightness + error; adjusted_target = filter->target_brightness + error;

View File

@@ -75,6 +75,7 @@ struct _GstIntervalometer
gchar *camera_element_name; /* Name of upstream idsueyesrc element */ gchar *camera_element_name; /* Name of upstream idsueyesrc element */
guint update_interval; /* Update interval in milliseconds */ guint update_interval; /* Update interval in milliseconds */
gdouble brightness_smoothing; /* Brightness smoothing factor (0-1, 0=no smoothing) */ gdouble brightness_smoothing; /* Brightness smoothing factor (0-1, 0=no smoothing) */
gdouble brightness_deadband; /* Deadband zone to prevent oscillation (0=disabled) */
/* Internal state */ /* Internal state */
GstElement *camera_src; /* Reference to upstream camera element */ GstElement *camera_src; /* Reference to upstream camera element */

View File

@@ -64,13 +64,6 @@
# #
# Troubleshooting: # Troubleshooting:
# #
# Error: "unknown signal name: rollover"
# - Cause: Linescan plugin not found or not properly registered with GStreamer
# - Solution:
# 1. Build plugins: .\build.ps1
# 2. Ensure GST_PLUGIN_PATH environment variable includes build output directory
# 3. Check plugin was built: look for linescan.dll in build directory
# 4. Test plugin availability: gst-inspect-1.0 linescan
# #
# Error: "No such element 'linescan'" or "No such element 'idsueyesrc'" # Error: "No such element 'linescan'" or "No such element 'idsueyesrc'"
# - Cause: Custom plugins not found in GST_PLUGIN_PATH # - Cause: Custom plugins not found in GST_PLUGIN_PATH
@@ -79,10 +72,6 @@
# 2. Set GST_PLUGIN_PATH to include the build directory # 2. Set GST_PLUGIN_PATH to include the build directory
# 3. Run: gst-inspect-1.0 --print-plugin-auto-install-info to check plugins # 3. Run: gst-inspect-1.0 --print-plugin-auto-install-info to check plugins
# #
# Error: "Not all elements could be created"
# - Cause: Missing GStreamer plugins or dependencies
# - Solution: Check that all required plugins are built and GStreamer is properly installed
#
# Performance Notes: # Performance Notes:
# - Files are saved via Python/PIL in the rollover callback (only when buffer wraps) # - Files are saved via Python/PIL in the rollover callback (only when buffer wraps)
# - The display shows the live linescan accumulation # - The display shows the live linescan accumulation
@@ -169,7 +158,7 @@ def on_rollover(linescan, buffer):
# Map the buffer to get image data # Map the buffer to get image data
success, mapinfo = buffer.map(Gst.MapFlags.READ) success, mapinfo = buffer.map(Gst.MapFlags.READ)
if not success: if not success:
# print(f"[ROLLOVER ERROR] Failed to map buffer for frame {frame_counter}") print(f"[ROLLOVER ERROR] Failed to map buffer for frame {frame_counter}")
return return
try: try:
@@ -208,7 +197,7 @@ def on_rollover(linescan, buffer):
else: else:
img.save(filename, 'JPEG', quality=95) img.save(filename, 'JPEG', quality=95)
print(f"[ROLLOVER] Frame {frame_counter} saved to: {filename}") # print(f"[ROLLOVER] Frame {frame_counter} saved to: {filename}")
except Exception as e: except Exception as e:
print(f"[ROLLOVER ERROR] Failed to save frame {frame_counter}: {e}") print(f"[ROLLOVER ERROR] Failed to save frame {frame_counter}: {e}")
@@ -261,7 +250,7 @@ Examples:
parser.add_argument('--output-dir', '-o', metavar='DIR', parser.add_argument('--output-dir', '-o', metavar='DIR',
help='Output directory (default: results/<date>/<funny-name>)') help='Output directory (default: results/<date>/<funny-name>)')
parser.add_argument('--mode', '-m', choices=['day', 'night'], default='day', parser.add_argument('--mode', '-m', choices=['day', 'night'], default='day',
help='Camera mode: day (0.4ms exposure, 0 gain) or night (5.25ms exposure, 65 gain) (default: day)') help='Camera mode: day (0.2ms exposure, 0 gain) or night (5.25ms exposure, 65 gain) (default: day)')
args = parser.parse_args() args = parser.parse_args()
@@ -395,10 +384,12 @@ Examples:
# Configure intervalometer # Configure intervalometer
intervalometer.set_property("enabled", True) intervalometer.set_property("enabled", True)
intervalometer.set_property("camera-element", source) intervalometer.set_property("camera-element", "source")
intervalometer.set_property("ramp-rate", "vslow") intervalometer.set_property("ramp-rate", "slow")
intervalometer.set_property("update-interval", 1000) intervalometer.set_property("update-interval", 500)
intervalometer.set_property("gain-max", 65) intervalometer.set_property("brightness-deadband", 10.0)
intervalometer.set_property("gain-max", 82)
intervalometer.set_property("target-brightness", 80.0)
intervalometer.set_property("log-file", "timelapse.csv") intervalometer.set_property("log-file", "timelapse.csv")
# Configure videocrop # Configure videocrop

View File

@@ -1,18 +1,49 @@
# Pipeline with tee to display and save frames on rollover # Linescan Pipeline Launcher
# Simple wrapper around launch-ids.py with sensible defaults
# Run from workspace root: c:\dev\gst-plugins-vision # Run from workspace root: c:\dev\gst-plugins-vision
# Or adjust the config-file path below
param(
[Parameter(Position=0)]
[ValidateSet('day', 'night')]
[string]$Mode = 'day',
[switch]$Help
)
if ($Help) {
Write-Host @"
Linescan Pipeline Launcher
Usage: .\launch_linescan.ps1 [day|night] [-Help]
Modes:
day - Day mode: exposure=0.2ms, gain=0, framerate=200fps (default)
night - Night mode: exposure=5.25ms, gain=65, framerate=200fps
All settings are configured in launch-ids.py with these defaults:
- Config: ini/roi-night.ini
- Device ID: 1
- Intervalometer: enabled
- Ramp rate: fast
- Update interval: 500ms
- Brightness deadband: 10.0
- Gain max: 82
- Target brightness: 80
- Log file: timelapse.csv
Examples:
.\launch_linescan.ps1 # Run in day mode (default)
.\launch_linescan.ps1 day # Run in day mode (explicit)
.\launch_linescan.ps1 night # Run in night mode
.\launch_linescan.ps1 -Help # Show this help
"@
exit 0
}
$env:GST_DEBUG="linescan:5" $env:GST_DEBUG="linescan:5"
# Note: Run this from the workspace root (c:\dev\gst-plugins-vision) Write-Host "Launching linescan pipeline in $Mode mode..."
# If running from gst/linescan/, use: config-file=../../ini/roi-night.ini Write-Host ""
uv run .\scripts\launch-ids.py `
--config .\ini\2456x4pix-500top-cw-extragain.ini ` # All configuration is handled by launch-ids.py defaults
--device-id 1 ` uv run .\scripts\launch-ids.py --mode $Mode
--framerate 750 `
--gain 0 `
--gain-boost `
--exposure 0.04 `
--host 10.81.2.183 `
--port 5000 `
--control-port 5001

View File

@@ -17,6 +17,9 @@
# Basic Usage: # Basic Usage:
# uv run .\scripts\launch-ids.py # Use all defaults # uv run .\scripts\launch-ids.py # Use all defaults
# uv run .\scripts\launch-ids.py --help # Show all options # uv run .\scripts\launch-ids.py --help # Show all options
# uv run .\scripts\launch-ids.py --mode day # Day mode preset (0.2ms, 0 gain)
# uv run .\scripts\launch-ids.py --mode night # Night mode preset (5.25ms, 65 gain)
# uv run .\scripts\launch-ids.py --intervalometer # Enable auto-exposure control
# uv run .\scripts\launch-ids.py exposure 16 # Set exposure to 16ms (simplified) # uv run .\scripts\launch-ids.py exposure 16 # Set exposure to 16ms (simplified)
# uv run .\scripts\launch-ids.py framerate 30 # Set framerate to 30fps (simplified) # uv run .\scripts\launch-ids.py framerate 30 # Set framerate to 30fps (simplified)
# uv run .\scripts\launch-ids.py gain 50 # Set gain to 50 (simplified) # uv run .\scripts\launch-ids.py gain 50 # Set gain to 50 (simplified)
@@ -26,7 +29,7 @@
# uv run .\scripts\launch-ids.py --display # Enable 1/4 sized preview window # uv run .\scripts\launch-ids.py --display # Enable 1/4 sized preview window
# #
# Features: # Features:
# - Configurable video streaming (default: UDP port 5000 to 127.0.0.1) # - Configurable video streaming (default: UDP port 5000 to 10.81.2.183)
# - Optional control interface (default: UDP port 5001 on 0.0.0.0) # - Optional control interface (default: UDP port 5001 on 0.0.0.0)
# - Dynamic exposure control (0.015-30000 milliseconds, default: 10ms) # - Dynamic exposure control (0.015-30000 milliseconds, default: 10ms)
# - Auto-exposure mode support (--auto-exposure flag) # - Auto-exposure mode support (--auto-exposure flag)
@@ -510,6 +513,9 @@ def parse_arguments():
epilog=""" epilog="""
Examples: Examples:
%(prog)s # Use all defaults %(prog)s # Use all defaults
%(prog)s --mode day # Day mode (0.2ms, 0 gain)
%(prog)s --mode night # Night mode (5.25ms, 65 gain)
%(prog)s --mode day --intervalometer # Day mode with auto-exposure
%(prog)s exposure 16 # Set exposure to 16ms %(prog)s exposure 16 # Set exposure to 16ms
%(prog)s framerate 30 # Set framerate to 30fps %(prog)s framerate 30 # Set framerate to 30fps
%(prog)s gain 50 # Set gain to 50 %(prog)s gain 50 # Set gain to 50
@@ -532,12 +538,34 @@ Examples:
nargs='?', nargs='?',
help='Value to set for the property (simplified syntax)') help='Value to set for the property (simplified syntax)')
# Mode selection
mode_group = parser.add_argument_group('Mode Selection')
mode_group.add_argument(
'--mode', '-m',
choices=['day', 'night'],
default='day',
help='Camera mode preset: day (0.2ms exposure, 0 gain, 200fps) or night (5.25ms exposure, 65 gain, 200fps)'
)
mode_group.add_argument(
'--intervalometer',
dest='intervalometer',
action='store_true',
default=True,
help='Enable intervalometer for automatic exposure control (enabled by default)'
)
mode_group.add_argument(
'--no-intervalometer',
dest='intervalometer',
action='store_false',
help='Disable intervalometer'
)
# Camera configuration # Camera configuration
camera_group = parser.add_argument_group('Camera Settings') camera_group = parser.add_argument_group('Camera Settings')
camera_group.add_argument( camera_group.add_argument(
'--config', '--config-file', '--config', '--config-file',
type=str, type=str,
default='ini/100fps-10exp-2456x4pix-500top-cw-extragain.ini', default='ini/2456x4pix-500top-cw-extragain.ini',
metavar='PATH', metavar='PATH',
help='Camera configuration file path' help='Camera configuration file path'
) )
@@ -565,7 +593,7 @@ Examples:
camera_group.add_argument( camera_group.add_argument(
'--device-id', '--device-id',
type=int, type=int,
default=0, default=1,
metavar='ID', metavar='ID',
help='Device ID (system enumeration, 0 is first, 0-254)' help='Device ID (system enumeration, 0 is first, 0-254)'
) )
@@ -624,7 +652,7 @@ Examples:
network_group.add_argument( network_group.add_argument(
'--host', '--udp-host', '--host', '--udp-host',
type=str, type=str,
default='127.0.0.1', default='10.81.2.183',
metavar='IP', metavar='IP',
help='UDP streaming destination host' help='UDP streaming destination host'
) )
@@ -704,6 +732,22 @@ Examples:
elif args.property and not args.value: elif args.property and not args.value:
parser.error(f"Property '{args.property}' requires a value") parser.error(f"Property '{args.property}' requires a value")
# Handle mode presets (day is default)
if args.mode == 'day':
if args.exposure is None:
args.exposure = 0.2
if args.gain is None:
args.gain = 0
if args.framerate is None:
args.framerate = 200
elif args.mode == 'night':
if args.exposure is None:
args.exposure = 5.25
if args.gain is None:
args.gain = 65
if args.framerate is None:
args.framerate = 200
# Validation - only validate if provided # Validation - only validate if provided
if args.exposure is not None and (args.exposure < 0.015 or args.exposure > 30000): if args.exposure is not None and (args.exposure < 0.015 or args.exposure > 30000):
parser.error(f"Exposure must be between 0.015 and 30000 ms, got {args.exposure}") parser.error(f"Exposure must be between 0.015 and 30000 ms, got {args.exposure}")
@@ -795,8 +839,30 @@ if args.auto_gain:
if args.gain_boost: if args.gain_boost:
src.set_property("gain-boost", True) src.set_property("gain-boost", True)
# Video crop to remove bottom pixels (if enabled) # Build pipeline elements list
elements_to_link = [src] elements_to_link = [src]
# Add intervalometer if enabled
if args.intervalometer:
intervalometer = Gst.ElementFactory.make("intervalometer", "intervalometer")
if not intervalometer:
print("ERROR: Could not create intervalometer element")
print("Make sure the intervalometer plugin is built and in GST_PLUGIN_PATH")
exit(1)
# Configure intervalometer
intervalometer.set_property("enabled", True)
intervalometer.set_property("camera-element", "src")
intervalometer.set_property("ramp-rate", "slow")
intervalometer.set_property("update-interval", 500)
intervalometer.set_property("brightness-deadband", 10.0)
intervalometer.set_property("gain-max", 82)
intervalometer.set_property("target-brightness", 80.0)
intervalometer.set_property("log-file", "timelapse.csv")
elements_to_link.append(intervalometer)
# Video crop to remove bottom pixels (if enabled)
if args.crop_bottom > 0: if args.crop_bottom > 0:
videocrop = Gst.ElementFactory.make("videocrop", "crop") videocrop = Gst.ElementFactory.make("videocrop", "crop")
videocrop.set_property("bottom", args.crop_bottom) videocrop.set_property("bottom", args.crop_bottom)
@@ -933,6 +999,8 @@ if not args.quiet:
print("=" * 60) print("=" * 60)
print("IDS uEye Camera - Pipeline Configuration") print("IDS uEye Camera - Pipeline Configuration")
print("=" * 60) print("=" * 60)
if args.mode:
print(f"Mode: {args.mode}")
print(f"Camera config: {args.config}") print(f"Camera config: {args.config}")
print(f"Camera ID: {args.camera_id}") print(f"Camera ID: {args.camera_id}")
print(f"Device ID: {args.device_id}") print(f"Device ID: {args.device_id}")
@@ -951,6 +1019,15 @@ if not args.quiet:
print(f"Auto-exposure: {'enabled' if args.auto_exposure else '(from INI file)'}") print(f"Auto-exposure: {'enabled' if args.auto_exposure else '(from INI file)'}")
print(f"Auto-gain: {'enabled' if args.auto_gain else '(from INI file)'}") print(f"Auto-gain: {'enabled' if args.auto_gain else '(from INI file)'}")
print(f"Gain boost: {'enabled' if args.gain_boost else '(from INI file)'}") print(f"Gain boost: {'enabled' if args.gain_boost else '(from INI file)'}")
if args.intervalometer:
print()
print("Intervalometer: enabled")
print(" Ramp rate: slow")
print(" Update int: 500 ms")
print(" Deadband: 10.0")
print(" Gain max: 82")
print(" Target bright: 80")
print(" Log file: timelapse.csv")
if args.crop_bottom > 0: if args.crop_bottom > 0:
print(f"Crop bottom: {args.crop_bottom} pixels") print(f"Crop bottom: {args.crop_bottom} pixels")
else: else: