<div dir="ltr"><div dir="ltr">Hi Hans,<div><br></div><div>Our use case is a bit different. </div><div><br></div><div>We are working with UVC cameras that have built-in auto-focus and a </div><div>V4L2_CID_FOCUS_ABSOLUTE control.<br></div><div></div><div><br></div><div><div>We just need to lock the lens position once they are focused on the </div><div>correct point.<br></div><div><br></div><div>After the cameras are installed they should remain about the same </div><div>distance from the objects they are recording, with only occasional </div><div>refocusing needed. Sometimes, certain objects may appear closer </div><div>to the camera, but we do not want the focus to shift when that </div><div>happens, in order to avoid losing time.</div></div><div><br></div><div>Best regards,</div><div><br></div><div>Cláudio</div></div><div><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">Hans de Goede <<a href="mailto:hdegoede@redhat.com">hdegoede@redhat.com</a>> escreveu (sábado, 14/09/2024 à(s) 11:05):<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Hi Cláudio,<br>
<br>
On 9/13/24 7:22 PM, Cláudio Paulo wrote:<br>
> Added mapping of controls::LensPosition and controls::AfMode to<br>
> v4l2 controls V4L2_CID_FOCUS_ABSOLUTE and V4L2_CID_FOCUS_AUTO<br>
> respectively when the device supports them.<br>
> <br>
> If not supported, they are not registered.<br>
> <br>
> Signed-off-by: Cláudio Paulo <<a href="mailto:claudio.paulo@makewise.pt" target="_blank">claudio.paulo@makewise.pt</a>><br>
<br>
Thank you for your patch.<br>
<br>
Like last year I have a group of students from my local university<br>
working on libcamera for ~1 day/week during the first semester<br>
of the academic year.<br>
<br>
They have just started so they have not introduced themselves to<br>
the list yet ...<br>
<br>
The reason I mention this is that I have given them the assignment<br>
to add auto-focus support to the software ISP. One of their "could have"<br>
requirements is to integrate support for software auto-focus into<br>
the UVC driver, because some cameras like e.g. the Logitech quickcam<br>
9000 pro have a non fixed lens / a V4L2_CID_FOCUS_ABSOLUTE control,<br>
but these cameras do not support autofocus inside the camera instead<br>
relying on autofocus in the driver / OS.<br>
<br>
I know there are also UVC camera with builtin auto-focus so I guess<br>
you are mostly adding these mappings for those?<br>
<br>
I just want to make sure you are not also looking into software<br>
auto-focus support ?  (to avoid double work getting done)<br>
<br>
Regards,<br>
<br>
Hans<br>
<br>
<br>
<br>
<br>
<br>
<br>
> ---<br>
>  include/linux/v4l2-controls.h                |  4 +<br>
>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 88 +++++++++++++++++---<br>
>  2 files changed, 82 insertions(+), 10 deletions(-)<br>
> <br>
> diff --git a/include/linux/v4l2-controls.h b/include/linux/v4l2-controls.h<br>
> index 882a8180..1ac85507 100644<br>
> --- a/include/linux/v4l2-controls.h<br>
> +++ b/include/linux/v4l2-controls.h<br>
> @@ -994,6 +994,10 @@ enum  v4l2_exposure_auto_type {<br>
>  #define V4L2_CID_FOCUS_ABSOLUTE                      (V4L2_CID_CAMERA_CLASS_BASE+10)<br>
>  #define V4L2_CID_FOCUS_RELATIVE                      (V4L2_CID_CAMERA_CLASS_BASE+11)<br>
>  #define V4L2_CID_FOCUS_AUTO                  (V4L2_CID_CAMERA_CLASS_BASE+12)<br>
> +enum v4l2_focus_auto_type {<br>
> +     V4L2_FOCUS_MANUAL = 0,<br>
> +     V4L2_FOCUS_AUTO = 1<br>
> +};<br>
>  <br>
>  #define V4L2_CID_ZOOM_ABSOLUTE                       (V4L2_CID_CAMERA_CLASS_BASE+13)<br>
>  #define V4L2_CID_ZOOM_RELATIVE                       (V4L2_CID_CAMERA_CLASS_BASE+14)<br>
> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp<br>
> index 6b32fa18..fe3c71d0 100644<br>
> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp<br>
> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp<br>
> @@ -304,13 +304,14 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,<br>
>               cid = V4L2_CID_EXPOSURE_ABSOLUTE;<br>
>       else if (id == controls::AnalogueGain)<br>
>               cid = V4L2_CID_GAIN;<br>
> +     else if (id == controls::LensPosition && controls->idMap()->count(V4L2_CID_FOCUS_ABSOLUTE)) // Check if device supports this control<br>
> +             cid = V4L2_CID_FOCUS_ABSOLUTE;<br>
> +     else if (id == controls::AfMode && controls->idMap()->count(V4L2_CID_FOCUS_AUTO)) // Check if device supports this control<br>
> +             cid = V4L2_CID_FOCUS_AUTO;<br>
>       else<br>
>               return -EINVAL;<br>
>  <br>
>       const ControlInfo &v4l2Info = controls->infoMap()->at(cid);<br>
> -     int32_t min = v4l2Info.min().get<int32_t>();<br>
> -     int32_t def = v4l2Info.def().get<int32_t>();<br>
> -     int32_t max = v4l2Info.max().get<int32_t>();<br>
>  <br>
>       /*<br>
>        * See UVCCameraData::addControl() for explanations of the different<br>
> @@ -318,6 +319,10 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,<br>
>        */<br>
>       switch (cid) {<br>
>       case V4L2_CID_BRIGHTNESS: {<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +<br>
>               float scale = std::max(max - def, def - min);<br>
>               float fvalue = value.get<float>() * scale + def;<br>
>               controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));<br>
> @@ -325,6 +330,9 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,<br>
>       }<br>
>  <br>
>       case V4L2_CID_SATURATION: {<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
> +<br>
>               float scale = def - min;<br>
>               float fvalue = value.get<float>() * scale + min;<br>
>               controls->set(cid, static_cast<int32_t>(lroundf(fvalue)));<br>
> @@ -345,6 +353,10 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,<br>
>  <br>
>       case V4L2_CID_CONTRAST:<br>
>       case V4L2_CID_GAIN: {<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +<br>
>               float m = (4.0f - 1.0f) / (max - def);<br>
>               float p = 1.0f - m * def;<br>
>  <br>
> @@ -358,6 +370,22 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,<br>
>               break;<br>
>       }<br>
>  <br>
> +     case V4L2_CID_FOCUS_ABSOLUTE: {<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +<br>
> +             float focusedAt50Cm = 0.15f * (max - min);<br>
> +             float scale = focusedAt50Cm / 2.0f;<br>
> +<br>
> +             controls->set(cid, static_cast<int>(min + value.get<float>() * scale));<br>
> +             break;<br>
> +     }<br>
> +<br>
> +     case V4L2_CID_FOCUS_AUTO: {<br>
> +             controls->set(cid, static_cast<int>(value.get<int>() == 0 ? V4L2_FOCUS_MANUAL : V4L2_FOCUS_AUTO));<br>
> +             break;<br>
> +     }<br>
> +<br>
>       default: {<br>
>               int32_t ivalue = value.get<int32_t>();<br>
>               controls->set(cid, ivalue);<br>
> @@ -655,14 +683,17 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,<br>
>       case V4L2_CID_GAIN:<br>
>               id = &controls::AnalogueGain;<br>
>               break;<br>
> +     case V4L2_CID_FOCUS_ABSOLUTE:<br>
> +             id = &controls::LensPosition;<br>
> +             break;<br>
> +     case V4L2_CID_FOCUS_AUTO:<br>
> +             id = &controls::AfMode;<br>
> +             break;<br>
>       default:<br>
>               return;<br>
>       }<br>
>  <br>
>       /* Map the control info. */<br>
> -     int32_t min = v4l2Info.min().get<int32_t>();<br>
> -     int32_t max = v4l2Info.max().get<int32_t>();<br>
> -     int32_t def = v4l2Info.def().get<int32_t>();<br>
>  <br>
>       switch (cid) {<br>
>       case V4L2_CID_BRIGHTNESS: {<br>
> @@ -673,6 +704,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,<br>
>                * Accommodate this by restricting the range of the libcamera<br>
>                * control, but always within the maximum limits.<br>
>                */<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
>               float scale = std::max(max - def, def - min);<br>
>  <br>
>               info = ControlInfo{<br>
> @@ -683,36 +717,42 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,<br>
>               break;<br>
>       }<br>
>  <br>
> -     case V4L2_CID_SATURATION:<br>
> +     case V4L2_CID_SATURATION: {<br>
>               /*<br>
>                * The Saturation control is a float, with 0.0 mapped to the<br>
>                * minimum value (corresponding to a fully desaturated image)<br>
>                * and 1.0 mapped to the default value. Calculate the maximum<br>
>                * value accordingly.<br>
>                */<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
>               info = ControlInfo{<br>
>                       { 0.0f },<br>
>                       { static_cast<float>(max - min) / (def - min) },<br>
>                       { 1.0f }<br>
>               };<br>
>               break;<br>
> -<br>
> +     }<br>
>       case V4L2_CID_EXPOSURE_AUTO:<br>
>               info = ControlInfo{ false, true, true };<br>
>               break;<br>
>  <br>
> -     case V4L2_CID_EXPOSURE_ABSOLUTE:<br>
> +     case V4L2_CID_EXPOSURE_ABSOLUTE: {<br>
>               /*<br>
>                * ExposureTime is in units of 1 µs, and UVC expects<br>
>                * V4L2_CID_EXPOSURE_ABSOLUTE in units of 100 µs.<br>
>                */<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
>               info = ControlInfo{<br>
>                       { min * 100 },<br>
>                       { max * 100 },<br>
>                       { def * 100 }<br>
>               };<br>
>               break;<br>
> -<br>
> +     }<br>
>       case V4L2_CID_CONTRAST:<br>
>       case V4L2_CID_GAIN: {<br>
>               /*<br>
> @@ -723,6 +763,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,<br>
>                * maximum values are respectively no lower than 0.5 and no<br>
>                * higher than 4.0.<br>
>                */<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
>               float m = (4.0f - 1.0f) / (max - def);<br>
>               float p = 1.0f - m * def;<br>
>  <br>
> @@ -738,6 +781,31 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,<br>
>               };<br>
>               break;<br>
>       }<br>
> +     case V4L2_CID_FOCUS_ABSOLUTE: {<br>
> +             int32_t min = v4l2Info.min().get<int32_t>();<br>
> +             int32_t max = v4l2Info.max().get<int32_t>();<br>
> +             int32_t def = v4l2Info.def().get<int32_t>();<br>
> +<br>
> +             float focusedAt50Cm = 0.15f * (max - min);<br>
> +             float scale = 2.0f / focusedAt50Cm;<br>
> +<br>
> +             info = ControlInfo{<br>
> +                     { 0.0f },<br>
> +                     { scale * (max - min) },<br>
> +                     { scale * (def - min) }<br>
> +             };<br>
> +             break;<br>
> +     }<br>
> +     case V4L2_CID_FOCUS_AUTO: {<br>
> +             bool def = v4l2Info.def().get<bool>();<br>
> +<br>
> +             info = ControlInfo{<br>
> +                     { static_cast<int>(V4L2_FOCUS_MANUAL) },<br>
> +                     { static_cast<int>(V4L2_FOCUS_AUTO) },<br>
> +                     { static_cast<int>(def ? V4L2_FOCUS_AUTO : V4L2_FOCUS_MANUAL) },<br>
> +             };<br>
> +             break;<br>
> +     }<br>
>  <br>
>       default:<br>
>               info = v4l2Info;<br>
<br>
</blockquote></div></div>