From patchwork Sun Apr 26 00:23:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3546 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 50B1262EFC for ; Sun, 26 Apr 2020 02:23:48 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="ZEcC//kS"; dkim-atps=neutral Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id AA4A14F7; Sun, 26 Apr 2020 02:23:47 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1587860627; bh=7BPJ2lzQDMhSYH+vtzJpSfVhtUTJw7rYHgmpgGoPMoM=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ZEcC//kSu0VpIckPWnYRYoWy69h1oPneC2CWoxfwGPwDoC6G39u38Fe4NS++YE4vQ zHCYpydwfrkJ5tCv9zEUZIKlU2PKMx4mboLveRFOJcwly9/4JWInJc2baGDGvlcpUn 4KdF4to1aWM3XnlMIbY6HJIa9sjsKHDoZLupAwCk= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 26 Apr 2020 03:23:28 +0300 Message-Id: <20200426002328.15500-1-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.25.3 In-Reply-To: <20200425004533.26907-5-laurent.pinchart@ideasonboard.com> References: <20200425004533.26907-5-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5.2 4/7] libcamera: controls: Updates to gain and exposure controls X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 26 Apr 2020 00:23:48 -0000 From: Naushir Patuck 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 Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- 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 #include +#include #include #include @@ -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() @@ -270,6 +277,28 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id, break; } + case V4L2_CID_EXPOSURE_ABSOLUTE: + controls->set(cid, value.get() / 100); + break; + + case V4L2_CID_GAIN: { + int32_t min = v4l2Info.min().get(); + int32_t max = v4l2Info.max().get(); + int32_t def = v4l2Info.def().get(); + + 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() - p) / m; + controls->set(cid, static_cast(lroundf(fvalue))); + break; + } + default: { int32_t ivalue = value.get(); 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 max = v4l2Info.max().get(); + int32_t def = v4l2Info.def().get(); + 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; From patchwork Sun Apr 26 00:25:30 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Pinchart X-Patchwork-Id: 3547 Return-Path: Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 1615062EFC for ; Sun, 26 Apr 2020 02:25:49 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="XHT9wJyG"; dkim-atps=neutral Received: from pendragon.bb.dnainternet.fi (81-175-216-236.bb.dnainternet.fi [81.175.216.236]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 8888A4F7; Sun, 26 Apr 2020 02:25:48 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1587860748; bh=ej33UgLvBW44zf0kIkTN0D3jComZd8XBpj6vtQOCV1U=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XHT9wJyGOazsoAaKtS6Mq0hXGsF/rMyx5LTgJdRUSGuYZq+rbDxyIQkgzXoYw5l1s 8j/zC4FJXQPp1hmQkEBgBblzsA0b/cjRnQRBqiXuDyc6t4/IQX3RwVKh/1xbCX+uSV q3IMrOu+BYH8pP01c9po8/UsmgxyUAm3weHg0SWc= From: Laurent Pinchart To: libcamera-devel@lists.libcamera.org Date: Sun, 26 Apr 2020 03:25:30 +0300 Message-Id: <20200426002530.24747-1-laurent.pinchart@ideasonboard.com> X-Mailer: git-send-email 2.25.3 In-Reply-To: <20200425004533.26907-6-laurent.pinchart@ideasonboard.com> References: <20200425004533.26907-6-laurent.pinchart@ideasonboard.com> MIME-Version: 1.0 Subject: [libcamera-devel] [PATCH v5.1 5/7] libcamera: controls: Reorder and update description of existing controls X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 26 Apr 2020 00:25:49 -0000 From: Naushir Patuck Group AE, AWB, etc. controls together for accessibility. Update descriptions for Contrast, Brightness, and Saturation controls. Update the uvcvideo and vimc pipeline handlers to use the new brightness, contrast and saturation units. UVC has no explicit units for those controls, so map them with a best effort heuristic. For VIMC, hardcode the control range based on knowledge of the driver implementation for simplicity. Signed-off-by: Naushir Patuck Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Jacopo Mondi --- Changes since v5: - Include math.h in vimc.cpp --- src/libcamera/control_ids.yaml | 42 +++++++----- src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 67 +++++++++++++++++--- src/libcamera/pipeline/vimc/vimc.cpp | 30 ++++++--- 3 files changed, 104 insertions(+), 35 deletions(-) diff --git a/src/libcamera/control_ids.yaml b/src/libcamera/control_ids.yaml index d8bdb3829be4..f2ac052b3d3e 100644 --- a/src/libcamera/control_ids.yaml +++ b/src/libcamera/control_ids.yaml @@ -25,23 +25,6 @@ controls: \sa AeEnable - - AwbEnable: - type: bool - description: | - Enable or disable the AWB. - - - Brightness: - type: int32_t - description: Specify a fixed brightness parameter - - - Contrast: - type: int32_t - description: Specify a fixed contrast parameter - - - Saturation: - type: int32_t - description: Specify a fixed saturation parameter - - ExposureTime: type: int32_t description: | @@ -58,4 +41,29 @@ controls: colour channels. This value cannot be lower than 1.0. \sa ExposureTime AeEnable + + - Brightness: + type: float + description: | + Specify a fixed brightness parameter. Positive values (up to 1.0) + produce brighter images; negative values (up to -1.0) produce darker + images and 0.0 leaves pixels unchanged. + + - Contrast: + type: float + description: | + Specify a fixed contrast parameter. Normal contrast is given by the + value 1.0; larger values produce images with more contrast. + + - AwbEnable: + type: bool + description: | + Enable or disable the AWB. + + - Saturation: + type: float + description: | + Specify a fixed saturation parameter. Normal saturation is given by + the value 1.0; larger values produce more saturated colours; 0.0 + produces a greyscale image. ... diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp index 08462542a79b..030ac6864752 100644 --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp @@ -263,12 +263,29 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id, return -EINVAL; const ControlInfo &v4l2Info = controls->infoMap()->at(cid); + int32_t min = v4l2Info.min().get(); + int32_t def = v4l2Info.def().get(); + int32_t max = v4l2Info.max().get(); /* * See UVCCameraData::addControl() for explanations of the different * value mappings. */ switch (cid) { + case V4L2_CID_BRIGHTNESS: { + float scale = std::max(max - def, def - min); + float fvalue = value.get() * scale + def; + controls->set(cid, static_cast(lroundf(fvalue))); + break; + } + + case V4L2_CID_SATURATION: { + float scale = def - min; + float fvalue = value.get() * scale + min; + controls->set(cid, static_cast(lroundf(fvalue))); + break; + } + case V4L2_CID_EXPOSURE_AUTO: { int32_t ivalue = value.get() ? V4L2_EXPOSURE_APERTURE_PRIORITY @@ -281,11 +298,8 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id, controls->set(cid, value.get() / 100); break; + case V4L2_CID_CONTRAST: case V4L2_CID_GAIN: { - int32_t min = v4l2Info.min().get(); - int32_t max = v4l2Info.max().get(); - int32_t def = v4l2Info.def().get(); - float m = (4.0f - 1.0f) / (max - def); float p = 1.0f - m * def; @@ -457,6 +471,38 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info, int32_t def = v4l2Info.def().get(); switch (cid) { + case V4L2_CID_BRIGHTNESS: { + /* + * The Brightness control is a float, with 0.0 mapped to the + * default value. The control range is [-1.0, 1.0], but the V4L2 + * default may not be in the middle of the V4L2 range. + * Accommodate this by restricting the range of the libcamera + * control, but always within the maximum limits. + */ + float scale = std::max(max - def, def - min); + + info = ControlInfo{ + { static_cast(min - def) / scale }, + { static_cast(max - def) / scale }, + { 0.0f } + }; + break; + } + + case V4L2_CID_SATURATION: + /* + * The Saturation control is a float, with 0.0 mapped to the + * minimum value (corresponding to a fully desaturated image) + * and 1.0 mapped to the default value. Calculate the maximum + * value accordingly. + */ + info = ControlInfo{ + { 0.0f }, + { static_cast(max - min) / (def - min) }, + { 1.0f } + }; + break; + case V4L2_CID_EXPOSURE_AUTO: info = ControlInfo{ false, true, true }; break; @@ -473,14 +519,15 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info, }; break; + case V4L2_CID_CONTRAST: 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. + * The Contrast and AnalogueGain controls are floats, 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 controls. 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; diff --git a/src/libcamera/pipeline/vimc/vimc.cpp b/src/libcamera/pipeline/vimc/vimc.cpp index c5eea3a01b0e..77de4c38bf01 100644 --- a/src/libcamera/pipeline/vimc/vimc.cpp +++ b/src/libcamera/pipeline/vimc/vimc.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -304,14 +305,24 @@ int PipelineHandlerVimc::processControls(VimcCameraData *data, Request *request) for (auto it : request->controls()) { unsigned int id = it.first; - ControlValue &value = it.second; + unsigned int offset; + uint32_t cid; - if (id == controls::Brightness) - controls.set(V4L2_CID_BRIGHTNESS, value); - else if (id == controls::Contrast) - controls.set(V4L2_CID_CONTRAST, value); - else if (id == controls::Saturation) - controls.set(V4L2_CID_SATURATION, value); + if (id == controls::Brightness) { + cid = V4L2_CID_BRIGHTNESS; + offset = 128; + } else if (id == controls::Contrast) { + cid = V4L2_CID_CONTRAST; + offset = 0; + } else if (id == controls::Saturation) { + cid = V4L2_CID_SATURATION; + offset = 0; + } else { + continue; + } + + int32_t value = lroundf(it.second.get() * 128 + offset); + controls.set(cid, utils::clamp(value, 0, 255)); } for (const auto &ctrl : controls) @@ -434,18 +445,21 @@ int VimcCameraData::init(MediaDevice *media) ControlInfoMap::Map ctrls; for (const auto &ctrl : controls) { - const ControlInfo &info = ctrl.second; const ControlId *id; + ControlInfo info; switch (ctrl.first->id()) { case V4L2_CID_BRIGHTNESS: id = &controls::Brightness; + info = ControlInfo{ { -1.0f }, { 1.0f }, { 0.0f } }; break; case V4L2_CID_CONTRAST: id = &controls::Contrast; + info = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } }; break; case V4L2_CID_SATURATION: id = &controls::Saturation; + info = ControlInfo{ { 0.0f }, { 2.0f }, { 1.0f } }; break; default: continue;