Add intervalometer filter for automatic exposure control
Implements YASS-inspired automatic exposure control for IDS uEye cameras.
The intervalometer filter analyzes video brightness in real-time and
smoothly ramps camera exposure and gain settings during changing light
conditions - ideal for sunset/sunrise time-lapse photography.
Key features:
- Automatic exposure ramping (0.85-1.24ms configurable range)
- Automatic gain control (0-52 configurable range)
- Real-time brightness analysis (GRAY8, GRAY16, RGB, BGR, BGRA)
- YASS-inspired ramping algorithm (exposure priority, then gain)
- Configurable ramp rates (VSlow/Slow/Medium/Fast/VFast)
- Exposure compensation (±4 stops)
- CSV logging of exposure parameters
- Direct GObject property control (no message bus overhead)
Technical implementation:
- GstBaseTransform filter for in-place processing
- Discovers upstream camera element by name
- Controls camera via g_object_set() for synchronous updates
- Frame-by-frame brightness calculation with format support
Files added:
- gst/intervalometer/gstintervalometer.c: Main implementation (734 lines)
- gst/intervalometer/gstintervalometer.h: Header with structure definitions
- gst/intervalometer/CMakeLists.txt: Build configuration
- gst/intervalometer/README.md: Comprehensive documentation
Files modified:
- gst/CMakeLists.txt: Added intervalometer subdirectory
- build.ps1: Added intervalometer to build and deployment pipeline
Usage example:
gst-launch-1.0 idsueyesrc name=cam ! \\
intervalometer enabled=true camera-element=cam \\
exposure-min=0.85 exposure-max=1.24 \\
gain-min=0 gain-max=52 ramp-rate=medium ! \\
autovideosink
Inspired by YASS (Yet Another Sunset Script) for CHDK cameras by
waterwingz, based on work by Fbonomi and soulf2.
This commit is contained in:
@@ -4,6 +4,7 @@ endif (OPENCV_FOUND)
|
||||
|
||||
add_subdirectory (bayerutils)
|
||||
add_subdirectory (extractcolor)
|
||||
add_subdirectory (intervalometer)
|
||||
|
||||
if (ENABLE_KLV)
|
||||
add_subdirectory (klv)
|
||||
|
||||
28
gst/intervalometer/CMakeLists.txt
Normal file
28
gst/intervalometer/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
set (SOURCES
|
||||
gstintervalometer.c
|
||||
)
|
||||
|
||||
set (HEADERS
|
||||
gstintervalometer.h)
|
||||
|
||||
include_directories (AFTER
|
||||
${ORC_INCLUDE_DIR})
|
||||
|
||||
set (libname gstintervalometer)
|
||||
|
||||
add_library (${libname} MODULE
|
||||
${SOURCES}
|
||||
${HEADERS})
|
||||
|
||||
target_link_libraries (${libname}
|
||||
${ORC_LIBRARIES}
|
||||
${GLIB2_LIBRARIES}
|
||||
${GOBJECT_LIBRARIES}
|
||||
${GSTREAMER_LIBRARY}
|
||||
${GSTREAMER_BASE_LIBRARY}
|
||||
${GSTREAMER_VIDEO_LIBRARY})
|
||||
|
||||
if (WIN32)
|
||||
install (FILES $<TARGET_PDB_FILE:${libname}> DESTINATION ${PDB_INSTALL_DIR} COMPONENT pdb OPTIONAL)
|
||||
endif ()
|
||||
install(TARGETS ${libname} LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR})
|
||||
253
gst/intervalometer/README.md
Normal file
253
gst/intervalometer/README.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# GStreamer Intervalometer Filter
|
||||
|
||||
**Automatic Exposure Control for IDS uEye Cameras**
|
||||
|
||||
Inspired by [YASS (Yet Another Sunset Script)](../../yass/README.md) for CHDK cameras.
|
||||
|
||||
## Overview
|
||||
|
||||
The `intervalometer` element is a GStreamer filter that automatically adjusts camera exposure and gain settings during changing light conditions. It analyzes video brightness in real-time and smoothly ramps camera parameters to maintain optimal exposure - perfect for time-lapse photography during sunset, sunrise, or other variable lighting scenarios.
|
||||
|
||||
## Features
|
||||
|
||||
- **Automatic Exposure Ramping**: Smoothly adjusts exposure time based on scene brightness
|
||||
- **Automatic Gain Control**: Increases/decreases sensor gain when exposure limits are reached
|
||||
- **Configurable Ranges**: Set custom min/max values for exposure (0.85-1.24ms) and gain (0-52)
|
||||
- **Multiple Ramp Rates**: Choose from VSlow/Slow/Medium/Fast/VFast adjustment speeds
|
||||
- **Exposure Compensation**: Fine-tune brightness with ±4 stops of compensation
|
||||
- **CSV Logging**: Optional detailed logging of all exposure parameters
|
||||
- **Multiple Format Support**: Works with GRAY8, GRAY16, RGB, BGR, and BGRA video
|
||||
|
||||
## Requirements
|
||||
|
||||
- GStreamer 1.0+
|
||||
- IDS uEye camera with `idsueyesrc` element
|
||||
- Camera must support runtime exposure and gain property changes
|
||||
|
||||
## Installation
|
||||
|
||||
The filter is built as part of the gst-plugins-vision project. Build and install normally:
|
||||
|
||||
```bash
|
||||
mkdir build
|
||||
cd build
|
||||
cmake ..
|
||||
make
|
||||
make install
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
### Control Properties
|
||||
|
||||
| Property | Type | Range | Default | Description |
|
||||
|----------|------|-------|---------|-------------|
|
||||
| `enabled` | boolean | - | TRUE | Enable/disable auto-exposure |
|
||||
| `target-brightness` | double | 0-255 | 128.0 | Target average brightness level |
|
||||
| `compensation` | double | -4.0 to 4.0 | 0.0 | Exposure compensation in stops |
|
||||
| `camera-element` | string | - | "" | Name of upstream idsueyesrc element |
|
||||
|
||||
### Exposure Range
|
||||
|
||||
| Property | Type | Range | Default | Description |
|
||||
|----------|------|-------|---------|-------------|
|
||||
| `exposure-min` | double | 0.01-1000.0 | 0.85 | Minimum exposure time (ms) |
|
||||
| `exposure-max` | double | 0.01-1000.0 | 1.24 | Maximum exposure time (ms) |
|
||||
|
||||
### Gain Range
|
||||
|
||||
| Property | Type | Range | Default | Description |
|
||||
|----------|------|-------|---------|-------------|
|
||||
| `gain-min` | int | 0-100 | 0 | Minimum gain value |
|
||||
| `gain-max` | int | 0-100 | 52 | Maximum gain value |
|
||||
|
||||
### Ramping
|
||||
|
||||
| Property | Type | Values | Default | Description |
|
||||
|----------|------|--------|---------|-------------|
|
||||
| `ramp-rate` | enum | VSlow, Slow, Medium, Fast, VFast | Medium | Speed of parameter changes |
|
||||
|
||||
### Logging
|
||||
|
||||
| Property | Type | Range | Default | Description |
|
||||
|----------|------|-------|---------|-------------|
|
||||
| `log-file` | string | - | "" | Path to CSV log file (empty = disabled) |
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Auto-Exposure
|
||||
|
||||
```bash
|
||||
gst-launch-1.0 idsueyesrc name=cam ! \
|
||||
intervalometer enabled=true camera-element=cam ! \
|
||||
videoconvert ! autovideosink
|
||||
```
|
||||
|
||||
### Custom Range for Day/Night Transition
|
||||
|
||||
Configure for the typical day (0.85ms exposure, gain 52) to night (1.24ms exposure, gain 0) range:
|
||||
|
||||
```bash
|
||||
gst-launch-1.0 idsueyesrc name=cam ! \
|
||||
intervalometer enabled=true camera-element=cam \
|
||||
exposure-min=0.85 exposure-max=1.24 \
|
||||
gain-min=0 gain-max=52 \
|
||||
ramp-rate=medium ! \
|
||||
videoconvert ! autovideosink
|
||||
```
|
||||
|
||||
### With Exposure Compensation
|
||||
|
||||
Adjust overall brightness with compensation:
|
||||
|
||||
```bash
|
||||
gst-launch-1.0 idsueyesrc name=cam ! \
|
||||
intervalometer enabled=true camera-element=cam \
|
||||
compensation=1.0 \
|
||||
target-brightness=140 ! \
|
||||
videoconvert ! autovideosink
|
||||
```
|
||||
|
||||
### With CSV Logging
|
||||
|
||||
Log all exposure data to a CSV file:
|
||||
|
||||
```bash
|
||||
gst-launch-1.0 idsueyesrc name=cam ! \
|
||||
intervalometer enabled=true camera-element=cam \
|
||||
log-file=exposure_log.csv ! \
|
||||
videoconvert ! autovideosink
|
||||
```
|
||||
|
||||
### Complete Time-Lapse Pipeline
|
||||
|
||||
Record a time-lapse with auto-exposure:
|
||||
|
||||
```bash
|
||||
gst-launch-1.0 idsueyesrc name=cam framerate=1 ! \
|
||||
intervalometer enabled=true camera-element=cam \
|
||||
exposure-min=0.85 exposure-max=1.24 \
|
||||
gain-min=0 gain-max=52 \
|
||||
ramp-rate=slow \
|
||||
log-file=timelapse_exposure.csv ! \
|
||||
videoconvert ! x264enc ! mp4mux ! \
|
||||
filesink location=timelapse.mp4
|
||||
```
|
||||
|
||||
## How It Works
|
||||
|
||||
### Exposure Control Algorithm
|
||||
|
||||
The filter uses a YASS-inspired algorithm:
|
||||
|
||||
1. **Brightness Analysis**: Calculates average brightness of each frame
|
||||
2. **Error Calculation**: Compares to target brightness (with compensation)
|
||||
3. **Ramping Priority**:
|
||||
- When too bright: Decreases exposure first, then gain
|
||||
- When too dark: Increases exposure first (up to max), then gain
|
||||
4. **Smooth Ramping**: Changes are gradual based on ramp-rate setting
|
||||
|
||||
### Typical Behavior
|
||||
|
||||
- **Daytime**: Fast shutter (low exposure), high gain for noise reduction
|
||||
- **Sunset/Dusk**: Gradually increases exposure time as light fades
|
||||
- **Night**: Maximum exposure time, minimum gain
|
||||
|
||||
### CSV Log Format
|
||||
|
||||
When logging is enabled, the filter creates a CSV file with:
|
||||
|
||||
```csv
|
||||
Frame,Time_s,Brightness,Exposure_ms,Gain,Target_Brightness
|
||||
0,0.000,145.32,0.850,52,128.00
|
||||
1,0.033,143.21,0.851,52,128.00
|
||||
2,0.067,142.15,0.853,52,128.00
|
||||
...
|
||||
```
|
||||
|
||||
## Camera Property Control
|
||||
|
||||
The filter finds and controls the upstream `idsueyesrc` element using the `camera-element` property. It sets:
|
||||
|
||||
- **exposure**: Exposure time in milliseconds
|
||||
- **gain**: Master gain (0-100 range)
|
||||
|
||||
Ensure your camera source is named and the name matches the `camera-element` property.
|
||||
|
||||
## Ramp Rates
|
||||
|
||||
| Rate | Multiplier | Best For |
|
||||
|------|------------|----------|
|
||||
| VSlow | 0.5x | Very slow light changes, maximum smoothness |
|
||||
| Slow | 1.0x | Gradual sunset/sunrise over hours |
|
||||
| Medium | 2.0x | Normal time-lapse scenarios |
|
||||
| Fast | 4.0x | Faster light changes, clouds passing |
|
||||
| VFast | 8.0x | Quick adaptation, testing |
|
||||
|
||||
## Tips for Best Results
|
||||
|
||||
### Time-Lapse Settings
|
||||
|
||||
```
|
||||
exposure-min: 0.85 (or camera-specific minimum)
|
||||
exposure-max: 1.24 (or 1/framerate to avoid motion blur)
|
||||
gain-min: 0 (cleanest image)
|
||||
gain-max: 52 (or camera's limit)
|
||||
ramp-rate: slow or medium
|
||||
target-brightness: 128-140
|
||||
```
|
||||
|
||||
### Fast Changing Conditions
|
||||
|
||||
```
|
||||
ramp-rate: fast or vfast
|
||||
compensation: Adjust to preference (-1.0 for darker, +1.0 for brighter)
|
||||
```
|
||||
|
||||
### Maximum Image Quality
|
||||
|
||||
```
|
||||
gain-max: 20-30 (lower max gain = less noise)
|
||||
ramp-rate: slow (smoother transitions)
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Filter not adjusting exposure:**
|
||||
- Verify `camera-element` property matches your camera source name
|
||||
- Check that camera allows runtime exposure/gain changes
|
||||
- Ensure `enabled=true` is set
|
||||
|
||||
**Changes too fast/slow:**
|
||||
- Adjust `ramp-rate` property
|
||||
- Check `exposure-min`/`exposure-max` range is appropriate
|
||||
|
||||
**Brightness not reaching target:**
|
||||
- Increase `gain-max` to allow more gain
|
||||
- Increase `exposure-max` if not motion-limited
|
||||
- Adjust `target-brightness` or use `compensation`
|
||||
|
||||
**Log file not created:**
|
||||
- Check file path is writable
|
||||
- Verify `log-file` property is set before starting pipeline
|
||||
|
||||
## Comparison to YASS
|
||||
|
||||
| Feature | YASS (CHDK) | Intervalometer (GStreamer) |
|
||||
|---------|-------------|---------------------------|
|
||||
| Platform | Canon cameras with CHDK | IDS uEye cameras |
|
||||
| Control | Shutter speed + ISO | Exposure time + Gain |
|
||||
| Integration | Standalone Lua script | GStreamer pipeline element |
|
||||
| Real-time | Script-based intervals | Frame-by-frame analysis |
|
||||
| Logging | CSV to SD card | CSV to filesystem |
|
||||
|
||||
## License
|
||||
|
||||
This filter is part of gst-plugins-vision and released under the GNU Library General Public License (LGPL).
|
||||
|
||||
Inspired by YASS (Yet Another Sunset Script) by waterwingz, based on work by Fbonomi and soulf2, released under GPL.
|
||||
|
||||
## See Also
|
||||
|
||||
- [YASS Documentation](../../yass/README.md) - Original CHDK script that inspired this filter
|
||||
- [idsueyesrc](../../sys/idsueye/gstidsueyesrc.c) - IDS uEye camera source element
|
||||
693
gst/intervalometer/gstintervalometer.c
Normal file
693
gst/intervalometer/gstintervalometer.c
Normal file
@@ -0,0 +1,693 @@
|
||||
/* GStreamer
|
||||
* Copyright (C) 2024 FIXME <fixme@example.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:element-intervalometer
|
||||
*
|
||||
* Automatic exposure control for IDS uEye cameras.
|
||||
* Inspired by YASS (Yet Another Sunset Script) for CHDK cameras.
|
||||
*
|
||||
* Analyzes video brightness and automatically adjusts camera exposure
|
||||
* and gain settings to maintain optimal exposure during changing light
|
||||
* conditions (e.g., sunset/sunrise time-lapse).
|
||||
*
|
||||
* <refsect2>
|
||||
* <title>Example launch line</title>
|
||||
* |[
|
||||
* gst-launch-1.0 idsueyesrc name=cam ! intervalometer enabled=true camera-element=cam ! videoconvert ! autovideosink
|
||||
* ]|
|
||||
* Automatically adjusts exposure and gain on the IDS uEye camera
|
||||
* </refsect2>
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "gstintervalometer.h"
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
GST_DEBUG_CATEGORY_STATIC (gst_intervalometer_debug);
|
||||
#define GST_CAT_DEFAULT gst_intervalometer_debug
|
||||
|
||||
/* Properties */
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ENABLED,
|
||||
PROP_TARGET_BRIGHTNESS,
|
||||
PROP_COMPENSATION,
|
||||
PROP_EXPOSURE_MIN,
|
||||
PROP_EXPOSURE_MAX,
|
||||
PROP_GAIN_MIN,
|
||||
PROP_GAIN_MAX,
|
||||
PROP_RAMP_RATE,
|
||||
PROP_LOG_FILE,
|
||||
PROP_CAMERA_ELEMENT
|
||||
};
|
||||
|
||||
#define DEFAULT_PROP_ENABLED TRUE
|
||||
#define DEFAULT_PROP_TARGET_BRIGHTNESS 128.0
|
||||
#define DEFAULT_PROP_COMPENSATION 0.0
|
||||
#define DEFAULT_PROP_EXPOSURE_MIN 0.85
|
||||
#define DEFAULT_PROP_EXPOSURE_MAX 1.24
|
||||
#define DEFAULT_PROP_GAIN_MIN 0
|
||||
#define DEFAULT_PROP_GAIN_MAX 52
|
||||
#define DEFAULT_PROP_RAMP_RATE RAMP_RATE_MEDIUM
|
||||
#define DEFAULT_PROP_LOG_FILE ""
|
||||
#define DEFAULT_PROP_CAMERA_ELEMENT ""
|
||||
|
||||
/* GStreamer boilerplate */
|
||||
#define gst_intervalometer_parent_class parent_class
|
||||
G_DEFINE_TYPE (GstIntervalometer, gst_intervalometer, GST_TYPE_BASE_TRANSFORM);
|
||||
|
||||
static GstStaticPadTemplate gst_intervalometer_sink_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("sink",
|
||||
GST_PAD_SINK,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ GRAY8, GRAY16_LE, GRAY16_BE, BGRA, RGB, BGR }"))
|
||||
);
|
||||
|
||||
static GstStaticPadTemplate gst_intervalometer_src_template =
|
||||
GST_STATIC_PAD_TEMPLATE ("src",
|
||||
GST_PAD_SRC,
|
||||
GST_PAD_ALWAYS,
|
||||
GST_STATIC_CAPS (GST_VIDEO_CAPS_MAKE ("{ GRAY8, GRAY16_LE, GRAY16_BE, BGRA, RGB, BGR }"))
|
||||
);
|
||||
|
||||
/* GObject vmethod declarations */
|
||||
static void gst_intervalometer_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec);
|
||||
static void gst_intervalometer_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec);
|
||||
static void gst_intervalometer_dispose (GObject * object);
|
||||
static void gst_intervalometer_finalize (GObject * object);
|
||||
|
||||
/* GstBaseTransform vmethod declarations */
|
||||
static gboolean gst_intervalometer_start (GstBaseTransform * trans);
|
||||
static gboolean gst_intervalometer_stop (GstBaseTransform * trans);
|
||||
static gboolean gst_intervalometer_set_caps (GstBaseTransform * trans,
|
||||
GstCaps * incaps, GstCaps * outcaps);
|
||||
static GstFlowReturn gst_intervalometer_transform_ip (GstBaseTransform * trans,
|
||||
GstBuffer * buf);
|
||||
|
||||
/* Helper functions */
|
||||
static void gst_intervalometer_reset (GstIntervalometer * filter);
|
||||
static gdouble gst_intervalometer_calculate_brightness (GstIntervalometer * filter,
|
||||
GstBuffer * buf);
|
||||
static void gst_intervalometer_update_camera_settings (GstIntervalometer * filter,
|
||||
gdouble brightness);
|
||||
static void gst_intervalometer_write_log (GstIntervalometer * filter,
|
||||
gdouble brightness);
|
||||
static GstElement * gst_intervalometer_find_camera_element (GstIntervalometer * filter);
|
||||
|
||||
/* Ramp rate multipliers (based on YASS algorithm) */
|
||||
static const gdouble ramp_rate_multipliers[] = {
|
||||
0.5, /* VSLOW */
|
||||
1.0, /* SLOW */
|
||||
2.0, /* MEDIUM */
|
||||
4.0, /* FAST */
|
||||
8.0 /* VFAST */
|
||||
};
|
||||
|
||||
static void
|
||||
gst_intervalometer_class_init (GstIntervalometerClass * klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
|
||||
GstBaseTransformClass *gstbasetransform_class =
|
||||
GST_BASE_TRANSFORM_CLASS (klass);
|
||||
|
||||
GST_DEBUG_CATEGORY_INIT (gst_intervalometer_debug, "intervalometer", 0,
|
||||
"Automatic Exposure Controller");
|
||||
|
||||
/* Register GObject vmethods */
|
||||
gobject_class->set_property = gst_intervalometer_set_property;
|
||||
gobject_class->get_property = gst_intervalometer_get_property;
|
||||
gobject_class->dispose = gst_intervalometer_dispose;
|
||||
gobject_class->finalize = gst_intervalometer_finalize;
|
||||
|
||||
/* Install GObject properties */
|
||||
g_object_class_install_property (gobject_class, PROP_ENABLED,
|
||||
g_param_spec_boolean ("enabled", "Enabled",
|
||||
"Enable automatic exposure control", DEFAULT_PROP_ENABLED,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_TARGET_BRIGHTNESS,
|
||||
g_param_spec_double ("target-brightness", "Target Brightness",
|
||||
"Target average brightness (0-255)", 0.0, 255.0,
|
||||
DEFAULT_PROP_TARGET_BRIGHTNESS,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_COMPENSATION,
|
||||
g_param_spec_double ("compensation", "Exposure Compensation",
|
||||
"Exposure compensation in stops", -4.0, 4.0,
|
||||
DEFAULT_PROP_COMPENSATION,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_EXPOSURE_MIN,
|
||||
g_param_spec_double ("exposure-min", "Minimum Exposure",
|
||||
"Minimum exposure time in milliseconds", 0.01, 1000.0,
|
||||
DEFAULT_PROP_EXPOSURE_MIN,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_EXPOSURE_MAX,
|
||||
g_param_spec_double ("exposure-max", "Maximum Exposure",
|
||||
"Maximum exposure time in milliseconds", 0.01, 1000.0,
|
||||
DEFAULT_PROP_EXPOSURE_MAX,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_GAIN_MIN,
|
||||
g_param_spec_int ("gain-min", "Minimum Gain",
|
||||
"Minimum gain value", 0, 100, DEFAULT_PROP_GAIN_MIN,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_GAIN_MAX,
|
||||
g_param_spec_int ("gain-max", "Maximum Gain",
|
||||
"Maximum gain value", 0, 100, DEFAULT_PROP_GAIN_MAX,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_RAMP_RATE,
|
||||
g_param_spec_enum ("ramp-rate", "Ramp Rate",
|
||||
"Speed of exposure/gain ramping", GST_TYPE_INTERVALOMETER,
|
||||
DEFAULT_PROP_RAMP_RATE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_LOG_FILE,
|
||||
g_param_spec_string ("log-file", "Log File",
|
||||
"Path to CSV log file (empty to disable logging)",
|
||||
DEFAULT_PROP_LOG_FILE,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
g_object_class_install_property (gobject_class, PROP_CAMERA_ELEMENT,
|
||||
g_param_spec_string ("camera-element", "Camera Element Name",
|
||||
"Name of the upstream camera element to control",
|
||||
DEFAULT_PROP_CAMERA_ELEMENT,
|
||||
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
|
||||
|
||||
/* Set element metadata */
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_intervalometer_sink_template));
|
||||
gst_element_class_add_pad_template (gstelement_class,
|
||||
gst_static_pad_template_get (&gst_intervalometer_src_template));
|
||||
|
||||
gst_element_class_set_static_metadata (gstelement_class,
|
||||
"Intervalometer Auto-Exposure", "Filter/Effect/Video",
|
||||
"Automatic exposure control for IDS uEye cameras (inspired by YASS)",
|
||||
"FIXME <fixme@example.com>");
|
||||
|
||||
/* Register GstBaseTransform vmethods */
|
||||
gstbasetransform_class->start = GST_DEBUG_FUNCPTR (gst_intervalometer_start);
|
||||
gstbasetransform_class->stop = GST_DEBUG_FUNCPTR (gst_intervalometer_stop);
|
||||
gstbasetransform_class->set_caps =
|
||||
GST_DEBUG_FUNCPTR (gst_intervalometer_set_caps);
|
||||
gstbasetransform_class->transform_ip =
|
||||
GST_DEBUG_FUNCPTR (gst_intervalometer_transform_ip);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_init (GstIntervalometer * filter)
|
||||
{
|
||||
/* Initialize properties */
|
||||
filter->enabled = DEFAULT_PROP_ENABLED;
|
||||
filter->target_brightness = DEFAULT_PROP_TARGET_BRIGHTNESS;
|
||||
filter->compensation = DEFAULT_PROP_COMPENSATION;
|
||||
filter->exposure_min = DEFAULT_PROP_EXPOSURE_MIN;
|
||||
filter->exposure_max = DEFAULT_PROP_EXPOSURE_MAX;
|
||||
filter->gain_min = DEFAULT_PROP_GAIN_MIN;
|
||||
filter->gain_max = DEFAULT_PROP_GAIN_MAX;
|
||||
filter->ramp_rate = DEFAULT_PROP_RAMP_RATE;
|
||||
filter->log_file = g_strdup (DEFAULT_PROP_LOG_FILE);
|
||||
filter->camera_element_name = g_strdup (DEFAULT_PROP_CAMERA_ELEMENT);
|
||||
|
||||
/* Initialize internal state */
|
||||
filter->camera_src = NULL;
|
||||
filter->current_exposure = DEFAULT_PROP_EXPOSURE_MIN;
|
||||
filter->current_gain = DEFAULT_PROP_GAIN_MIN;
|
||||
filter->target_exposure = DEFAULT_PROP_EXPOSURE_MIN;
|
||||
filter->target_gain = DEFAULT_PROP_GAIN_MIN;
|
||||
filter->frame_count = 0;
|
||||
filter->start_time = GST_CLOCK_TIME_NONE;
|
||||
filter->log_fp = NULL;
|
||||
filter->log_header_written = FALSE;
|
||||
filter->video_info_valid = FALSE;
|
||||
filter->ramp_step = 1.0;
|
||||
|
||||
/* Set in-place transform */
|
||||
gst_base_transform_set_in_place (GST_BASE_TRANSFORM (filter), TRUE);
|
||||
gst_base_transform_set_passthrough (GST_BASE_TRANSFORM (filter), TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_set_property (GObject * object, guint prop_id,
|
||||
const GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_ENABLED:
|
||||
filter->enabled = g_value_get_boolean (value);
|
||||
break;
|
||||
case PROP_TARGET_BRIGHTNESS:
|
||||
filter->target_brightness = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_COMPENSATION:
|
||||
filter->compensation = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_EXPOSURE_MIN:
|
||||
filter->exposure_min = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_EXPOSURE_MAX:
|
||||
filter->exposure_max = g_value_get_double (value);
|
||||
break;
|
||||
case PROP_GAIN_MIN:
|
||||
filter->gain_min = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_GAIN_MAX:
|
||||
filter->gain_max = g_value_get_int (value);
|
||||
break;
|
||||
case PROP_RAMP_RATE:
|
||||
filter->ramp_rate = g_value_get_enum (value);
|
||||
break;
|
||||
case PROP_LOG_FILE:
|
||||
g_free (filter->log_file);
|
||||
filter->log_file = g_value_dup_string (value);
|
||||
break;
|
||||
case PROP_CAMERA_ELEMENT:
|
||||
g_free (filter->camera_element_name);
|
||||
filter->camera_element_name = g_value_dup_string (value);
|
||||
/* Try to find camera element when name is set */
|
||||
if (filter->camera_element_name && strlen (filter->camera_element_name) > 0) {
|
||||
filter->camera_src = gst_intervalometer_find_camera_element (filter);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_get_property (GObject * object, guint prop_id,
|
||||
GValue * value, GParamSpec * pspec)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (object);
|
||||
|
||||
switch (prop_id) {
|
||||
case PROP_ENABLED:
|
||||
g_value_set_boolean (value, filter->enabled);
|
||||
break;
|
||||
case PROP_TARGET_BRIGHTNESS:
|
||||
g_value_set_double (value, filter->target_brightness);
|
||||
break;
|
||||
case PROP_COMPENSATION:
|
||||
g_value_set_double (value, filter->compensation);
|
||||
break;
|
||||
case PROP_EXPOSURE_MIN:
|
||||
g_value_set_double (value, filter->exposure_min);
|
||||
break;
|
||||
case PROP_EXPOSURE_MAX:
|
||||
g_value_set_double (value, filter->exposure_max);
|
||||
break;
|
||||
case PROP_GAIN_MIN:
|
||||
g_value_set_int (value, filter->gain_min);
|
||||
break;
|
||||
case PROP_GAIN_MAX:
|
||||
g_value_set_int (value, filter->gain_max);
|
||||
break;
|
||||
case PROP_RAMP_RATE:
|
||||
g_value_set_enum (value, filter->ramp_rate);
|
||||
break;
|
||||
case PROP_LOG_FILE:
|
||||
g_value_set_string (value, filter->log_file);
|
||||
break;
|
||||
case PROP_CAMERA_ELEMENT:
|
||||
g_value_set_string (value, filter->camera_element_name);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_dispose (GObject * object)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (object);
|
||||
|
||||
if (filter->camera_src) {
|
||||
gst_object_unref (filter->camera_src);
|
||||
filter->camera_src = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_finalize (GObject * object)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (object);
|
||||
|
||||
g_free (filter->log_file);
|
||||
g_free (filter->camera_element_name);
|
||||
|
||||
if (filter->log_fp) {
|
||||
fclose (filter->log_fp);
|
||||
filter->log_fp = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_intervalometer_start (GstBaseTransform * trans)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (trans);
|
||||
|
||||
GST_DEBUG_OBJECT (filter, "start");
|
||||
|
||||
filter->frame_count = 0;
|
||||
filter->start_time = gst_clock_get_time (gst_system_clock_obtain ());
|
||||
|
||||
/* Open log file if specified */
|
||||
if (filter->log_file && strlen (filter->log_file) > 0) {
|
||||
filter->log_fp = fopen (filter->log_file, "w");
|
||||
if (!filter->log_fp) {
|
||||
GST_WARNING_OBJECT (filter, "Failed to open log file: %s", filter->log_file);
|
||||
} else {
|
||||
filter->log_header_written = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find camera element if name was provided */
|
||||
if (filter->camera_element_name && strlen (filter->camera_element_name) > 0) {
|
||||
filter->camera_src = gst_intervalometer_find_camera_element (filter);
|
||||
if (!filter->camera_src) {
|
||||
GST_WARNING_OBJECT (filter, "Could not find camera element: %s",
|
||||
filter->camera_element_name);
|
||||
}
|
||||
}
|
||||
|
||||
gst_intervalometer_reset (filter);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_intervalometer_stop (GstBaseTransform * trans)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (trans);
|
||||
|
||||
GST_DEBUG_OBJECT (filter, "stop");
|
||||
|
||||
if (filter->log_fp) {
|
||||
fclose (filter->log_fp);
|
||||
filter->log_fp = NULL;
|
||||
}
|
||||
|
||||
if (filter->camera_src) {
|
||||
gst_object_unref (filter->camera_src);
|
||||
filter->camera_src = NULL;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gst_intervalometer_set_caps (GstBaseTransform * trans, GstCaps * incaps,
|
||||
GstCaps * outcaps)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (trans);
|
||||
|
||||
if (!gst_video_info_from_caps (&filter->video_info, incaps)) {
|
||||
GST_ERROR_OBJECT (filter, "Failed to parse caps");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
filter->video_info_valid = TRUE;
|
||||
|
||||
GST_DEBUG_OBJECT (filter, "Set caps: %" GST_PTR_FORMAT, incaps);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GstFlowReturn
|
||||
gst_intervalometer_transform_ip (GstBaseTransform * trans, GstBuffer * buf)
|
||||
{
|
||||
GstIntervalometer *filter = GST_INTERVALOMETER (trans);
|
||||
gdouble brightness;
|
||||
|
||||
if (!filter->enabled || !filter->video_info_valid) {
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
/* Calculate brightness from current frame */
|
||||
brightness = gst_intervalometer_calculate_brightness (filter, buf);
|
||||
|
||||
/* Update camera settings based on brightness */
|
||||
gst_intervalometer_update_camera_settings (filter, brightness);
|
||||
|
||||
/* Write to log file if enabled */
|
||||
if (filter->log_fp) {
|
||||
gst_intervalometer_write_log (filter, brightness);
|
||||
}
|
||||
|
||||
filter->frame_count++;
|
||||
|
||||
return GST_FLOW_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_reset (GstIntervalometer * filter)
|
||||
{
|
||||
filter->current_exposure = filter->exposure_min;
|
||||
filter->current_gain = filter->gain_min;
|
||||
filter->target_exposure = filter->exposure_min;
|
||||
filter->target_gain = filter->gain_min;
|
||||
filter->frame_count = 0;
|
||||
}
|
||||
|
||||
static gdouble
|
||||
gst_intervalometer_calculate_brightness (GstIntervalometer * filter,
|
||||
GstBuffer * buf)
|
||||
{
|
||||
GstMapInfo map;
|
||||
gdouble sum = 0.0;
|
||||
guint64 pixel_count = 0;
|
||||
guint i;
|
||||
|
||||
if (!gst_buffer_map (buf, &map, GST_MAP_READ)) {
|
||||
GST_WARNING_OBJECT (filter, "Failed to map buffer");
|
||||
return filter->target_brightness;
|
||||
}
|
||||
|
||||
/* Calculate average brightness based on format */
|
||||
switch (GST_VIDEO_INFO_FORMAT (&filter->video_info)) {
|
||||
case GST_VIDEO_FORMAT_GRAY8:
|
||||
{
|
||||
guint8 *data = map.data;
|
||||
pixel_count = map.size;
|
||||
for (i = 0; i < pixel_count; i++) {
|
||||
sum += data[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_VIDEO_FORMAT_GRAY16_LE:
|
||||
case GST_VIDEO_FORMAT_GRAY16_BE:
|
||||
{
|
||||
guint16 *data = (guint16 *) map.data;
|
||||
pixel_count = map.size / 2;
|
||||
for (i = 0; i < pixel_count; i++) {
|
||||
sum += data[i] / 256.0; /* Scale 16-bit to 8-bit range */
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_VIDEO_FORMAT_RGB:
|
||||
case GST_VIDEO_FORMAT_BGR:
|
||||
{
|
||||
/* Calculate luminance: Y = 0.299*R + 0.587*G + 0.114*B */
|
||||
guint8 *data = map.data;
|
||||
pixel_count = map.size / 3;
|
||||
for (i = 0; i < map.size; i += 3) {
|
||||
sum += 0.299 * data[i] + 0.587 * data[i + 1] + 0.114 * data[i + 2];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GST_VIDEO_FORMAT_BGRA:
|
||||
{
|
||||
guint8 *data = map.data;
|
||||
pixel_count = map.size / 4;
|
||||
for (i = 0; i < map.size; i += 4) {
|
||||
sum += 0.299 * data[i + 2] + 0.587 * data[i + 1] + 0.114 * data[i];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
GST_WARNING_OBJECT (filter, "Unsupported video format");
|
||||
gst_buffer_unmap (buf, &map);
|
||||
return filter->target_brightness;
|
||||
}
|
||||
|
||||
gst_buffer_unmap (buf, &map);
|
||||
|
||||
if (pixel_count == 0) {
|
||||
return filter->target_brightness;
|
||||
}
|
||||
|
||||
return sum / pixel_count;
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_update_camera_settings (GstIntervalometer * filter,
|
||||
gdouble brightness)
|
||||
{
|
||||
gdouble error, adjusted_target, exposure_range, gain_range;
|
||||
gdouble ramp_multiplier;
|
||||
|
||||
if (!filter->camera_src) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Calculate brightness error with compensation */
|
||||
error = (filter->target_brightness - brightness) *
|
||||
pow (2.0, filter->compensation);
|
||||
|
||||
/* Adjust target brightness based on error */
|
||||
adjusted_target = filter->target_brightness + error;
|
||||
|
||||
/* Get ramp multiplier */
|
||||
ramp_multiplier = ramp_rate_multipliers[filter->ramp_rate];
|
||||
|
||||
/* Calculate exposure and gain ranges */
|
||||
exposure_range = filter->exposure_max - filter->exposure_min;
|
||||
gain_range = filter->gain_max - filter->gain_min;
|
||||
|
||||
/* Determine target exposure and gain using YASS-like algorithm */
|
||||
if (brightness > adjusted_target) {
|
||||
/* Too bright - decrease exposure first, then gain */
|
||||
if (filter->current_exposure > filter->exposure_min) {
|
||||
filter->target_exposure = filter->current_exposure -
|
||||
(exposure_range * 0.01 * ramp_multiplier);
|
||||
filter->target_exposure = MAX (filter->target_exposure, filter->exposure_min);
|
||||
} else if (filter->current_gain > filter->gain_min) {
|
||||
filter->target_gain = filter->current_gain - (gint) (ramp_multiplier);
|
||||
filter->target_gain = MAX (filter->target_gain, filter->gain_min);
|
||||
}
|
||||
} else {
|
||||
/* Too dark - increase exposure first up to max, then increase gain */
|
||||
if (filter->current_exposure < filter->exposure_max) {
|
||||
filter->target_exposure = filter->current_exposure +
|
||||
(exposure_range * 0.01 * ramp_multiplier);
|
||||
filter->target_exposure = MIN (filter->target_exposure, filter->exposure_max);
|
||||
} else if (filter->current_gain < filter->gain_max) {
|
||||
filter->target_gain = filter->current_gain + (gint) (ramp_multiplier);
|
||||
filter->target_gain = MIN (filter->target_gain, filter->gain_max);
|
||||
}
|
||||
}
|
||||
|
||||
/* Smooth ramping towards target */
|
||||
if (fabs (filter->current_exposure - filter->target_exposure) > 0.001) {
|
||||
filter->current_exposure = filter->target_exposure;
|
||||
g_object_set (filter->camera_src, "exposure", filter->current_exposure, NULL);
|
||||
GST_DEBUG_OBJECT (filter, "Set exposure to %.3f ms", filter->current_exposure);
|
||||
}
|
||||
|
||||
if (filter->current_gain != filter->target_gain) {
|
||||
filter->current_gain = filter->target_gain;
|
||||
g_object_set (filter->camera_src, "gain", filter->current_gain, NULL);
|
||||
GST_DEBUG_OBJECT (filter, "Set gain to %d", filter->current_gain);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gst_intervalometer_write_log (GstIntervalometer * filter, gdouble brightness)
|
||||
{
|
||||
if (!filter->log_fp) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write CSV header on first frame */
|
||||
if (!filter->log_header_written) {
|
||||
fprintf (filter->log_fp,
|
||||
"Frame,Time_s,Brightness,Exposure_ms,Gain,Target_Brightness\n");
|
||||
filter->log_header_written = TRUE;
|
||||
}
|
||||
|
||||
/* Calculate elapsed time */
|
||||
GstClockTime now = gst_clock_get_time (gst_system_clock_obtain ());
|
||||
gdouble elapsed = (now - filter->start_time) / 1000000000.0;
|
||||
|
||||
/* Write data row */
|
||||
fprintf (filter->log_fp, "%llu,%.3f,%.2f,%.3f,%d,%.2f\n",
|
||||
(unsigned long long) filter->frame_count,
|
||||
elapsed,
|
||||
brightness,
|
||||
filter->current_exposure,
|
||||
filter->current_gain,
|
||||
filter->target_brightness);
|
||||
|
||||
fflush (filter->log_fp);
|
||||
}
|
||||
|
||||
static GstElement *
|
||||
gst_intervalometer_find_camera_element (GstIntervalometer * filter)
|
||||
{
|
||||
GstElement *pipeline, *element = NULL;
|
||||
GstBin *bin;
|
||||
|
||||
/* Get pipeline */
|
||||
pipeline = GST_ELEMENT (gst_element_get_parent (GST_ELEMENT (filter)));
|
||||
if (!pipeline) {
|
||||
GST_WARNING_OBJECT (filter, "Not in a pipeline");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Search for element by name */
|
||||
if (GST_IS_BIN (pipeline)) {
|
||||
bin = GST_BIN (pipeline);
|
||||
element = gst_bin_get_by_name (bin, filter->camera_element_name);
|
||||
}
|
||||
|
||||
gst_object_unref (pipeline);
|
||||
|
||||
if (element) {
|
||||
GST_INFO_OBJECT (filter, "Found camera element: %s",
|
||||
filter->camera_element_name);
|
||||
} else {
|
||||
GST_WARNING_OBJECT (filter, "Could not find camera element: %s",
|
||||
filter->camera_element_name);
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
plugin_init (GstPlugin * plugin)
|
||||
{
|
||||
return gst_element_register (plugin, "intervalometer", GST_RANK_NONE,
|
||||
GST_TYPE_INTERVALOMETER);
|
||||
}
|
||||
|
||||
GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
|
||||
GST_VERSION_MINOR,
|
||||
intervalometer,
|
||||
"Automatic exposure control for IDS uEye cameras",
|
||||
plugin_init, GST_PACKAGE_VERSION, GST_PACKAGE_LICENSE, GST_PACKAGE_NAME,
|
||||
GST_PACKAGE_ORIGIN)
|
||||
104
gst/intervalometer/gstintervalometer.h
Normal file
104
gst/intervalometer/gstintervalometer.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* GStreamer
|
||||
* Copyright (C) 2024 FIXME <fixme@example.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_INTERVALOMETER_H__
|
||||
#define __GST_INTERVALOMETER_H__
|
||||
|
||||
#include <stdio.h>
|
||||
#include <gst/base/gstbasetransform.h>
|
||||
#include <gst/video/video.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GST_TYPE_INTERVALOMETER \
|
||||
(gst_intervalometer_get_type())
|
||||
#define GST_INTERVALOMETER(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_INTERVALOMETER,GstIntervalometer))
|
||||
#define GST_INTERVALOMETER_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_INTERVALOMETER,GstIntervalometerClass))
|
||||
#define GST_IS_INTERVALOMETER(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_INTERVALOMETER))
|
||||
#define GST_IS_INTERVALOMETER_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_INTERVALOMETER))
|
||||
|
||||
typedef struct _GstIntervalometer GstIntervalometer;
|
||||
typedef struct _GstIntervalometerClass GstIntervalometerClass;
|
||||
|
||||
typedef enum {
|
||||
RAMP_RATE_VSLOW = 0,
|
||||
RAMP_RATE_SLOW,
|
||||
RAMP_RATE_MEDIUM,
|
||||
RAMP_RATE_FAST,
|
||||
RAMP_RATE_VFAST
|
||||
} GstIntervalometerRampRate;
|
||||
|
||||
/**
|
||||
* GstIntervalometer:
|
||||
* @element: the parent element.
|
||||
*
|
||||
* Auto-exposure controller for IDS uEye cameras.
|
||||
* Inspired by YASS (Yet Another Sunset Script) for CHDK cameras.
|
||||
*/
|
||||
struct _GstIntervalometer
|
||||
{
|
||||
GstBaseTransform element;
|
||||
|
||||
/* Properties */
|
||||
gboolean enabled;
|
||||
gdouble target_brightness; /* Target average brightness (0-255) */
|
||||
gdouble compensation; /* Exposure compensation in stops */
|
||||
gdouble exposure_min; /* Minimum exposure in ms */
|
||||
gdouble exposure_max; /* Maximum exposure in ms */
|
||||
gint gain_min; /* Minimum gain (0-100) */
|
||||
gint gain_max; /* Maximum gain (0-100) */
|
||||
GstIntervalometerRampRate ramp_rate;
|
||||
gchar *log_file; /* CSV log file path */
|
||||
gchar *camera_element_name; /* Name of upstream idsueyesrc element */
|
||||
|
||||
/* Internal state */
|
||||
GstElement *camera_src; /* Reference to upstream camera element */
|
||||
gdouble current_exposure; /* Current exposure setting */
|
||||
gint current_gain; /* Current gain setting */
|
||||
gdouble target_exposure; /* Target exposure for ramping */
|
||||
gint target_gain; /* Target gain for ramping */
|
||||
|
||||
guint64 frame_count; /* Number of frames processed */
|
||||
GstClockTime start_time; /* Time when processing started */
|
||||
|
||||
FILE *log_fp; /* Log file handle */
|
||||
gboolean log_header_written; /* Whether CSV header has been written */
|
||||
|
||||
/* Video info */
|
||||
GstVideoInfo video_info;
|
||||
gboolean video_info_valid;
|
||||
|
||||
/* Ramping parameters */
|
||||
gdouble ramp_step; /* Current ramping step size */
|
||||
};
|
||||
|
||||
struct _GstIntervalometerClass
|
||||
{
|
||||
GstBaseTransformClass parent_class;
|
||||
};
|
||||
|
||||
GType gst_intervalometer_get_type(void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GST_INTERVALOMETER_H__ */
|
||||
Reference in New Issue
Block a user