[libcamera-devel,v5.2,4/7] libcamera: controls: Updates to gain and exposure controls

Message ID 20200426002328.15500-1-laurent.pinchart@ideasonboard.com
State Accepted
Headers show
Series
  • Untitled series #827
Related show

Commit Message

Laurent Pinchart April 26, 2020, 12:23 a.m. UTC
From: Naushir Patuck <naush@raspberrypi.com>

Rename:
ManualExposure -> ExposureTime
ManualGain -> AnalogueGain

Use micro-seconds units for ExposureTime. This is changed from milli-
seconds. The latter would not allow very low exposure times.
AnalogueGain switch to use a float to allow fractional gain adjustments.

Update the uvcvideo pipeline handler to use the new exposure and gain
units. For ExposureTime, UVC uses units of 100 micro-seconds, so map
the values before setting V4L2_CID_EXPOSURE_ABSOLUTE. For AnalogueGain,
UVC has no explicit gain units, so map the default gain value to 1.0
and linearly scale to the requested value.

Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
---
Changes since v5:

- Include math.h in uvcvideo.cpp
---
 src/libcamera/control_ids.yaml               | 22 ++++--
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 78 +++++++++++++++++++-
 2 files changed, 88 insertions(+), 12 deletions(-)

Comments

Jacopo Mondi April 27, 2020, 2:44 p.m. UTC | #1
Hi Laurent,

On Sun, Apr 26, 2020 at 03:23:28AM +0300, Laurent Pinchart wrote:
> From: Naushir Patuck <naush@raspberrypi.com>
>
> Rename:
> ManualExposure -> ExposureTime
> ManualGain -> AnalogueGain
>
> Use micro-seconds units for ExposureTime. This is changed from milli-
> seconds. The latter would not allow very low exposure times.
> AnalogueGain switch to use a float to allow fractional gain adjustments.
>
> Update the uvcvideo pipeline handler to use the new exposure and gain
> units. For ExposureTime, UVC uses units of 100 micro-seconds, so map
> the values before setting V4L2_CID_EXPOSURE_ABSOLUTE. For AnalogueGain,
> UVC has no explicit gain units, so map the default gain value to 1.0
> and linearly scale to the requested value.
>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>

Thanks
  j

> ---
> Changes since v5:
>
> - Include math.h in uvcvideo.cpp
> ---
>  src/libcamera/control_ids.yaml               | 22 ++++--
>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 78 +++++++++++++++++++-
>  2 files changed, 88 insertions(+), 12 deletions(-)
>
> diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
> index bcbab195a374..d8bdb3829be4 100644
> --- a/src/libcamera/control_ids.yaml
> +++ b/src/libcamera/control_ids.yaml
> @@ -12,7 +12,7 @@ controls:
>        description: |
>          Enable or disable the AE.
>
> -        \sa ManualExposure
> +        \sa ExposureTime AnalogueGain
>
>    - AeLocked:
>        type: bool
> @@ -30,8 +30,6 @@ controls:
>        description: |
>          Enable or disable the AWB.
>
> -        \sa ManualGain
> -
>    - Brightness:
>        type: int32_t
>        description: Specify a fixed brightness parameter
> @@ -44,12 +42,20 @@ controls:
>        type: int32_t
>        description: Specify a fixed saturation parameter
>
> -  - ManualExposure:
> +  - ExposureTime:
>        type: int32_t
> -      description: Specify a fixed exposure time in milli-seconds
> +      description: |
> +        Exposure time (shutter speed) for the frame applied in the sensor
> +        device. This value is specified in micro-seconds.
>
> -  - ManualGain:
> -      type: int32_t
> -      description: Specify a fixed gain parameter
> +        \sa AnalogueGain AeEnable
> +
> +  - AnalogueGain:
> +      type: float
> +      description: |
> +        Analogue gain value applied in the sensor device.
> +        The value of the control specifies the gain multiplier applied to all
> +        colour channels. This value cannot be lower than 1.0.
>
> +        \sa ExposureTime AeEnable
>  ...
> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> index b040f2460b1c..08462542a79b 100644
> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> @@ -7,6 +7,7 @@
>
>  #include <algorithm>
>  #include <iomanip>
> +#include <math.h>
>  #include <sys/sysmacros.h>
>  #include <tuple>
>
> @@ -254,13 +255,19 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
>  		cid = V4L2_CID_SATURATION;
>  	else if (id == controls::AeEnable)
>  		cid = V4L2_CID_EXPOSURE_AUTO;
> -	else if (id == controls::ManualExposure)
> +	else if (id == controls::ExposureTime)
>  		cid = V4L2_CID_EXPOSURE_ABSOLUTE;
> -	else if (id == controls::ManualGain)
> +	else if (id == controls::AnalogueGain)
>  		cid = V4L2_CID_GAIN;
>  	else
>  		return -EINVAL;
>
> +	const ControlInfo &v4l2Info = controls->infoMap()->at(cid);
> +
> +	/*
> +	 * See UVCCameraData::addControl() for explanations of the different
> +	 * value mappings.
> +	 */
>  	switch (cid) {
>  	case V4L2_CID_EXPOSURE_AUTO: {
>  		int32_t ivalue = value.get<bool>()
> @@ -270,6 +277,28 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
>  		break;
>  	}
>
> +	case V4L2_CID_EXPOSURE_ABSOLUTE:
> +		controls->set(cid, value.get<int32_t>() / 100);
> +		break;
> +
> +	case V4L2_CID_GAIN: {
> +		int32_t min = v4l2Info.min().get<int32_t>();
> +		int32_t max = v4l2Info.max().get<int32_t>();
> +		int32_t def = v4l2Info.def().get<int32_t>();
> +
> +		float m = (4.0f - 1.0f) / (max - def);
> +		float p = 1.0f - m * def;
> +
> +		if (m * min + p < 0.5f) {
> +			m = (1.0f - 0.5f) / (def - min);
> +			p = 1.0f - m * def;
> +		}
> +
> +		float fvalue = (value.get<float>() - p) / m;
> +		controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));
> +		break;
> +	}
> +
>  	default: {
>  		int32_t ivalue = value.get<int32_t>();
>  		controls->set(cid, ivalue);
> @@ -413,21 +442,62 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
>  		id = &controls::AeEnable;
>  		break;
>  	case V4L2_CID_EXPOSURE_ABSOLUTE:
> -		id = &controls::ManualExposure;
> +		id = &controls::ExposureTime;
>  		break;
>  	case V4L2_CID_GAIN:
> -		id = &controls::ManualGain;
> +		id = &controls::AnalogueGain;
>  		break;
>  	default:
>  		return;
>  	}
>
>  	/* Map the control info. */
> +	int32_t min = v4l2Info.min().get<int32_t>();
> +	int32_t max = v4l2Info.max().get<int32_t>();
> +	int32_t def = v4l2Info.def().get<int32_t>();
> +
>  	switch (cid) {
>  	case V4L2_CID_EXPOSURE_AUTO:
>  		info = ControlInfo{ false, true, true };
>  		break;
>
> +	case V4L2_CID_EXPOSURE_ABSOLUTE:
> +		/*
> +		 * ExposureTime is in units of 1 µs, and UVC expects
> +		 * V4L2_CID_EXPOSURE_ABSOLUTE in units of 100 µs.
> +		 */
> +		info = ControlInfo{
> +			{ min * 100 },
> +			{ max * 100 },
> +			{ def * 100 }
> +		};
> +		break;
> +
> +	case V4L2_CID_GAIN: {
> +		/*
> +		 * The AnalogueGain control is a float, with 1.0 mapped to the
> +		 * default value. UVC doesn't specify units, and cameras have
> +		 * been seen to expose very different ranges for the gain
> +		 * control. Arbitrarily assume that the minimum and maximum
> +		 * values are respectively no lower than 0.5 and no higher than
> +		 * 4.0.
> +		 */
> +		float m = (4.0f - 1.0f) / (max - def);
> +		float p = 1.0f - m * def;
> +
> +		if (m * min + p < 0.5f) {
> +			m = (1.0f - 0.5f) / (def - min);
> +			p = 1.0f - m * def;
> +		}
> +
> +		info = ControlInfo{
> +			{ m * min + p },
> +			{ m * max + p },
> +			{ 1.0f }
> +		};
> +		break;
> +	}
> +
>  	default:
>  		info = v4l2Info;
>  		break;
> --
> Regards,
>
> Laurent Pinchart
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml
index bcbab195a374..d8bdb3829be4 100644
--- a/src/libcamera/control_ids.yaml
+++ b/src/libcamera/control_ids.yaml
@@ -12,7 +12,7 @@  controls:
       description: |
         Enable or disable the AE.
 
-        \sa ManualExposure
+        \sa ExposureTime AnalogueGain
 
   - AeLocked:
       type: bool
@@ -30,8 +30,6 @@  controls:
       description: |
         Enable or disable the AWB.
 
-        \sa ManualGain
-
   - Brightness:
       type: int32_t
       description: Specify a fixed brightness parameter
@@ -44,12 +42,20 @@  controls:
       type: int32_t
       description: Specify a fixed saturation parameter
 
-  - ManualExposure:
+  - ExposureTime:
       type: int32_t
-      description: Specify a fixed exposure time in milli-seconds
+      description: |
+        Exposure time (shutter speed) for the frame applied in the sensor
+        device. This value is specified in micro-seconds.
 
-  - ManualGain:
-      type: int32_t
-      description: Specify a fixed gain parameter
+        \sa AnalogueGain AeEnable
+
+  - AnalogueGain:
+      type: float
+      description: |
+        Analogue gain value applied in the sensor device.
+        The value of the control specifies the gain multiplier applied to all
+        colour channels. This value cannot be lower than 1.0.
 
+        \sa ExposureTime AeEnable
 ...
diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index b040f2460b1c..08462542a79b 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -7,6 +7,7 @@ 
 
 #include <algorithm>
 #include <iomanip>
+#include <math.h>
 #include <sys/sysmacros.h>
 #include <tuple>
 
@@ -254,13 +255,19 @@  int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
 		cid = V4L2_CID_SATURATION;
 	else if (id == controls::AeEnable)
 		cid = V4L2_CID_EXPOSURE_AUTO;
-	else if (id == controls::ManualExposure)
+	else if (id == controls::ExposureTime)
 		cid = V4L2_CID_EXPOSURE_ABSOLUTE;
-	else if (id == controls::ManualGain)
+	else if (id == controls::AnalogueGain)
 		cid = V4L2_CID_GAIN;
 	else
 		return -EINVAL;
 
+	const ControlInfo &v4l2Info = controls->infoMap()->at(cid);
+
+	/*
+	 * See UVCCameraData::addControl() for explanations of the different
+	 * value mappings.
+	 */
 	switch (cid) {
 	case V4L2_CID_EXPOSURE_AUTO: {
 		int32_t ivalue = value.get<bool>()
@@ -270,6 +277,28 @@  int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
 		break;
 	}
 
+	case V4L2_CID_EXPOSURE_ABSOLUTE:
+		controls->set(cid, value.get<int32_t>() / 100);
+		break;
+
+	case V4L2_CID_GAIN: {
+		int32_t min = v4l2Info.min().get<int32_t>();
+		int32_t max = v4l2Info.max().get<int32_t>();
+		int32_t def = v4l2Info.def().get<int32_t>();
+
+		float m = (4.0f - 1.0f) / (max - def);
+		float p = 1.0f - m * def;
+
+		if (m * min + p < 0.5f) {
+			m = (1.0f - 0.5f) / (def - min);
+			p = 1.0f - m * def;
+		}
+
+		float fvalue = (value.get<float>() - p) / m;
+		controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));
+		break;
+	}
+
 	default: {
 		int32_t ivalue = value.get<int32_t>();
 		controls->set(cid, ivalue);
@@ -413,21 +442,62 @@  void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
 		id = &controls::AeEnable;
 		break;
 	case V4L2_CID_EXPOSURE_ABSOLUTE:
-		id = &controls::ManualExposure;
+		id = &controls::ExposureTime;
 		break;
 	case V4L2_CID_GAIN:
-		id = &controls::ManualGain;
+		id = &controls::AnalogueGain;
 		break;
 	default:
 		return;
 	}
 
 	/* Map the control info. */
+	int32_t min = v4l2Info.min().get<int32_t>();
+	int32_t max = v4l2Info.max().get<int32_t>();
+	int32_t def = v4l2Info.def().get<int32_t>();
+
 	switch (cid) {
 	case V4L2_CID_EXPOSURE_AUTO:
 		info = ControlInfo{ false, true, true };
 		break;
 
+	case V4L2_CID_EXPOSURE_ABSOLUTE:
+		/*
+		 * ExposureTime is in units of 1 µs, and UVC expects
+		 * V4L2_CID_EXPOSURE_ABSOLUTE in units of 100 µs.
+		 */
+		info = ControlInfo{
+			{ min * 100 },
+			{ max * 100 },
+			{ def * 100 }
+		};
+		break;
+
+	case V4L2_CID_GAIN: {
+		/*
+		 * The AnalogueGain control is a float, with 1.0 mapped to the
+		 * default value. UVC doesn't specify units, and cameras have
+		 * been seen to expose very different ranges for the gain
+		 * control. Arbitrarily assume that the minimum and maximum
+		 * values are respectively no lower than 0.5 and no higher than
+		 * 4.0.
+		 */
+		float m = (4.0f - 1.0f) / (max - def);
+		float p = 1.0f - m * def;
+
+		if (m * min + p < 0.5f) {
+			m = (1.0f - 0.5f) / (def - min);
+			p = 1.0f - m * def;
+		}
+
+		info = ControlInfo{
+			{ m * min + p },
+			{ m * max + p },
+			{ 1.0f }
+		};
+		break;
+	}
+
 	default:
 		info = v4l2Info;
 		break;