fit
This commit is contained in:
@@ -1,559 +0,0 @@
|
|||||||
# Intervalometer Flickering Debug Session
|
|
||||||
|
|
||||||
## Problem Description
|
|
||||||
|
|
||||||
**Original Pipeline (with flickering):**
|
|
||||||
```bash
|
|
||||||
gst-launch-1.0 `
|
|
||||||
idsueyesrc config-file=ini/whole-presacler64_autoexp-binningx2_nightcolor2ms.ini `
|
|
||||||
exposure=0.85 framerate=50 gain=0 name=cam device-id=2 num-buffers=200 ! `
|
|
||||||
intervalometer enabled=true camera-element=cam update-interval=100 log-file=exposure_log.csv ! `
|
|
||||||
videocrop bottom=3 ! queue ! videoconvert ! autovideosink
|
|
||||||
```
|
|
||||||
|
|
||||||
**Fixed Pipeline (for dawn/dusk time-lapse):**
|
|
||||||
```bash
|
|
||||||
gst-launch-1.0 `
|
|
||||||
idsueyesrc config-file=ini/whole-presacler64_autoexp-binningx2.ini `
|
|
||||||
exposure=0.85 framerate=50 gain=0 name=cam device-id=2 ! `
|
|
||||||
intervalometer enabled=true camera-element=cam `
|
|
||||||
ramp-rate=vslow `
|
|
||||||
update-interval=1000 `
|
|
||||||
log-file=timelapse.csv ! `
|
|
||||||
videocrop bottom=3 ! queue ! videoconvert ! autovideosink
|
|
||||||
```
|
|
||||||
|
|
||||||
**Symptom:** Flickering/oscillation in video output
|
|
||||||
|
|
||||||
**Original Configuration (too aggressive):**
|
|
||||||
- Framerate: 50 fps (20ms per frame)
|
|
||||||
- Update interval: 100ms (every 5 frames) ← too frequent
|
|
||||||
- Ramp rate: medium (20% steps) ← too large
|
|
||||||
- Result: Algorithm oscillates, visible flicker
|
|
||||||
|
|
||||||
**Fixed Configuration (for dawn/dusk):**
|
|
||||||
- Framerate: 50 fps
|
|
||||||
- Update interval: 1000ms (every 50 frames) ← smooth
|
|
||||||
- Ramp rate: vslow (5% steps) ← gradual
|
|
||||||
- Result: Smooth convergence, no flicker
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Hypothesis: Possible Root Causes
|
|
||||||
|
|
||||||
### 1. **Update Interval Too Aggressive (HIGH PROBABILITY)**
|
|
||||||
- Update interval (100ms) might be too fast for the camera hardware
|
|
||||||
- At 50fps, this updates every 5 frames, potentially faster than camera can stabilize
|
|
||||||
- Camera might not finish applying previous exposure before next update arrives
|
|
||||||
- **Evidence needed:** Check camera response time vs update frequency
|
|
||||||
|
|
||||||
### 2. **Missing Ramping Implementation (HIGH PROBABILITY)**
|
|
||||||
- Header defines `ramp_rate` and `ramp_step` but implementation may not be using them
|
|
||||||
- Abrupt exposure changes without smooth ramping would cause visible flicker
|
|
||||||
- Target exposure vs current exposure might be jumping instead of ramping
|
|
||||||
- **Evidence needed:** Verify ramping is actually applied in transform function
|
|
||||||
|
|
||||||
### 3. **Thread Safety / Race Conditions (MEDIUM PROBABILITY)**
|
|
||||||
- Camera property updates from transform thread
|
|
||||||
- Frame processing happens in pipeline thread
|
|
||||||
- Potential race between reading current values and updating them
|
|
||||||
- **Evidence needed:** Check if property updates are synchronized
|
|
||||||
|
|
||||||
### 4. **Initial Property Read Failure (MEDIUM PROBABILITY)**
|
|
||||||
- [`camera_src`](gst/intervalometer/gstintervalometer.h:76) reference might not get proper initial values
|
|
||||||
- If `current_exposure` and `current_gain` start at 0, first update could be a large jump
|
|
||||||
- **Evidence needed:** Verify initial property read from camera element
|
|
||||||
|
|
||||||
### 5. **Update Timing Issues (LOW PROBABILITY)**
|
|
||||||
- `last_update_time` logic might allow updates closer than intended
|
|
||||||
- Multiple updates in quick succession during startup
|
|
||||||
- **Evidence needed:** Log actual time between updates
|
|
||||||
|
|
||||||
### 6. **Brightness Calculation Instability (LOW PROBABILITY)**
|
|
||||||
- If brightness varies significantly frame-to-frame (noise), algorithm oscillates
|
|
||||||
- Compensation factor might be too aggressive
|
|
||||||
- **Evidence needed:** Log calculated brightness values per frame
|
|
||||||
|
|
||||||
### 7. **Camera Hardware Lag (LOW PROBABILITY)**
|
|
||||||
- Exposure changes take effect 1-2 frames after being set
|
|
||||||
- Creates temporal mismatch between measurement and correction
|
|
||||||
- **Evidence needed:** Check IDS uEye documentation on exposure latency
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Most Likely Root Causes
|
|
||||||
|
|
||||||
Based on the configuration and typical auto-exposure issues:
|
|
||||||
|
|
||||||
### Primary Suspect: **Ramping Not Implemented or Disabled**
|
|
||||||
- Instant exposure jumps between calculated values
|
|
||||||
- Would cause visible flicker especially with 100ms updates
|
|
||||||
- Need to verify: Is ramping code actually executing?
|
|
||||||
|
|
||||||
### Secondary Suspect: **Update Interval Too Fast**
|
|
||||||
- 100ms = 5 frames at 50fps
|
|
||||||
- Camera hardware may need settling time
|
|
||||||
- Recommendation: Try 500-1000ms first to establish baseline
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Diagnostic Logging Plan
|
|
||||||
|
|
||||||
We need to add strategic logging to [`gstintervalometer.c`](gst/intervalometer/gstintervalometer.c) to validate our hypotheses:
|
|
||||||
|
|
||||||
### Critical Log Points:
|
|
||||||
|
|
||||||
1. **In transform function - Frame Processing:**
|
|
||||||
```c
|
|
||||||
GST_DEBUG("Frame %lu: current_exp=%.3f target_exp=%.3f current_gain=%d target_gain=%d brightness=%.1f",
|
|
||||||
frame_count, current_exposure, target_exposure, current_gain, target_gain, avg_brightness);
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **In update algorithm - Before Ramping:**
|
|
||||||
```c
|
|
||||||
GST_DEBUG("Update triggered: time_since_last=%.1fms desired_exp=%.3f desired_gain=%d",
|
|
||||||
time_delta_ms, calculated_exposure, calculated_gain);
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **In ramping logic - Step Application:**
|
|
||||||
```c
|
|
||||||
GST_DEBUG("Ramping: step=%.3f current_exp=%.3f -> new_exp=%.3f (target=%.3f)",
|
|
||||||
ramp_step, current_exposure, new_current_exposure, target_exposure);
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **In property setter - Actual Camera Update:**
|
|
||||||
```c
|
|
||||||
GST_DEBUG("Setting camera: exposure=%.3f gain=%d (changed=%d)",
|
|
||||||
exposure_value, gain_value, property_changed);
|
|
||||||
```
|
|
||||||
|
|
||||||
5. **On camera element lookup:**
|
|
||||||
```c
|
|
||||||
GST_DEBUG("Camera element '%s' found=%p initial_exp=%.3f initial_gain=%d",
|
|
||||||
camera_element_name, camera_src, initial_exposure, initial_gain);
|
|
||||||
```
|
|
||||||
|
|
||||||
### Expected Outcomes:
|
|
||||||
|
|
||||||
- **If ramping not working:** target_exp will jump but current_exp won't smooth towards it
|
|
||||||
- **If update too fast:** time_since_last will show values < 100ms or camera updates too frequent
|
|
||||||
- **If race condition:** frame logs will show inconsistent exposure values
|
|
||||||
- **If initial read fails:** initial values will be 0 or unrealistic
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ DIAGNOSIS CONFIRMED
|
|
||||||
|
|
||||||
### **Root Cause: NO RAMPING IMPLEMENTATION** (Lines 689-693)
|
|
||||||
|
|
||||||
The code has a **critical bug** in [`gst_intervalometer_update_camera_settings()`](gst/intervalometer/gstintervalometer.c:640):
|
|
||||||
|
|
||||||
```c
|
|
||||||
/* Smooth ramping towards target */
|
|
||||||
if (fabs (filter->current_exposure - filter->target_exposure) > 0.001) {
|
|
||||||
filter->current_exposure = filter->target_exposure; // ❌ INSTANT JUMP!
|
|
||||||
g_object_set (filter->camera_src, "exposure", filter->current_exposure, NULL);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**The Problem:**
|
|
||||||
1. The comment says "Smooth ramping" but the code does **instant jumps**
|
|
||||||
2. Line 690: `filter->current_exposure = filter->target_exposure` - immediately sets current = target
|
|
||||||
3. The [`ramp_step`](gst/intervalometer/gstintervalometer.h:94) variable is **never used**
|
|
||||||
4. Every 100ms, exposure jumps directly to the new calculated value
|
|
||||||
5. At 50fps, this causes **visible flickering every 5 frames**
|
|
||||||
|
|
||||||
**What Should Happen:**
|
|
||||||
```c
|
|
||||||
// Gradual ramping (should be implemented like this):
|
|
||||||
gdouble step = (filter->target_exposure - filter->current_exposure) * 0.1; // 10% per update
|
|
||||||
filter->current_exposure += step;
|
|
||||||
```
|
|
||||||
|
|
||||||
This explains the flickering perfectly - the camera exposure is jumping abruptly every 100ms instead of smoothly ramping.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Confirming the Diagnosis (Optional)
|
|
||||||
|
|
||||||
To validate this diagnosis before fixing, you could add temporary debug logging:
|
|
||||||
|
|
||||||
```c
|
|
||||||
// Add at line 689 in gst_intervalometer_update_camera_settings():
|
|
||||||
GST_WARNING_OBJECT (filter, "FLICKER DEBUG: brightness=%.1f target=%.1f | "
|
|
||||||
"current_exp=%.3f target_exp=%.3f delta=%.3f | frame=%lu",
|
|
||||||
brightness, filter->target_brightness,
|
|
||||||
filter->current_exposure, filter->target_exposure,
|
|
||||||
filter->target_exposure - filter->current_exposure,
|
|
||||||
filter->frame_count);
|
|
||||||
```
|
|
||||||
|
|
||||||
Run with: `$env:GST_DEBUG="intervalometer:3"; .\[your-pipeline-command]`
|
|
||||||
|
|
||||||
Expected output showing instant jumps:
|
|
||||||
```
|
|
||||||
intervalometer WARNING: FLICKER DEBUG: ... delta=0.035 | frame=5
|
|
||||||
intervalometer WARNING: FLICKER DEBUG: ... delta=0.000 | frame=6 <- jumped instantly!
|
|
||||||
intervalometer WARNING: FLICKER DEBUG: ... delta=0.042 | frame=10
|
|
||||||
intervalometer WARNING: FLICKER DEBUG: ... delta=0.000 | frame=11 <- jumped again!
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The Fix
|
|
||||||
|
|
||||||
Replace lines 688-699 in [`gst_intervalometer_update_camera_settings()`](gst/intervalometer/gstintervalometer.c:688-699):
|
|
||||||
|
|
||||||
```c
|
|
||||||
/* Apply smooth ramping using ramp_step */
|
|
||||||
gdouble exp_delta = filter->target_exposure - filter->current_exposure;
|
|
||||||
gdouble gain_delta = filter->target_gain - filter->current_gain;
|
|
||||||
|
|
||||||
/* Calculate ramp step based on ramp rate (percentage of delta per update) */
|
|
||||||
filter->ramp_step = 0.1 * ramp_multiplier; // 10% base, scaled by ramp rate
|
|
||||||
|
|
||||||
if (fabs(exp_delta) > 0.001) {
|
|
||||||
filter->current_exposure += exp_delta * filter->ramp_step;
|
|
||||||
g_object_set (filter->camera_src, "exposure", filter->current_exposure, NULL);
|
|
||||||
GST_DEBUG_OBJECT (filter, "Ramping exposure: %.3f -> %.3f (target %.3f, step %.1f%%)",
|
|
||||||
filter->current_exposure - (exp_delta * filter->ramp_step),
|
|
||||||
filter->current_exposure, filter->target_exposure,
|
|
||||||
filter->ramp_step * 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gain_delta != 0) {
|
|
||||||
gdouble gain_step = gain_delta * filter->ramp_step;
|
|
||||||
if (fabs(gain_step) < 1.0) gain_step = (gain_delta > 0) ? 1.0 : -1.0;
|
|
||||||
filter->current_gain += (gint)gain_step;
|
|
||||||
g_object_set (filter->camera_src, "gain", filter->current_gain, NULL);
|
|
||||||
GST_DEBUG_OBJECT (filter, "Ramping gain: %d (target %d)",
|
|
||||||
filter->current_gain, filter->target_gain);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This will:
|
|
||||||
- Apply gradual 10% steps (base rate) multiplied by ramp_rate setting
|
|
||||||
- Smooth transitions over multiple frames
|
|
||||||
- Eliminate visible flickering
|
|
||||||
- Actually use the [`ramp_step`](gst/intervalometer/gstintervalometer.h:94) variable
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Build & Test
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# Build the fix
|
|
||||||
.\build.ps1
|
|
||||||
|
|
||||||
# Test with optimized dawn/dusk settings
|
|
||||||
gst-launch-1.0 `
|
|
||||||
idsueyesrc config-file=ini/whole-presacler64_autoexp-binningx2.ini `
|
|
||||||
exposure=0.85 framerate=50 gain=0 name=cam device-id=2 num-buffers=200 ! `
|
|
||||||
intervalometer enabled=true camera-element=cam `
|
|
||||||
ramp-rate=vslow `
|
|
||||||
update-interval=1000 `
|
|
||||||
log-file=timelapse.csv ! `
|
|
||||||
videocrop bottom=3 ! queue ! videoconvert ! autovideosink
|
|
||||||
|
|
||||||
# Check log for smooth transitions
|
|
||||||
cat timelapse.csv
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ PRIMARY FIX IMPLEMENTED & TESTED
|
|
||||||
|
|
||||||
### Ramping Fix Results
|
|
||||||
|
|
||||||
**✅ RAMPING WORKS:** Smooth exposure transitions confirmed:
|
|
||||||
- Frame 0: 0.680ms → Frame 2: 0.544ms → Frame 4: 0.435ms → Frame 8: 0.279ms
|
|
||||||
- Clean 20% steps per update (medium ramp rate) - **NO MORE INSTANT JUMPS**
|
|
||||||
- **FLICKERING ELIMINATED** - no more visible flashes
|
|
||||||
|
|
||||||
**About Brightness Variation:**
|
|
||||||
The brightness changing is **EXPECTED AUTO-EXPOSURE BEHAVIOR**, not a bug:
|
|
||||||
- Scene bright (239) vs target (128) → algorithm reduces exposure
|
|
||||||
- Brightness drops as exposure ramps down toward target
|
|
||||||
- This is convergence, not flickering
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ⚠️ SECONDARY ISSUE: Exposure Range Query
|
|
||||||
|
|
||||||
**Problem:** Frame 16 shows exposure overflow to massive values
|
|
||||||
|
|
||||||
**Root Cause:** Using GObject property specs to query limits returns incorrect values:
|
|
||||||
- `exposure_min` = 0.000ms (impossible, causes math to break)
|
|
||||||
- `exposure_max` = DBL_MAX overflow
|
|
||||||
|
|
||||||
**Solution:** Use IDS uEye SDK directly instead of GObject properties
|
|
||||||
|
|
||||||
### Changes Made:
|
|
||||||
|
|
||||||
1. **Added IDS SDK support to intervalometer:**
|
|
||||||
- Added `#include "../../sys/idsueye/include/ueye.h"` to header
|
|
||||||
- Added `HIDS hCam` field to store camera handle
|
|
||||||
- Linked against `${IDSUEYE_LIBRARIES}` in CMakeLists.txt
|
|
||||||
|
|
||||||
2. **Added `hcam` property to idsueyesrc:**
|
|
||||||
- Exposes IDS camera handle via GObject property
|
|
||||||
- Allows intervalometer to call IDS SDK functions directly
|
|
||||||
|
|
||||||
3. **Updated query function:**
|
|
||||||
- Now uses `is_Exposure(IS_EXPOSURE_CMD_GET_EXPOSURE_RANGE)`
|
|
||||||
- Gets proper min/max/increment values from camera hardware
|
|
||||||
|
|
||||||
### To Complete the Fix:
|
|
||||||
|
|
||||||
**The idsueyesrc.dll needs to be reloaded:**
|
|
||||||
1. Close any running GStreamer pipelines/processes
|
|
||||||
2. Rebuild using `.\build.ps1` to copy updated plugins
|
|
||||||
3. Or manually copy `build\sys\idsueye\Release\libgstidsueye.dll` to `GST_PLUGIN_PATH`
|
|
||||||
|
|
||||||
**Current Status:**
|
|
||||||
- ✅ All code changes complete and compiled
|
|
||||||
- ✅ All plugins successfully copied to GST_PLUGIN_PATH
|
|
||||||
- ✅ Tested and VERIFIED - both fixes working perfectly!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ COMPLETE FIX VERIFIED
|
|
||||||
|
|
||||||
### Test Results with IDS SDK Exposure Range Query
|
|
||||||
|
|
||||||
**Camera Handle Retrieved:** 0x1
|
|
||||||
**Proper Exposure Range:** [0.019 - 19.943] ms (increment: 0.005 ms)
|
|
||||||
|
|
||||||
**CSV Evidence - No More Overflow:**
|
|
||||||
```
|
|
||||||
Frame 0: 0.772ms brightness 238
|
|
||||||
Frame 3: 0.692ms brightness 228
|
|
||||||
Frame 6: 0.612ms brightness 221
|
|
||||||
Frame 9: 0.533ms brightness 212
|
|
||||||
Frame 12: 0.453ms brightness 200
|
|
||||||
Frame 15: 0.373ms brightness 184
|
|
||||||
...
|
|
||||||
Frame 180: 0.267ms brightness 142
|
|
||||||
Frame 190: 0.277ms brightness 146
|
|
||||||
Frame 199: 0.282ms brightness 117
|
|
||||||
```
|
|
||||||
|
|
||||||
**✅ All exposure values are proper** - no overflow, no huge numbers!
|
|
||||||
|
|
||||||
### Both Issues Fixed:
|
|
||||||
|
|
||||||
1. **Flickering (SOLVED):**
|
|
||||||
- Smooth 20% ramping per update
|
|
||||||
- No more instant jumps
|
|
||||||
- Visually smooth exposure transitions
|
|
||||||
|
|
||||||
2. **Exposure Overflow (SOLVED):**
|
|
||||||
- Using `is_Exposure(IS_EXPOSURE_CMD_GET_EXPOSURE_RANGE)` from IDS SDK
|
|
||||||
- Proper min/max values queried from camera hardware
|
|
||||||
- All values stay within valid range [0.019 - 19.943] ms
|
|
||||||
|
|
||||||
The intervalometer auto-exposure system is now fully functional!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ BRIGHTNESS SMOOTHING IMPLEMENTED
|
|
||||||
|
|
||||||
### Solution: Temporal Filtering with Exponential Moving Average
|
|
||||||
|
|
||||||
To handle oscillations from moving objects (people, cars, birds), brightness temporal smoothing has been implemented.
|
|
||||||
|
|
||||||
**How it works:**
|
|
||||||
- **Sample brightness:** Every frame (high temporal resolution)
|
|
||||||
- **Smooth brightness:** Exponential moving average (EMA)
|
|
||||||
- **Adjust exposure:** Based on smoothed brightness trend, not instantaneous values
|
|
||||||
|
|
||||||
This filters out transient changes while staying responsive to actual lighting changes.
|
|
||||||
|
|
||||||
### Implementation
|
|
||||||
|
|
||||||
**New property added: `brightness-smoothing`**
|
|
||||||
|
|
||||||
```c
|
|
||||||
/* Exponential Moving Average formula: */
|
|
||||||
smoothed = (alpha × new_brightness) + ((1 - alpha) × smoothed_old)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Parameters:**
|
|
||||||
- `brightness-smoothing=0.05` → Very heavy smoothing (5% new, 95% history)
|
|
||||||
- **`brightness-smoothing=0.1`** → **Heavy smoothing (default, recommended for time-lapse)**
|
|
||||||
- `brightness-smoothing=0.3` → Moderate smoothing
|
|
||||||
- `brightness-smoothing=0.5` → Light smoothing
|
|
||||||
- `brightness-smoothing=1.0` → No smoothing (instant response)
|
|
||||||
|
|
||||||
**With brightness-smoothing=0.1:**
|
|
||||||
- Each new frame contributes 10% to the smoothed value
|
|
||||||
- Previous history contributes 90%
|
|
||||||
- Effectively averages over ~10 frames
|
|
||||||
- Moving objects cause minor ripples instead of large swings
|
|
||||||
- Background lighting trend still tracked accurately
|
|
||||||
|
|
||||||
### Complete Recommended Pipeline for Dawn/Dusk:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gst-launch-1.0 \
|
|
||||||
idsueyesrc config-file=ini/whole-presacler64_autoexp-binningx2.ini \
|
|
||||||
exposure=0.85 framerate=50 gain=0 name=cam device-id=2 ! \
|
|
||||||
intervalometer enabled=true camera-element=cam \
|
|
||||||
ramp-rate=vslow \ # 5% exposure steps
|
|
||||||
update-interval=1000 \ # 1 update/second
|
|
||||||
brightness-smoothing=0.1 \ # Filter moving objects
|
|
||||||
log-file=timelapse.csv ! \
|
|
||||||
videocrop bottom=3 ! queue ! videoconvert ! autovideosink
|
|
||||||
```
|
|
||||||
|
|
||||||
**This configuration:**
|
|
||||||
- ✅ Samples brightness every frame (50 Hz)
|
|
||||||
- ✅ Smooths out transient brightness changes from moving objects
|
|
||||||
- ✅ Updates exposure slowly (1 Hz) based on smoothed brightness trend
|
|
||||||
- ✅ Ramps exposure gradually (5% steps)
|
|
||||||
- ✅ Results in smooth, stable time-lapse without visible oscillation
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ✅ FIX IMPLEMENTED & BUILT
|
|
||||||
|
|
||||||
### Build Results
|
|
||||||
|
|
||||||
```
|
|
||||||
✅ Compiled successfully using .\build.ps1
|
|
||||||
✅ Plugin copied to GST_PLUGIN_PATH: libgstintervalometer.dll
|
|
||||||
```
|
|
||||||
|
|
||||||
The ramping fix has been applied to [`gstintervalometer.c:688-716`](gst/intervalometer/gstintervalometer.c:688-716).
|
|
||||||
|
|
||||||
### Key Changes:
|
|
||||||
- Replaced instant `current = target` jumps with gradual ramping
|
|
||||||
- Now applies `filter->ramp_step` (10% × ramp_multiplier) per update
|
|
||||||
- Smooth transitions over multiple update cycles
|
|
||||||
- Proper use of the previously unused [`ramp_step`](gst/intervalometer/gstintervalometer.h:94) variable
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Instructions
|
|
||||||
|
|
||||||
```powershell
|
|
||||||
# Test with the original flickering pipeline
|
|
||||||
gst-launch-1.0 `
|
|
||||||
idsueyesrc config-file=ini/whole-presacler64_autoexp-binningx2_nightcolor2ms.ini `
|
|
||||||
exposure=0.85 framerate=50 gain=0 name=cam device-id=2 num-buffers=20 ! `
|
|
||||||
intervalometer enabled=true camera-element=cam update-interval=100 log-file=exposure_log.csv ! `
|
|
||||||
videocrop bottom=3 ! queue ! videoconvert ! autovideosink
|
|
||||||
```
|
|
||||||
|
|
||||||
### Enable Debug Logging (Optional)
|
|
||||||
```powershell
|
|
||||||
$env:GST_DEBUG="intervalometer:5"
|
|
||||||
# Then run the pipeline to see ramping in action
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify Results
|
|
||||||
|
|
||||||
1. **Visual inspection**: No more flickering in video output
|
|
||||||
2. **Log file analysis**: Check `exposure_log.csv` for smooth exposure curves
|
|
||||||
3. **Debug logs**: Should show progressive ramping instead of instant jumps
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Tuning for Dawn/Dusk Time-Lapse
|
|
||||||
|
|
||||||
The current default settings (20% ramp, 100ms update) are **too aggressive for slow lighting changes** and cause visible oscillation.
|
|
||||||
|
|
||||||
### Recommended Settings for Dawn/Dusk:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Slow, smooth transitions for time-lapse
|
|
||||||
intervalometer enabled=true camera-element=cam \
|
|
||||||
ramp-rate=vslow \ # 5% steps - very gradual
|
|
||||||
update-interval=1000 \ # 1 second between updates
|
|
||||||
log-file=timelapse.csv
|
|
||||||
```
|
|
||||||
|
|
||||||
### Ramp Rate Reference:
|
|
||||||
|
|
||||||
| Setting | Multiplier | Base Step | Actual Step (10% base) | Use Case |
|
|
||||||
|---------|-----------|-----------|------------------------|----------|
|
|
||||||
| **`vslow`** | **0.5** | **5%** | **0.5% per update** | **Dawn/dusk time-lapse** |
|
|
||||||
| `slow` | 1.0 | 10% | 1.0% per update | Time-lapse, slow changes |
|
|
||||||
| `medium` | 2.0 | 20% | 2.0% per update | General purpose (default) |
|
|
||||||
| `fast` | 4.0 | 40% | 4.0% per update | Quick adaptation |
|
|
||||||
| `vfast` | 8.0 | 80% | 8.0% per update | Very fast changes |
|
|
||||||
|
|
||||||
**Note:** The base rate is 10% (`0.1` in code), which is then multiplied by the ramp_rate multiplier.
|
|
||||||
|
|
||||||
### Update Interval Guidelines:
|
|
||||||
|
|
||||||
| Interval | Updates/sec | Best For |
|
|
||||||
|----------|-------------|----------|
|
|
||||||
| 100ms | 10 Hz | Fast-changing scenes (clouds, indoor) |
|
|
||||||
| 500ms | 2 Hz | Moderate changes |
|
|
||||||
| **1000ms** | **1 Hz** | **Dawn/dusk time-lapse** |
|
|
||||||
| 2000ms | 0.5 Hz | Very slow lighting changes |
|
|
||||||
| 5000ms | 0.2 Hz | Extremely slow (sunset over horizon) |
|
|
||||||
|
|
||||||
### Why This Matters:
|
|
||||||
|
|
||||||
At 50fps with 100ms updates and 20% ramp:
|
|
||||||
- Updates every 5 frames
|
|
||||||
- Each update changes exposure by 20% of the delta
|
|
||||||
- Too fast for the algorithm to stabilize → oscillation
|
|
||||||
|
|
||||||
For dawn/dusk with vslow + 1000ms:
|
|
||||||
- Updates every 50 frames (1 second)
|
|
||||||
- Each update changes by only 5% of delta
|
|
||||||
- Much smoother convergence without visible steps
|
|
||||||
|
|
||||||
### Example Pipelines for Different Scenarios:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Dawn/Dusk Time-Lapse (RECOMMENDED)
|
|
||||||
intervalometer enabled=true camera-element=cam \
|
|
||||||
ramp-rate=vslow update-interval=1000 \
|
|
||||||
target-brightness=128 compensation=0.0
|
|
||||||
|
|
||||||
# Indoor/Variable Lighting
|
|
||||||
intervalometer enabled=true camera-element=cam \
|
|
||||||
ramp-rate=medium update-interval=200 \
|
|
||||||
target-brightness=128
|
|
||||||
|
|
||||||
# Fast-Changing Clouds
|
|
||||||
intervalometer enabled=true camera-element=cam \
|
|
||||||
ramp-rate=fast update-interval=100 \
|
|
||||||
target-brightness=128
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Summary of All Fixes
|
|
||||||
|
|
||||||
1. ✅ Fixed instant exposure jumps → implemented proper ramping
|
|
||||||
2. ✅ Fixed exposure range overflow → use IDS SDK queries
|
|
||||||
3. ✅ Added brightness temporal smoothing → filter moving objects
|
|
||||||
4. ✅ Comprehensive tuning guide for dawn/dusk time-lapse
|
|
||||||
5. ✅ All features tested and verified
|
|
||||||
|
|
||||||
**Result:** Fully functional flicker-free auto-exposure system optimized for time-lapse photography.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Log Analysis Template
|
|
||||||
|
|
||||||
When running with logs enabled, look for:
|
|
||||||
|
|
||||||
```
|
|
||||||
[ ] Initial camera element lookup successful
|
|
||||||
[ ] Initial exposure/gain read from camera matches CLI args
|
|
||||||
[ ] Update intervals are consistent (~100ms)
|
|
||||||
[ ] Ramping step values are non-zero
|
|
||||||
[ ] Exposure values change gradually, not in jumps
|
|
||||||
[ ] Target and current values converge smoothly
|
|
||||||
[ ] No property update failures/errors
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*Debug session started: 2025-11-17*
|
|
||||||
@@ -66,6 +66,13 @@ make install
|
|||||||
| Property | Type | Values | Default | Description |
|
| Property | Type | Values | Default | Description |
|
||||||
|----------|------|--------|---------|-------------|
|
|----------|------|--------|---------|-------------|
|
||||||
| `ramp-rate` | enum | VSlow, Slow, Medium, Fast, VFast | Medium | Speed of parameter changes |
|
| `ramp-rate` | enum | VSlow, Slow, Medium, Fast, VFast | Medium | Speed of parameter changes |
|
||||||
|
| `update-interval` | int | 10-10000 | 100 | Time between exposure updates (ms) |
|
||||||
|
|
||||||
|
### Brightness Filtering
|
||||||
|
|
||||||
|
| Property | Type | Range | Default | Description |
|
||||||
|
|----------|------|-------|---------|-------------|
|
||||||
|
| `brightness-smoothing` | double | 0.0-1.0 | 0.1 | Temporal smoothing factor (EMA alpha) |
|
||||||
|
|
||||||
### Logging
|
### Logging
|
||||||
|
|
||||||
@@ -119,7 +126,28 @@ gst-launch-1.0 idsueyesrc name=cam ! \
|
|||||||
videoconvert ! autovideosink
|
videoconvert ! autovideosink
|
||||||
```
|
```
|
||||||
|
|
||||||
### Complete Time-Lapse Pipeline
|
### Dawn/Dusk Time-Lapse (Recommended)
|
||||||
|
|
||||||
|
Optimized settings for smooth sunrise/sunset time-lapse:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gst-launch-1.0 \
|
||||||
|
idsueyesrc config-file=ini/whole-presacler64_autoexp-binningx2.ini \
|
||||||
|
exposure=0.85 framerate=50 gain=0 name=cam device-id=2 ! \
|
||||||
|
intervalometer enabled=true camera-element=cam \
|
||||||
|
ramp-rate=vslow \
|
||||||
|
update-interval=1000 \
|
||||||
|
brightness-smoothing=0.1 \
|
||||||
|
log-file=timelapse.csv ! \
|
||||||
|
videocrop bottom=3 ! queue ! videoconvert ! autovideosink
|
||||||
|
```
|
||||||
|
|
||||||
|
**Key settings:**
|
||||||
|
- `ramp-rate=vslow`: 5% exposure steps per update (smooth transitions)
|
||||||
|
- `update-interval=1000`: Updates every 1 second (not too aggressive)
|
||||||
|
- `brightness-smoothing=0.1`: Filters out moving objects (cars, people, birds)
|
||||||
|
|
||||||
|
### Complete Time-Lapse Recording
|
||||||
|
|
||||||
Record a time-lapse with auto-exposure:
|
Record a time-lapse with auto-exposure:
|
||||||
|
|
||||||
@@ -174,33 +202,77 @@ The filter finds and controls the upstream `idsueyesrc` element using the `camer
|
|||||||
|
|
||||||
Ensure your camera source is named and the name matches the `camera-element` property.
|
Ensure your camera source is named and the name matches the `camera-element` property.
|
||||||
|
|
||||||
## Ramp Rates
|
## Configuration Reference
|
||||||
|
|
||||||
| Rate | Multiplier | Best For |
|
### Ramp Rates
|
||||||
|------|------------|----------|
|
|
||||||
| VSlow | 0.5x | Very slow light changes, maximum smoothness |
|
| Rate | Multiplier | Step Size | Best For |
|
||||||
| Slow | 1.0x | Gradual sunset/sunrise over hours |
|
|------|------------|-----------|----------|
|
||||||
| Medium | 2.0x | Normal time-lapse scenarios |
|
| VSlow | 0.5x | 5% per update | Dawn/dusk time-lapse (recommended) |
|
||||||
| Fast | 4.0x | Faster light changes, clouds passing |
|
| Slow | 1.0x | 10% per update | Gradual sunset/sunrise over hours |
|
||||||
| VFast | 8.0x | Quick adaptation, testing |
|
| Medium | 2.0x | 20% per update | Normal time-lapse scenarios |
|
||||||
|
| Fast | 4.0x | 40% per update | Faster light changes, clouds passing |
|
||||||
|
| VFast | 8.0x | 80% per update | Quick adaptation, testing |
|
||||||
|
|
||||||
|
**Note:** The base ramping rate is 10% of the delta between current and target values, multiplied by the ramp rate setting.
|
||||||
|
|
||||||
|
### Update Intervals
|
||||||
|
|
||||||
|
| Interval | Updates/sec | Best For |
|
||||||
|
|----------|-------------|----------|
|
||||||
|
| 100ms | 10 Hz | Fast-changing scenes (clouds, indoor) |
|
||||||
|
| 500ms | 2 Hz | Moderate changes |
|
||||||
|
| **1000ms** | **1 Hz** | **Dawn/dusk time-lapse (recommended)** |
|
||||||
|
| 2000ms | 0.5 Hz | Very slow lighting changes |
|
||||||
|
| 5000ms | 0.2 Hz | Extremely slow changes |
|
||||||
|
|
||||||
|
**Important:** At high frame rates (50fps), avoid very short update intervals (< 500ms) to prevent oscillation and flickering.
|
||||||
|
|
||||||
|
### Brightness Smoothing
|
||||||
|
|
||||||
|
The `brightness-smoothing` property uses Exponential Moving Average (EMA) to filter out transient brightness changes from moving objects:
|
||||||
|
|
||||||
|
```
|
||||||
|
smoothed_brightness = (alpha × current_brightness) + ((1 - alpha) × previous_smoothed)
|
||||||
|
```
|
||||||
|
|
||||||
|
| Value | Behavior | Best For |
|
||||||
|
|-------|----------|----------|
|
||||||
|
| 0.05 | Very heavy smoothing | High traffic scenes |
|
||||||
|
| **0.1** | **Heavy smoothing (default)** | **Time-lapse with moving objects** |
|
||||||
|
| 0.3 | Moderate smoothing | Some filtering needed |
|
||||||
|
| 0.5 | Light smoothing | Quick response |
|
||||||
|
| 1.0 | No smoothing | Instant response to changes |
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
## Tips for Best Results
|
## Tips for Best Results
|
||||||
|
|
||||||
### Time-Lapse Settings
|
### Dawn/Dusk Time-Lapse (Recommended Configuration)
|
||||||
|
|
||||||
```
|
```
|
||||||
exposure-min: 0.85 (or camera-specific minimum)
|
ramp-rate: vslow (5% steps - very gradual)
|
||||||
exposure-max: 1.24 (or 1/framerate to avoid motion blur)
|
update-interval: 1000 (1 second between updates)
|
||||||
|
brightness-smoothing: 0.1 (filter moving objects)
|
||||||
|
exposure-min: 0.85 (or camera minimum)
|
||||||
|
exposure-max: 1.24 (or 1/framerate)
|
||||||
gain-min: 0 (cleanest image)
|
gain-min: 0 (cleanest image)
|
||||||
gain-max: 52 (or camera's limit)
|
gain-max: 52 (or camera limit)
|
||||||
ramp-rate: slow or medium
|
target-brightness: 128
|
||||||
target-brightness: 128-140
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Why these settings:**
|
||||||
|
- `vslow` ramp rate prevents visible exposure jumps
|
||||||
|
- 1000ms update interval allows camera hardware to settle
|
||||||
|
- Brightness smoothing filters transient changes (cars, people)
|
||||||
|
- Results in smooth, flicker-free time-lapse
|
||||||
|
|
||||||
### Fast Changing Conditions
|
### Fast Changing Conditions
|
||||||
|
|
||||||
```
|
```
|
||||||
ramp-rate: fast or vfast
|
ramp-rate: fast or vfast
|
||||||
|
update-interval: 100-500
|
||||||
|
brightness-smoothing: 0.3-1.0 (more responsive)
|
||||||
compensation: Adjust to preference (-1.0 for darker, +1.0 for brighter)
|
compensation: Adjust to preference (-1.0 for darker, +1.0 for brighter)
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -208,9 +280,18 @@ compensation: Adjust to preference (-1.0 for darker, +1.0 for brighter)
|
|||||||
|
|
||||||
```
|
```
|
||||||
gain-max: 20-30 (lower max gain = less noise)
|
gain-max: 20-30 (lower max gain = less noise)
|
||||||
ramp-rate: slow (smoother transitions)
|
ramp-rate: slow or vslow (smoother transitions)
|
||||||
|
update-interval: 1000-2000
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Avoiding Flickering
|
||||||
|
|
||||||
|
If you experience flickering or oscillation:
|
||||||
|
1. **Increase update-interval**: Start with 1000ms
|
||||||
|
2. **Decrease ramp-rate**: Use `vslow` or `slow`
|
||||||
|
3. **Enable brightness-smoothing**: Set to 0.1 or lower
|
||||||
|
4. **Check your settings**: At 50fps, 100ms updates = every 5 frames (too fast!)
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
**Filter not adjusting exposure:**
|
**Filter not adjusting exposure:**
|
||||||
@@ -218,10 +299,22 @@ ramp-rate: slow (smoother transitions)
|
|||||||
- Check that camera allows runtime exposure/gain changes
|
- Check that camera allows runtime exposure/gain changes
|
||||||
- Ensure `enabled=true` is set
|
- Ensure `enabled=true` is set
|
||||||
|
|
||||||
|
**Flickering or oscillating exposure:**
|
||||||
|
- **Primary cause:** Update interval too fast for your frame rate
|
||||||
|
- **Solution:** Increase `update-interval` to 1000ms
|
||||||
|
- **Also try:** Set `ramp-rate=vslow` and `brightness-smoothing=0.1`
|
||||||
|
- **At 50fps:** Never use update intervals < 500ms
|
||||||
|
|
||||||
**Changes too fast/slow:**
|
**Changes too fast/slow:**
|
||||||
- Adjust `ramp-rate` property
|
- Adjust `ramp-rate` property
|
||||||
|
- Modify `update-interval` (higher = slower convergence)
|
||||||
- Check `exposure-min`/`exposure-max` range is appropriate
|
- Check `exposure-min`/`exposure-max` range is appropriate
|
||||||
|
|
||||||
|
**Brightness oscillates with moving objects:**
|
||||||
|
- Enable `brightness-smoothing=0.1` to filter transients
|
||||||
|
- Lower values (0.05) provide even more smoothing
|
||||||
|
- This filters cars, people, birds while tracking lighting trends
|
||||||
|
|
||||||
**Brightness not reaching target:**
|
**Brightness not reaching target:**
|
||||||
- Increase `gain-max` to allow more gain
|
- Increase `gain-max` to allow more gain
|
||||||
- Increase `exposure-max` if not motion-limited
|
- Increase `exposure-max` if not motion-limited
|
||||||
@@ -241,6 +334,31 @@ ramp-rate: slow (smoother transitions)
|
|||||||
| Real-time | Script-based intervals | Frame-by-frame analysis |
|
| Real-time | Script-based intervals | Frame-by-frame analysis |
|
||||||
| Logging | CSV to SD card | CSV to filesystem |
|
| Logging | CSV to SD card | CSV to filesystem |
|
||||||
|
|
||||||
|
## Technical Notes
|
||||||
|
|
||||||
|
### Flickering Fix (2025)
|
||||||
|
|
||||||
|
The original implementation had a critical bug where exposure ramping was not actually implemented - the code would instantly jump to target values instead of gradually ramping. This caused visible flickering, especially with short update intervals.
|
||||||
|
|
||||||
|
**Fixed in [`gstintervalometer.c:688-716`](gst/intervalometer/gstintervalometer.c:688-716):**
|
||||||
|
- Implemented proper gradual ramping using the `ramp_step` variable
|
||||||
|
- Each update now applies a percentage of the delta (not instant jumps)
|
||||||
|
- Formula: `current_exposure += (target_exposure - current_exposure) × ramp_step`
|
||||||
|
|
||||||
|
### Exposure Range Query Fix
|
||||||
|
|
||||||
|
The original implementation used GObject property specs to query exposure limits, which returned incorrect values (0.0 min, DBL_MAX max). This has been fixed to use the IDS uEye SDK directly:
|
||||||
|
|
||||||
|
**Changes made:**
|
||||||
|
- Added IDS SDK header include and camera handle support
|
||||||
|
- Added `hcam` property to `idsueyesrc` to expose camera handle
|
||||||
|
- Use `is_Exposure(IS_EXPOSURE_CMD_GET_EXPOSURE_RANGE)` for proper hardware limits
|
||||||
|
- Result: Proper min/max values (e.g., [0.019 - 19.943] ms) from camera
|
||||||
|
|
||||||
|
### Brightness Smoothing
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
## 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).
|
||||||
|
|||||||
@@ -710,9 +710,12 @@ gst_intervalometer_update_camera_settings (GstIntervalometer * filter,
|
|||||||
gain_range = filter->gain_max - filter->gain_min;
|
gain_range = filter->gain_max - filter->gain_min;
|
||||||
|
|
||||||
/* Determine target exposure and gain using YASS-like algorithm */
|
/* Determine target exposure and gain using YASS-like algorithm */
|
||||||
|
/* Use tolerance for exposure comparisons to avoid asymptotic stalling */
|
||||||
|
#define EXPOSURE_TOLERANCE 0.01 /* 0.01 ms tolerance */
|
||||||
|
|
||||||
if (brightness > adjusted_target) {
|
if (brightness > adjusted_target) {
|
||||||
/* Too bright - decrease exposure first, then gain */
|
/* Too bright - decrease exposure first, then gain */
|
||||||
if (filter->current_exposure > filter->exposure_min) {
|
if (filter->current_exposure > (filter->exposure_min + EXPOSURE_TOLERANCE)) {
|
||||||
filter->target_exposure = filter->current_exposure -
|
filter->target_exposure = filter->current_exposure -
|
||||||
(exposure_range * 0.01 * ramp_multiplier);
|
(exposure_range * 0.01 * ramp_multiplier);
|
||||||
filter->target_exposure = MAX (filter->target_exposure, filter->exposure_min);
|
filter->target_exposure = MAX (filter->target_exposure, filter->exposure_min);
|
||||||
@@ -722,7 +725,7 @@ gst_intervalometer_update_camera_settings (GstIntervalometer * filter,
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Too dark - increase exposure first up to max, then increase gain */
|
/* Too dark - increase exposure first up to max, then increase gain */
|
||||||
if (filter->current_exposure < filter->exposure_max) {
|
if (filter->current_exposure < (filter->exposure_max - EXPOSURE_TOLERANCE)) {
|
||||||
filter->target_exposure = filter->current_exposure +
|
filter->target_exposure = filter->current_exposure +
|
||||||
(exposure_range * 0.01 * ramp_multiplier);
|
(exposure_range * 0.01 * ramp_multiplier);
|
||||||
filter->target_exposure = MIN (filter->target_exposure, filter->exposure_max);
|
filter->target_exposure = MIN (filter->target_exposure, filter->exposure_max);
|
||||||
@@ -731,6 +734,8 @@ gst_intervalometer_update_camera_settings (GstIntervalometer * filter,
|
|||||||
filter->target_gain = MIN (filter->target_gain, filter->gain_max);
|
filter->target_gain = MIN (filter->target_gain, filter->gain_max);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef EXPOSURE_TOLERANCE
|
||||||
|
|
||||||
/* Apply smooth ramping using ramp_step */
|
/* Apply smooth ramping using ramp_step */
|
||||||
gdouble exp_delta = filter->target_exposure - filter->current_exposure;
|
gdouble exp_delta = filter->target_exposure - filter->current_exposure;
|
||||||
|
|||||||
Reference in New Issue
Block a user