diff --git a/scripts/camera_control.py b/scripts/camera_control.py index df8582e..21881e5 100644 --- a/scripts/camera_control.py +++ b/scripts/camera_control.py @@ -15,6 +15,8 @@ Usage: uv run scripts/camera_control.py set-framerate 30 # Set framerate to 30fps uv run scripts/camera_control.py get-gain # Get current gain uv run scripts/camera_control.py set-gain 50 # Set gain to 50 + uv run scripts/camera_control.py get-auto-exposure # Get auto-exposure status + uv run scripts/camera_control.py set-auto-exposure 1 # Enable auto-exposure uv run scripts/camera_control.py status # Get pipeline status This script provides both individual control commands and full test suite functionality @@ -182,6 +184,8 @@ Examples: %(prog)s set-framerate 30 # Set framerate to 30fps %(prog)s get-gain # Get current gain %(prog)s set-gain 50 # Set gain to 50 + %(prog)s get-auto-exposure # Get auto-exposure status + %(prog)s set-auto-exposure 1 # Enable auto-exposure %(prog)s status # Get pipeline status """, formatter_class=argparse.RawDescriptionHelpFormatter @@ -190,7 +194,7 @@ Examples: parser.add_argument('command', nargs='?', choices=['test', 'get-exposure', 'set-exposure', 'get-framerate', 'set-framerate', - 'get-gain', 'set-gain', 'status'], + 'get-gain', 'set-gain', 'get-auto-exposure', 'set-auto-exposure', 'status'], help='Command to execute') parser.add_argument('value', @@ -259,6 +263,22 @@ Examples: sys.exit(1) success = simple_command(f"SET_GAIN {gain_value}", f"Setting gain to {gain_value}") + elif args.command == 'get-auto-exposure': + success = simple_command("GET_AUTO_EXPOSURE", "Getting auto-exposure status") + + elif args.command == 'set-auto-exposure': + if args.value is None: + print("Error: set-auto-exposure requires a value (0=off, 1=on)") + parser.print_help() + sys.exit(1) + # Convert to int + ae_value = int(args.value) + if ae_value not in [0, 1]: + print("Error: Auto-exposure must be 0 (off) or 1 (on)") + sys.exit(1) + success = simple_command(f"SET_AUTO_EXPOSURE {ae_value}", + f"{'Enabling' if ae_value else 'Disabling'} auto-exposure") + elif args.command == 'status': success = simple_command("STATUS", "Getting pipeline status") diff --git a/scripts/launch-ids.py b/scripts/launch-ids.py index 7fd91f7..3a63407 100644 --- a/scripts/launch-ids.py +++ b/scripts/launch-ids.py @@ -26,7 +26,9 @@ # - Configurable video streaming (default: UDP port 5000 to 127.0.0.1) # - Optional control interface (default: UDP port 5001 on 0.0.0.0) # - Dynamic exposure control (0.015-30000 milliseconds, default: 10ms) +# - Auto-exposure mode support (--auto-exposure flag) # - Dynamic framerate control (1-20000 fps, default: 750fps) +# - Dynamic gain control (0-100, 0 for auto, default: 0) # - Dynamic camera ID selection (0-254, default: 0 for first found) # - Dynamic device ID selection (0-254, system enumeration ID) # - Configurable video cropping (default: crop 3 pixels from bottom) @@ -40,11 +42,13 @@ # GET_FRAMERATE - Get current framerate # SET_CAMERA_ID - Set camera ID (0-254, 0 is first found) # GET_CAMERA_ID - Get current camera ID -# SET_DEVICE_ID - Set device ID (0-254, system enumeration) -# GET_DEVICE_ID - Get current device ID -# SET_GAIN - Set master gain (0-100, 0 for auto) -# GET_GAIN - Get current gain value -# STATUS - Get pipeline status and current settings +# SET_DEVICE_ID - Set device ID (0-254, system enumeration) +# GET_DEVICE_ID - Get current device ID +# SET_GAIN - Set master gain (0-100, 0 for auto) +# GET_GAIN - Get current gain value +# SET_AUTO_EXPOSURE <0|1> - Enable (1) or disable (0) auto-exposure +# GET_AUTO_EXPOSURE - Get auto-exposure status +# STATUS - Get pipeline status and current settings # # Example Control Usage: # echo "SET_EXPOSURE 10" | nc -u 127.0.0.1 5001 @@ -134,7 +138,7 @@ class ControlServer: print(f"Control server listening on UDP port {self.port}") print(" Commands: SET_EXPOSURE , GET_EXPOSURE, SET_FRAMERATE , GET_FRAMERATE,") print(" SET_CAMERA_ID , GET_CAMERA_ID, SET_DEVICE_ID , GET_DEVICE_ID,") - print(" SET_GAIN , GET_GAIN, STATUS") + print(" SET_GAIN , GET_GAIN, SET_AUTO_EXPOSURE <0|1>, GET_AUTO_EXPOSURE, STATUS") while self.running: try: @@ -192,6 +196,10 @@ class ControlServer: return self.handle_set_gain(parts) elif cmd == "GET_GAIN": return self.handle_get_gain() + elif cmd == "SET_AUTO_EXPOSURE": + return self.handle_set_auto_exposure(parts) + elif cmd == "GET_AUTO_EXPOSURE": + return self.handle_get_auto_exposure() elif cmd == "STATUS": return self.handle_status() else: @@ -336,6 +344,33 @@ class ControlServer: except Exception as e: return f"ERROR: {str(e)}" + def handle_set_auto_exposure(self, parts): + """Handle SET_AUTO_EXPOSURE command""" + if len(parts) != 2: + return "ERROR INVALID_SYNTAX: Usage: SET_AUTO_EXPOSURE <0|1>" + + try: + value = int(parts[1]) + if value not in [0, 1]: + return "ERROR OUT_OF_RANGE: Auto-exposure must be 0 (off) or 1 (on)" + + self.src.set_property("auto-exposure", bool(value)) + actual = self.src.get_property("auto-exposure") + return f"OK {int(actual)}" + + except ValueError: + return "ERROR INVALID_SYNTAX: Auto-exposure must be 0 or 1" + except Exception as e: + return f"ERROR: {str(e)}" + + def handle_get_auto_exposure(self): + """Handle GET_AUTO_EXPOSURE command""" + try: + value = self.src.get_property("auto-exposure") + return f"OK {int(value)}" + except Exception as e: + return f"ERROR: {str(e)}" + def handle_status(self): """Handle STATUS command""" try: @@ -344,6 +379,7 @@ class ControlServer: camera_id = self.src.get_property("camera-id") device_id = self.src.get_property("device-id") gain = self.src.get_property("gain") + auto_exposure = int(self.src.get_property("auto-exposure")) # Get pipeline state state = "UNKNOWN" @@ -351,7 +387,7 @@ class ControlServer: _, current_state, _ = self.pipeline.get_state(0) state = current_state.value_nick.upper() - return f"OK exposure={exposure} framerate={framerate} camera_id={camera_id} device_id={device_id} gain={gain} state={state}" + return f"OK exposure={exposure} framerate={framerate} camera_id={camera_id} device_id={device_id} gain={gain} auto_exposure={auto_exposure} state={state}" except Exception as e: return f"ERROR: {str(e)}" @@ -424,6 +460,11 @@ Examples: metavar='GAIN', help='Master gain (0-100, 0 for auto/default)' ) + camera_group.add_argument( + '--auto-exposure', + action='store_true', + help='Enable automatic exposure control' + ) # Video processing video_group = parser.add_argument_group('Video Processing') @@ -576,6 +617,9 @@ src.set_property("device-id", args.device_id) # Gain (0 for auto/default) src.set_property("gain", args.gain) +# Auto-exposure +src.set_property("auto-exposure", args.auto_exposure) + # Video crop to remove bottom pixels (if enabled) elements_to_link = [src] if args.crop_bottom > 0: diff --git a/sys/idsueye/gstidsueyesrc.c b/sys/idsueye/gstidsueyesrc.c index d6dceec..a28db70 100644 --- a/sys/idsueye/gstidsueyesrc.c +++ b/sys/idsueye/gstidsueyesrc.c @@ -74,7 +74,8 @@ enum PROP_TIMEOUT, PROP_EXPOSURE, PROP_FRAMERATE, - PROP_GAIN + PROP_GAIN, + PROP_AUTO_EXPOSURE }; #define DEFAULT_PROP_CAMERA_ID 0 @@ -85,6 +86,7 @@ enum #define DEFAULT_PROP_EXPOSURE 0 #define DEFAULT_PROP_FRAMERATE 0 #define DEFAULT_PROP_GAIN 0 +#define DEFAULT_PROP_AUTO_EXPOSURE FALSE /* pad templates */ @@ -169,6 +171,10 @@ gst_idsueyesrc_class_init (GstIdsueyeSrcClass * klass) "Master gain (0-100, 0 for auto)", 0, 100, DEFAULT_PROP_GAIN, (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + g_object_class_install_property (gobject_class, PROP_AUTO_EXPOSURE, + g_param_spec_boolean ("auto-exposure", "Auto Exposure", + "Enable automatic exposure control", DEFAULT_PROP_AUTO_EXPOSURE, + (GParamFlags) (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); } static void @@ -204,6 +210,7 @@ gst_idsueyesrc_init (GstIdsueyeSrc * src) src->exposure = DEFAULT_PROP_EXPOSURE; src->framerate = DEFAULT_PROP_FRAMERATE; src->gain = DEFAULT_PROP_GAIN; + src->auto_exposure = DEFAULT_PROP_AUTO_EXPOSURE; src->stop_requested = FALSE; src->caps = NULL; @@ -259,6 +266,12 @@ gst_idsueyesrc_set_property (GObject * object, guint property_id, } } break; + case PROP_AUTO_EXPOSURE: + src->auto_exposure = g_value_get_boolean (value); + if (src->is_started) { + gst_idsueyesrc_set_framerate_exposure (src); + } + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -299,6 +312,9 @@ gst_idsueyesrc_get_property (GObject * object, guint property_id, case PROP_GAIN: g_value_set_int (value, src->gain); break; + case PROP_AUTO_EXPOSURE: + g_value_set_boolean (value, src->auto_exposure); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; @@ -925,12 +941,34 @@ gst_idsueyesrc_set_framerate_exposure (GstIdsueyeSrc * src) success = FALSE; } - ret = - is_Exposure (src->hCam, IS_EXPOSURE_CMD_SET_EXPOSURE, &src->exposure, 8); - if (ret != IS_SUCCESS) { - GST_WARNING_OBJECT (src, "Failed to set exposure to %.3f (error %d)", - src->exposure, ret); - success = FALSE; + /* Handle auto-exposure */ + if (src->auto_exposure) { + /* Enable auto shutter (auto exposure) */ + double enable = 1.0; + ret = is_SetAutoParameter (src->hCam, IS_SET_ENABLE_AUTO_SHUTTER, + &enable, NULL); + if (ret != IS_SUCCESS) { + GST_WARNING_OBJECT (src, "Failed to enable auto exposure (error %d)", ret); + success = FALSE; + } else { + GST_DEBUG_OBJECT (src, "Auto exposure enabled"); + } + } else { + /* Disable auto shutter and set manual exposure */ + double disable = 0.0; + ret = is_SetAutoParameter (src->hCam, IS_SET_ENABLE_AUTO_SHUTTER, + &disable, NULL); + if (ret != IS_SUCCESS) { + GST_WARNING_OBJECT (src, "Failed to disable auto exposure (error %d)", ret); + } + + /* Set manual exposure */ + ret = is_Exposure (src->hCam, IS_EXPOSURE_CMD_SET_EXPOSURE, &src->exposure, 8); + if (ret != IS_SUCCESS) { + GST_WARNING_OBJECT (src, "Failed to set exposure to %.3f (error %d)", + src->exposure, ret); + success = FALSE; + } } /* Set gain if specified (0 means use default/auto) */ diff --git a/sys/idsueye/gstidsueyesrc.h b/sys/idsueye/gstidsueyesrc.h index 79b092d..7a62049 100644 --- a/sys/idsueye/gstidsueyesrc.h +++ b/sys/idsueye/gstidsueyesrc.h @@ -58,6 +58,7 @@ struct _GstIdsueyeSrc gdouble exposure; gdouble framerate; gint gain; + gboolean auto_exposure; GstClockTime acq_start_time; guint32 last_frame_count;