[libcamera-devel,v2] py: Add the SensorConfiguration class
diff mbox series

Message ID 20231011140238.39058-1-david.plowman@raspberrypi.com
State Accepted
Commit d05009d34c67dfb11e890eaee2858a7e99ed06e4
Headers show
Series
  • [libcamera-devel,v2] py: Add the SensorConfiguration class
Related show

Commit Message

David Plowman Oct. 11, 2023, 2:02 p.m. UTC
We provide access to the various fields of the new SensorConfiguration
class. The class also needs a constructor so that Python applications
can make one and put it into the CameraConfiguration.

Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
---
 src/py/libcamera/py_main.cpp | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

Comments

Kieran Bingham Oct. 12, 2023, 8:29 a.m. UTC | #1
Hi David,

Quoting David Plowman via libcamera-devel (2023-10-11 15:02:38)
> We provide access to the various fields of the new SensorConfiguration
> class. The class also needs a constructor so that Python applications
> can make one and put it into the CameraConfiguration.
> 
> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> ---
>  src/py/libcamera/py_main.cpp | 36 ++++++++++++++++++++++++++++++++++++
>  1 file changed, 36 insertions(+)
> 
> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
> index 01fb15a9..d2ce3722 100644
> --- a/src/py/libcamera/py_main.cpp
> +++ b/src/py/libcamera/py_main.cpp
> @@ -112,6 +112,7 @@ PYBIND11_MODULE(_libcamera, m)
>  
>         auto pyCameraManager = py::class_<PyCameraManager, std::shared_ptr<PyCameraManager>>(m, "CameraManager");
>         auto pyCamera = py::class_<Camera, PyCameraSmartPtr<Camera>>(m, "Camera");
> +       auto pySensorConfiguration = py::class_<SensorConfiguration>(m, "SensorConfiguration");
>         auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
>         auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
>         auto pyStreamConfiguration = py::class_<StreamConfiguration>(m, "StreamConfiguration");
> @@ -281,6 +282,40 @@ PYBIND11_MODULE(_libcamera, m)
>                         return ret;
>                 });
>  
> +       pySensorConfiguration
> +               .def(py::init<>())
> +               .def_readwrite("bit_depth", &SensorConfiguration::bitDepth)
> +               .def_readwrite("analog_crop", &SensorConfiguration::analogCrop)
> +               .def_property(
> +                       "binning",
> +                       [](SensorConfiguration &self) {
> +                               return py::make_tuple(self.binning.binX, self.binning.binY);
> +                       },
> +                       [](SensorConfiguration &self, py::object value) {
> +                               auto vec = value.cast<std::vector<unsigned int>>();
> +                               if (vec.size() != 2)
> +                                       throw std::runtime_error("binning requires iterable of 2 values");
> +                               self.binning.binX = vec[0];
> +                               self.binning.binY = vec[1];

This seems reasonable to me. Partially filling in either of binning or
skipping would be invalid.

Thanks for filling in the rest of the logic:

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>

Tomi - Are you happy with this?



> +                       })
> +               .def_property(
> +                       "skipping",
> +                       [](SensorConfiguration &self) {
> +                               return py::make_tuple(self.skipping.xOddInc, self.skipping.xEvenInc,
> +                                                     self.skipping.yOddInc, self.skipping.yEvenInc);
> +                       },
> +                       [](SensorConfiguration &self, py::object value) {
> +                               auto vec = value.cast<std::vector<unsigned int>>();
> +                               if (vec.size() != 4)
> +                                       throw std::runtime_error("skipping requires iterable of 4 values");
> +                               self.skipping.xOddInc = vec[0];
> +                               self.skipping.xEvenInc = vec[1];
> +                               self.skipping.yOddInc = vec[2];
> +                               self.skipping.yEvenInc = vec[3];
> +                       })
> +               .def_readwrite("output_size", &SensorConfiguration::outputSize)
> +               .def("is_valid", &SensorConfiguration::isValid);
> +
>         pyCameraConfiguration
>                 .def("__iter__", [](CameraConfiguration &self) {
>                         return py::make_iterator<py::return_value_policy::reference_internal>(self);
> @@ -293,6 +328,7 @@ PYBIND11_MODULE(_libcamera, m)
>                      py::return_value_policy::reference_internal)
>                 .def_property_readonly("size", &CameraConfiguration::size)
>                 .def_property_readonly("empty", &CameraConfiguration::empty)
> +               .def_readwrite("sensor_config", &CameraConfiguration::sensorConfig)
>                 .def_readwrite("transform", &CameraConfiguration::transform);
>  
>         pyCameraConfigurationStatus
> -- 
> 2.39.2
>
Tomi Valkeinen Oct. 20, 2023, 7:51 a.m. UTC | #2
On 12/10/2023 11:29, Kieran Bingham via libcamera-devel wrote:
> Hi David,
> 
> Quoting David Plowman via libcamera-devel (2023-10-11 15:02:38)
>> We provide access to the various fields of the new SensorConfiguration
>> class. The class also needs a constructor so that Python applications
>> can make one and put it into the CameraConfiguration.
>>
>> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
>> ---
>>   src/py/libcamera/py_main.cpp | 36 ++++++++++++++++++++++++++++++++++++
>>   1 file changed, 36 insertions(+)
>>
>> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
>> index 01fb15a9..d2ce3722 100644
>> --- a/src/py/libcamera/py_main.cpp
>> +++ b/src/py/libcamera/py_main.cpp
>> @@ -112,6 +112,7 @@ PYBIND11_MODULE(_libcamera, m)
>>   
>>          auto pyCameraManager = py::class_<PyCameraManager, std::shared_ptr<PyCameraManager>>(m, "CameraManager");
>>          auto pyCamera = py::class_<Camera, PyCameraSmartPtr<Camera>>(m, "Camera");
>> +       auto pySensorConfiguration = py::class_<SensorConfiguration>(m, "SensorConfiguration");
>>          auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
>>          auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
>>          auto pyStreamConfiguration = py::class_<StreamConfiguration>(m, "StreamConfiguration");
>> @@ -281,6 +282,40 @@ PYBIND11_MODULE(_libcamera, m)
>>                          return ret;
>>                  });
>>   
>> +       pySensorConfiguration
>> +               .def(py::init<>())
>> +               .def_readwrite("bit_depth", &SensorConfiguration::bitDepth)
>> +               .def_readwrite("analog_crop", &SensorConfiguration::analogCrop)
>> +               .def_property(
>> +                       "binning",
>> +                       [](SensorConfiguration &self) {
>> +                               return py::make_tuple(self.binning.binX, self.binning.binY);
>> +                       },
>> +                       [](SensorConfiguration &self, py::object value) {
>> +                               auto vec = value.cast<std::vector<unsigned int>>();
>> +                               if (vec.size() != 2)
>> +                                       throw std::runtime_error("binning requires iterable of 2 values");
>> +                               self.binning.binX = vec[0];
>> +                               self.binning.binY = vec[1];
> 
> This seems reasonable to me. Partially filling in either of binning or
> skipping would be invalid.
> 
> Thanks for filling in the rest of the logic:
> 
> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> 
> Tomi - Are you happy with this?

I think it's simpler and better to do:

.def_property(
	"binning",
	[](SensorConfiguration &self) {
		return std::make_tuple(self.binning.binX, self.binning.binY);
	},
	[](SensorConfiguration &self, std::tuple<uint32_t, uint32_t> value) {
		self.binning.binX = std::get<0>(value);
		self.binning.binY = std::get<1>(value);
	})

  Tomi


> 
> 
> 
>> +                       })
>> +               .def_property(
>> +                       "skipping",
>> +                       [](SensorConfiguration &self) {
>> +                               return py::make_tuple(self.skipping.xOddInc, self.skipping.xEvenInc,
>> +                                                     self.skipping.yOddInc, self.skipping.yEvenInc);
>> +                       },
>> +                       [](SensorConfiguration &self, py::object value) {
>> +                               auto vec = value.cast<std::vector<unsigned int>>();
>> +                               if (vec.size() != 4)
>> +                                       throw std::runtime_error("skipping requires iterable of 4 values");
>> +                               self.skipping.xOddInc = vec[0];
>> +                               self.skipping.xEvenInc = vec[1];
>> +                               self.skipping.yOddInc = vec[2];
>> +                               self.skipping.yEvenInc = vec[3];
>> +                       })
>> +               .def_readwrite("output_size", &SensorConfiguration::outputSize)
>> +               .def("is_valid", &SensorConfiguration::isValid);
>> +
>>          pyCameraConfiguration
>>                  .def("__iter__", [](CameraConfiguration &self) {
>>                          return py::make_iterator<py::return_value_policy::reference_internal>(self);
>> @@ -293,6 +328,7 @@ PYBIND11_MODULE(_libcamera, m)
>>                       py::return_value_policy::reference_internal)
>>                  .def_property_readonly("size", &CameraConfiguration::size)
>>                  .def_property_readonly("empty", &CameraConfiguration::empty)
>> +               .def_readwrite("sensor_config", &CameraConfiguration::sensorConfig)
>>                  .def_readwrite("transform", &CameraConfiguration::transform);
>>   
>>          pyCameraConfigurationStatus
>> -- 
>> 2.39.2
>>
David Plowman Oct. 20, 2023, 8:17 a.m. UTC | #3
Hi Tomi

Thanks for that. I had no idea you could write that... the joys of C++!

Would folks like me to send another update?

David

On Fri, 20 Oct 2023 at 08:51, Tomi Valkeinen
<tomi.valkeinen@ideasonboard.com> wrote:
>
> On 12/10/2023 11:29, Kieran Bingham via libcamera-devel wrote:
> > Hi David,
> >
> > Quoting David Plowman via libcamera-devel (2023-10-11 15:02:38)
> >> We provide access to the various fields of the new SensorConfiguration
> >> class. The class also needs a constructor so that Python applications
> >> can make one and put it into the CameraConfiguration.
> >>
> >> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> >> ---
> >>   src/py/libcamera/py_main.cpp | 36 ++++++++++++++++++++++++++++++++++++
> >>   1 file changed, 36 insertions(+)
> >>
> >> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
> >> index 01fb15a9..d2ce3722 100644
> >> --- a/src/py/libcamera/py_main.cpp
> >> +++ b/src/py/libcamera/py_main.cpp
> >> @@ -112,6 +112,7 @@ PYBIND11_MODULE(_libcamera, m)
> >>
> >>          auto pyCameraManager = py::class_<PyCameraManager, std::shared_ptr<PyCameraManager>>(m, "CameraManager");
> >>          auto pyCamera = py::class_<Camera, PyCameraSmartPtr<Camera>>(m, "Camera");
> >> +       auto pySensorConfiguration = py::class_<SensorConfiguration>(m, "SensorConfiguration");
> >>          auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
> >>          auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
> >>          auto pyStreamConfiguration = py::class_<StreamConfiguration>(m, "StreamConfiguration");
> >> @@ -281,6 +282,40 @@ PYBIND11_MODULE(_libcamera, m)
> >>                          return ret;
> >>                  });
> >>
> >> +       pySensorConfiguration
> >> +               .def(py::init<>())
> >> +               .def_readwrite("bit_depth", &SensorConfiguration::bitDepth)
> >> +               .def_readwrite("analog_crop", &SensorConfiguration::analogCrop)
> >> +               .def_property(
> >> +                       "binning",
> >> +                       [](SensorConfiguration &self) {
> >> +                               return py::make_tuple(self.binning.binX, self.binning.binY);
> >> +                       },
> >> +                       [](SensorConfiguration &self, py::object value) {
> >> +                               auto vec = value.cast<std::vector<unsigned int>>();
> >> +                               if (vec.size() != 2)
> >> +                                       throw std::runtime_error("binning requires iterable of 2 values");
> >> +                               self.binning.binX = vec[0];
> >> +                               self.binning.binY = vec[1];
> >
> > This seems reasonable to me. Partially filling in either of binning or
> > skipping would be invalid.
> >
> > Thanks for filling in the rest of the logic:
> >
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> >
> > Tomi - Are you happy with this?
>
> I think it's simpler and better to do:
>
> .def_property(
>         "binning",
>         [](SensorConfiguration &self) {
>                 return std::make_tuple(self.binning.binX, self.binning.binY);
>         },
>         [](SensorConfiguration &self, std::tuple<uint32_t, uint32_t> value) {
>                 self.binning.binX = std::get<0>(value);
>                 self.binning.binY = std::get<1>(value);
>         })
>
>   Tomi
>
>
> >
> >
> >
> >> +                       })
> >> +               .def_property(
> >> +                       "skipping",
> >> +                       [](SensorConfiguration &self) {
> >> +                               return py::make_tuple(self.skipping.xOddInc, self.skipping.xEvenInc,
> >> +                                                     self.skipping.yOddInc, self.skipping.yEvenInc);
> >> +                       },
> >> +                       [](SensorConfiguration &self, py::object value) {
> >> +                               auto vec = value.cast<std::vector<unsigned int>>();
> >> +                               if (vec.size() != 4)
> >> +                                       throw std::runtime_error("skipping requires iterable of 4 values");
> >> +                               self.skipping.xOddInc = vec[0];
> >> +                               self.skipping.xEvenInc = vec[1];
> >> +                               self.skipping.yOddInc = vec[2];
> >> +                               self.skipping.yEvenInc = vec[3];
> >> +                       })
> >> +               .def_readwrite("output_size", &SensorConfiguration::outputSize)
> >> +               .def("is_valid", &SensorConfiguration::isValid);
> >> +
> >>          pyCameraConfiguration
> >>                  .def("__iter__", [](CameraConfiguration &self) {
> >>                          return py::make_iterator<py::return_value_policy::reference_internal>(self);
> >> @@ -293,6 +328,7 @@ PYBIND11_MODULE(_libcamera, m)
> >>                       py::return_value_policy::reference_internal)
> >>                  .def_property_readonly("size", &CameraConfiguration::size)
> >>                  .def_property_readonly("empty", &CameraConfiguration::empty)
> >> +               .def_readwrite("sensor_config", &CameraConfiguration::sensorConfig)
> >>                  .def_readwrite("transform", &CameraConfiguration::transform);
> >>
> >>          pyCameraConfigurationStatus
> >> --
> >> 2.39.2
> >>
>
Tomi Valkeinen Oct. 20, 2023, 8:19 a.m. UTC | #4
On 20/10/2023 11:17, David Plowman wrote:
> Hi Tomi
> 
> Thanks for that. I had no idea you could write that... the joys of C++!

I think usually with pybind11 you should think in terms of C++. Use the 
C++ types whenever possible, as they're more performant and specific 
than the Python types, and let pybind11 do the conversions between the 
Python and C++ types.

  Tomi

> 
> Would folks like me to send another update?
> 
> David
> 
> On Fri, 20 Oct 2023 at 08:51, Tomi Valkeinen
> <tomi.valkeinen@ideasonboard.com> wrote:
>>
>> On 12/10/2023 11:29, Kieran Bingham via libcamera-devel wrote:
>>> Hi David,
>>>
>>> Quoting David Plowman via libcamera-devel (2023-10-11 15:02:38)
>>>> We provide access to the various fields of the new SensorConfiguration
>>>> class. The class also needs a constructor so that Python applications
>>>> can make one and put it into the CameraConfiguration.
>>>>
>>>> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
>>>> ---
>>>>    src/py/libcamera/py_main.cpp | 36 ++++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 36 insertions(+)
>>>>
>>>> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
>>>> index 01fb15a9..d2ce3722 100644
>>>> --- a/src/py/libcamera/py_main.cpp
>>>> +++ b/src/py/libcamera/py_main.cpp
>>>> @@ -112,6 +112,7 @@ PYBIND11_MODULE(_libcamera, m)
>>>>
>>>>           auto pyCameraManager = py::class_<PyCameraManager, std::shared_ptr<PyCameraManager>>(m, "CameraManager");
>>>>           auto pyCamera = py::class_<Camera, PyCameraSmartPtr<Camera>>(m, "Camera");
>>>> +       auto pySensorConfiguration = py::class_<SensorConfiguration>(m, "SensorConfiguration");
>>>>           auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
>>>>           auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
>>>>           auto pyStreamConfiguration = py::class_<StreamConfiguration>(m, "StreamConfiguration");
>>>> @@ -281,6 +282,40 @@ PYBIND11_MODULE(_libcamera, m)
>>>>                           return ret;
>>>>                   });
>>>>
>>>> +       pySensorConfiguration
>>>> +               .def(py::init<>())
>>>> +               .def_readwrite("bit_depth", &SensorConfiguration::bitDepth)
>>>> +               .def_readwrite("analog_crop", &SensorConfiguration::analogCrop)
>>>> +               .def_property(
>>>> +                       "binning",
>>>> +                       [](SensorConfiguration &self) {
>>>> +                               return py::make_tuple(self.binning.binX, self.binning.binY);
>>>> +                       },
>>>> +                       [](SensorConfiguration &self, py::object value) {
>>>> +                               auto vec = value.cast<std::vector<unsigned int>>();
>>>> +                               if (vec.size() != 2)
>>>> +                                       throw std::runtime_error("binning requires iterable of 2 values");
>>>> +                               self.binning.binX = vec[0];
>>>> +                               self.binning.binY = vec[1];
>>>
>>> This seems reasonable to me. Partially filling in either of binning or
>>> skipping would be invalid.
>>>
>>> Thanks for filling in the rest of the logic:
>>>
>>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>>
>>> Tomi - Are you happy with this?
>>
>> I think it's simpler and better to do:
>>
>> .def_property(
>>          "binning",
>>          [](SensorConfiguration &self) {
>>                  return std::make_tuple(self.binning.binX, self.binning.binY);
>>          },
>>          [](SensorConfiguration &self, std::tuple<uint32_t, uint32_t> value) {
>>                  self.binning.binX = std::get<0>(value);
>>                  self.binning.binY = std::get<1>(value);
>>          })
>>
>>    Tomi
>>
>>
>>>
>>>
>>>
>>>> +                       })
>>>> +               .def_property(
>>>> +                       "skipping",
>>>> +                       [](SensorConfiguration &self) {
>>>> +                               return py::make_tuple(self.skipping.xOddInc, self.skipping.xEvenInc,
>>>> +                                                     self.skipping.yOddInc, self.skipping.yEvenInc);
>>>> +                       },
>>>> +                       [](SensorConfiguration &self, py::object value) {
>>>> +                               auto vec = value.cast<std::vector<unsigned int>>();
>>>> +                               if (vec.size() != 4)
>>>> +                                       throw std::runtime_error("skipping requires iterable of 4 values");
>>>> +                               self.skipping.xOddInc = vec[0];
>>>> +                               self.skipping.xEvenInc = vec[1];
>>>> +                               self.skipping.yOddInc = vec[2];
>>>> +                               self.skipping.yEvenInc = vec[3];
>>>> +                       })
>>>> +               .def_readwrite("output_size", &SensorConfiguration::outputSize)
>>>> +               .def("is_valid", &SensorConfiguration::isValid);
>>>> +
>>>>           pyCameraConfiguration
>>>>                   .def("__iter__", [](CameraConfiguration &self) {
>>>>                           return py::make_iterator<py::return_value_policy::reference_internal>(self);
>>>> @@ -293,6 +328,7 @@ PYBIND11_MODULE(_libcamera, m)
>>>>                        py::return_value_policy::reference_internal)
>>>>                   .def_property_readonly("size", &CameraConfiguration::size)
>>>>                   .def_property_readonly("empty", &CameraConfiguration::empty)
>>>> +               .def_readwrite("sensor_config", &CameraConfiguration::sensorConfig)
>>>>                   .def_readwrite("transform", &CameraConfiguration::transform);
>>>>
>>>>           pyCameraConfigurationStatus
>>>> --
>>>> 2.39.2
>>>>
>>
Kieran Bingham Oct. 20, 2023, 8:20 a.m. UTC | #5
Quoting Tomi Valkeinen (2023-10-20 08:51:08)
> On 12/10/2023 11:29, Kieran Bingham via libcamera-devel wrote:
> > Hi David,
> > 
> > Quoting David Plowman via libcamera-devel (2023-10-11 15:02:38)
> >> We provide access to the various fields of the new SensorConfiguration
> >> class. The class also needs a constructor so that Python applications
> >> can make one and put it into the CameraConfiguration.
> >>
> >> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
> >> ---
> >>   src/py/libcamera/py_main.cpp | 36 ++++++++++++++++++++++++++++++++++++
> >>   1 file changed, 36 insertions(+)
> >>
> >> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
> >> index 01fb15a9..d2ce3722 100644
> >> --- a/src/py/libcamera/py_main.cpp
> >> +++ b/src/py/libcamera/py_main.cpp
> >> @@ -112,6 +112,7 @@ PYBIND11_MODULE(_libcamera, m)
> >>   
> >>          auto pyCameraManager = py::class_<PyCameraManager, std::shared_ptr<PyCameraManager>>(m, "CameraManager");
> >>          auto pyCamera = py::class_<Camera, PyCameraSmartPtr<Camera>>(m, "Camera");
> >> +       auto pySensorConfiguration = py::class_<SensorConfiguration>(m, "SensorConfiguration");
> >>          auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
> >>          auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
> >>          auto pyStreamConfiguration = py::class_<StreamConfiguration>(m, "StreamConfiguration");
> >> @@ -281,6 +282,40 @@ PYBIND11_MODULE(_libcamera, m)
> >>                          return ret;
> >>                  });
> >>   
> >> +       pySensorConfiguration
> >> +               .def(py::init<>())
> >> +               .def_readwrite("bit_depth", &SensorConfiguration::bitDepth)
> >> +               .def_readwrite("analog_crop", &SensorConfiguration::analogCrop)
> >> +               .def_property(
> >> +                       "binning",
> >> +                       [](SensorConfiguration &self) {
> >> +                               return py::make_tuple(self.binning.binX, self.binning.binY);
> >> +                       },
> >> +                       [](SensorConfiguration &self, py::object value) {
> >> +                               auto vec = value.cast<std::vector<unsigned int>>();
> >> +                               if (vec.size() != 2)
> >> +                                       throw std::runtime_error("binning requires iterable of 2 values");
> >> +                               self.binning.binX = vec[0];
> >> +                               self.binning.binY = vec[1];
> > 
> > This seems reasonable to me. Partially filling in either of binning or
> > skipping would be invalid.
> > 
> > Thanks for filling in the rest of the logic:
> > 
> > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > 
> > Tomi - Are you happy with this?
> 
> I think it's simpler and better to do:
> 
> .def_property(
>         "binning",
>         [](SensorConfiguration &self) {
>                 return std::make_tuple(self.binning.binX, self.binning.binY);
>         },
>         [](SensorConfiguration &self, std::tuple<uint32_t, uint32_t> value) {
>                 self.binning.binX = std::get<0>(value);
>                 self.binning.binY = std::get<1>(value);

Does the std::get<> add the bounds checking that allows the vec.size()
check to be removed?

>         })
> 

I'm afraid I already merged this I think.

If it's a cleanup/fixup - can  you submit a patch please?

--
Kieran


>   Tomi
> 
> 
> > 
> > 
> > 
> >> +                       })
> >> +               .def_property(
> >> +                       "skipping",
> >> +                       [](SensorConfiguration &self) {
> >> +                               return py::make_tuple(self.skipping.xOddInc, self.skipping.xEvenInc,
> >> +                                                     self.skipping.yOddInc, self.skipping.yEvenInc);
> >> +                       },
> >> +                       [](SensorConfiguration &self, py::object value) {
> >> +                               auto vec = value.cast<std::vector<unsigned int>>();
> >> +                               if (vec.size() != 4)
> >> +                                       throw std::runtime_error("skipping requires iterable of 4 values");
> >> +                               self.skipping.xOddInc = vec[0];
> >> +                               self.skipping.xEvenInc = vec[1];
> >> +                               self.skipping.yOddInc = vec[2];
> >> +                               self.skipping.yEvenInc = vec[3];
> >> +                       })
> >> +               .def_readwrite("output_size", &SensorConfiguration::outputSize)
> >> +               .def("is_valid", &SensorConfiguration::isValid);
> >> +
> >>          pyCameraConfiguration
> >>                  .def("__iter__", [](CameraConfiguration &self) {
> >>                          return py::make_iterator<py::return_value_policy::reference_internal>(self);
> >> @@ -293,6 +328,7 @@ PYBIND11_MODULE(_libcamera, m)
> >>                       py::return_value_policy::reference_internal)
> >>                  .def_property_readonly("size", &CameraConfiguration::size)
> >>                  .def_property_readonly("empty", &CameraConfiguration::empty)
> >> +               .def_readwrite("sensor_config", &CameraConfiguration::sensorConfig)
> >>                  .def_readwrite("transform", &CameraConfiguration::transform);
> >>   
> >>          pyCameraConfigurationStatus
> >> -- 
> >> 2.39.2
> >>
>
Tomi Valkeinen Oct. 20, 2023, 8:21 a.m. UTC | #6
On 20/10/2023 11:20, Kieran Bingham wrote:
> Quoting Tomi Valkeinen (2023-10-20 08:51:08)
>> On 12/10/2023 11:29, Kieran Bingham via libcamera-devel wrote:
>>> Hi David,
>>>
>>> Quoting David Plowman via libcamera-devel (2023-10-11 15:02:38)
>>>> We provide access to the various fields of the new SensorConfiguration
>>>> class. The class also needs a constructor so that Python applications
>>>> can make one and put it into the CameraConfiguration.
>>>>
>>>> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>
>>>> ---
>>>>    src/py/libcamera/py_main.cpp | 36 ++++++++++++++++++++++++++++++++++++
>>>>    1 file changed, 36 insertions(+)
>>>>
>>>> diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
>>>> index 01fb15a9..d2ce3722 100644
>>>> --- a/src/py/libcamera/py_main.cpp
>>>> +++ b/src/py/libcamera/py_main.cpp
>>>> @@ -112,6 +112,7 @@ PYBIND11_MODULE(_libcamera, m)
>>>>    
>>>>           auto pyCameraManager = py::class_<PyCameraManager, std::shared_ptr<PyCameraManager>>(m, "CameraManager");
>>>>           auto pyCamera = py::class_<Camera, PyCameraSmartPtr<Camera>>(m, "Camera");
>>>> +       auto pySensorConfiguration = py::class_<SensorConfiguration>(m, "SensorConfiguration");
>>>>           auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
>>>>           auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
>>>>           auto pyStreamConfiguration = py::class_<StreamConfiguration>(m, "StreamConfiguration");
>>>> @@ -281,6 +282,40 @@ PYBIND11_MODULE(_libcamera, m)
>>>>                           return ret;
>>>>                   });
>>>>    
>>>> +       pySensorConfiguration
>>>> +               .def(py::init<>())
>>>> +               .def_readwrite("bit_depth", &SensorConfiguration::bitDepth)
>>>> +               .def_readwrite("analog_crop", &SensorConfiguration::analogCrop)
>>>> +               .def_property(
>>>> +                       "binning",
>>>> +                       [](SensorConfiguration &self) {
>>>> +                               return py::make_tuple(self.binning.binX, self.binning.binY);
>>>> +                       },
>>>> +                       [](SensorConfiguration &self, py::object value) {
>>>> +                               auto vec = value.cast<std::vector<unsigned int>>();
>>>> +                               if (vec.size() != 2)
>>>> +                                       throw std::runtime_error("binning requires iterable of 2 values");
>>>> +                               self.binning.binX = vec[0];
>>>> +                               self.binning.binY = vec[1];
>>>
>>> This seems reasonable to me. Partially filling in either of binning or
>>> skipping would be invalid.
>>>
>>> Thanks for filling in the rest of the logic:
>>>
>>> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
>>>
>>> Tomi - Are you happy with this?
>>
>> I think it's simpler and better to do:
>>
>> .def_property(
>>          "binning",
>>          [](SensorConfiguration &self) {
>>                  return std::make_tuple(self.binning.binX, self.binning.binY);
>>          },
>>          [](SensorConfiguration &self, std::tuple<uint32_t, uint32_t> value) {
>>                  self.binning.binX = std::get<0>(value);
>>                  self.binning.binY = std::get<1>(value);
> 
> Does the std::get<> add the bounds checking that allows the vec.size()
> check to be removed?

It's the "std::tuple<uint32_t, uint32_t> value" that forces the types 
and the size.

  Tomi

> 
>>          })
>>
> 
> I'm afraid I already merged this I think.
> 
> If it's a cleanup/fixup - can  you submit a patch please?
> 
> --
> Kieran
> 
> 
>>    Tomi
>>
>>
>>>
>>>
>>>
>>>> +                       })
>>>> +               .def_property(
>>>> +                       "skipping",
>>>> +                       [](SensorConfiguration &self) {
>>>> +                               return py::make_tuple(self.skipping.xOddInc, self.skipping.xEvenInc,
>>>> +                                                     self.skipping.yOddInc, self.skipping.yEvenInc);
>>>> +                       },
>>>> +                       [](SensorConfiguration &self, py::object value) {
>>>> +                               auto vec = value.cast<std::vector<unsigned int>>();
>>>> +                               if (vec.size() != 4)
>>>> +                                       throw std::runtime_error("skipping requires iterable of 4 values");
>>>> +                               self.skipping.xOddInc = vec[0];
>>>> +                               self.skipping.xEvenInc = vec[1];
>>>> +                               self.skipping.yOddInc = vec[2];
>>>> +                               self.skipping.yEvenInc = vec[3];
>>>> +                       })
>>>> +               .def_readwrite("output_size", &SensorConfiguration::outputSize)
>>>> +               .def("is_valid", &SensorConfiguration::isValid);
>>>> +
>>>>           pyCameraConfiguration
>>>>                   .def("__iter__", [](CameraConfiguration &self) {
>>>>                           return py::make_iterator<py::return_value_policy::reference_internal>(self);
>>>> @@ -293,6 +328,7 @@ PYBIND11_MODULE(_libcamera, m)
>>>>                        py::return_value_policy::reference_internal)
>>>>                   .def_property_readonly("size", &CameraConfiguration::size)
>>>>                   .def_property_readonly("empty", &CameraConfiguration::empty)
>>>> +               .def_readwrite("sensor_config", &CameraConfiguration::sensorConfig)
>>>>                   .def_readwrite("transform", &CameraConfiguration::transform);
>>>>    
>>>>           pyCameraConfigurationStatus
>>>> -- 
>>>> 2.39.2
>>>>
>>

Patch
diff mbox series

diff --git a/src/py/libcamera/py_main.cpp b/src/py/libcamera/py_main.cpp
index 01fb15a9..d2ce3722 100644
--- a/src/py/libcamera/py_main.cpp
+++ b/src/py/libcamera/py_main.cpp
@@ -112,6 +112,7 @@  PYBIND11_MODULE(_libcamera, m)
 
 	auto pyCameraManager = py::class_<PyCameraManager, std::shared_ptr<PyCameraManager>>(m, "CameraManager");
 	auto pyCamera = py::class_<Camera, PyCameraSmartPtr<Camera>>(m, "Camera");
+	auto pySensorConfiguration = py::class_<SensorConfiguration>(m, "SensorConfiguration");
 	auto pyCameraConfiguration = py::class_<CameraConfiguration>(m, "CameraConfiguration");
 	auto pyCameraConfigurationStatus = py::enum_<CameraConfiguration::Status>(pyCameraConfiguration, "Status");
 	auto pyStreamConfiguration = py::class_<StreamConfiguration>(m, "StreamConfiguration");
@@ -281,6 +282,40 @@  PYBIND11_MODULE(_libcamera, m)
 			return ret;
 		});
 
+	pySensorConfiguration
+		.def(py::init<>())
+		.def_readwrite("bit_depth", &SensorConfiguration::bitDepth)
+		.def_readwrite("analog_crop", &SensorConfiguration::analogCrop)
+		.def_property(
+			"binning",
+			[](SensorConfiguration &self) {
+				return py::make_tuple(self.binning.binX, self.binning.binY);
+			},
+			[](SensorConfiguration &self, py::object value) {
+				auto vec = value.cast<std::vector<unsigned int>>();
+				if (vec.size() != 2)
+					throw std::runtime_error("binning requires iterable of 2 values");
+				self.binning.binX = vec[0];
+				self.binning.binY = vec[1];
+			})
+		.def_property(
+			"skipping",
+			[](SensorConfiguration &self) {
+				return py::make_tuple(self.skipping.xOddInc, self.skipping.xEvenInc,
+						      self.skipping.yOddInc, self.skipping.yEvenInc);
+			},
+			[](SensorConfiguration &self, py::object value) {
+				auto vec = value.cast<std::vector<unsigned int>>();
+				if (vec.size() != 4)
+					throw std::runtime_error("skipping requires iterable of 4 values");
+				self.skipping.xOddInc = vec[0];
+				self.skipping.xEvenInc = vec[1];
+				self.skipping.yOddInc = vec[2];
+				self.skipping.yEvenInc = vec[3];
+			})
+		.def_readwrite("output_size", &SensorConfiguration::outputSize)
+		.def("is_valid", &SensorConfiguration::isValid);
+
 	pyCameraConfiguration
 		.def("__iter__", [](CameraConfiguration &self) {
 			return py::make_iterator<py::return_value_policy::reference_internal>(self);
@@ -293,6 +328,7 @@  PYBIND11_MODULE(_libcamera, m)
 		     py::return_value_policy::reference_internal)
 		.def_property_readonly("size", &CameraConfiguration::size)
 		.def_property_readonly("empty", &CameraConfiguration::empty)
+		.def_readwrite("sensor_config", &CameraConfiguration::sensorConfig)
 		.def_readwrite("transform", &CameraConfiguration::transform);
 
 	pyCameraConfigurationStatus