[libcamera-devel] [PATCH v3 1/2] ipa: Create a camera sensor helper class

Jean-Michel Hautbois jeanmichel.hautbois at ideasonboard.com
Mon Jun 14 09:59:25 CEST 2021



On 14/06/2021 09:51, Jean-Michel Hautbois wrote:
> Hi Kieran,
> 
> On 11/06/2021 11:53, Kieran Bingham wrote:
>> Hi JM
>>
>> On 11/06/2021 08:03, Jean-Michel Hautbois wrote:
>>> Setting analogue gain for a specific sensor is not a straghtforward
>>
>> s/straghtforward/straightforward/
>>
>>> operation, as one needs to know how the gain is calculated for it.
>>>
>>> This commit introduces a new camera sensor helper in libipa which aims
>>> to solve this specific issue.
>>> It is based on the MIPI alliance Specification for Camera Command Set
>>> and implements, for now, only the analogue "Global gain" mode.
>>>
>>> Is makes it possible to only have 4 parameters to store per sensor, and
>>> the gain calculation is then done identically for all of them. This
>>> commit gives the gains for imx219 (based on RPi cam_helper), ov5670 and
>>> ov5693.
>>>
>>> Adding a new sensor is pretty straightfoward as one only needs to
>>
>> s/straightfoward/straightforward/
>>
>>> implement the sub-class for it and register that class to the
>>> CameraSensorHelperFactory.
>>>
>>> Signed-off-by: Jean-Michel Hautbois <jeanmichel.hautbois at ideasonboard.com>
>>> ---
>>>  src/ipa/libipa/camera_sensor_helper.cpp | 319 ++++++++++++++++++++++++
>>>  src/ipa/libipa/camera_sensor_helper.h   |  88 +++++++
>>>  src/ipa/libipa/meson.build              |   2 +
>>>  3 files changed, 409 insertions(+)
>>>  create mode 100644 src/ipa/libipa/camera_sensor_helper.cpp
>>>  create mode 100644 src/ipa/libipa/camera_sensor_helper.h
>>>
>>> diff --git a/src/ipa/libipa/camera_sensor_helper.cpp b/src/ipa/libipa/camera_sensor_helper.cpp
>>> new file mode 100644
>>> index 00000000..4e50ee53
>>> --- /dev/null
>>> +++ b/src/ipa/libipa/camera_sensor_helper.cpp
>>> @@ -0,0 +1,319 @@
>>> +/* SPDX-License-Identifier: BSD-2-Clause */
>>> +/*
>>> + * Copyright (C) 2021, Google Inc.
>>> + *
>>> + * camera_sensor_helper.cpp - Helper class providing camera information
>>> + */
>>> +#include "camera_sensor_helper.h"
>>> +
>>> +#include <map>
>>> +
>>> +#include "libcamera/internal/log.h"
>>> +
>>> +/**
>>> + * \file camera_sensor_helper.h
>>> + * \brief Helper class for each sensor
>>> + *
>>> + * Each camera sensor supported by libcamera may need specific informations to
>>
>> s/informations/information/
>>
>>
>>> + * be sent (analogue gain is sensor dependant for instance).
>>> + *
>>> + * Every subclass of CameraSensorHelper shall be registered with libipa using
>>> + * the REGISTER_CAMERA_SENSOR_HELPER() macro.
>>> + */
>>> +
>>> +namespace libcamera {
>>> +
>>> +LOG_DEFINE_CATEGORY(CameraSensorHelper)
>>> +
>>> +namespace ipa {
>>> +
>>> +/**
>>> + * \class CameraSensorHelper
>>> + * \brief Compute sensor tuning parameters using sensor-specific constants
>>> + *
>>> + * CameraSensorHelper instances are unique and sensor dependant.
>>> + */
>>> +
>>> +/**
>>> + * \brief Construct a CameraSensorHelper instance
>>> + * \param[in] name The sensor model name
>>> + *
>>> + * The CameraSensorHelper instances shall never be constructed manually, but always
>>> + * through the CameraSensorHelperFactory::create() method.
>>> + */
>>> +CameraSensorHelper::CameraSensorHelper()
>>> +{
>>> +}
>>> +
>>> +CameraSensorHelper::~CameraSensorHelper()
>>> +{
>>> +}
>>
>> Do these need to be implemented if they're empty ? (Or are they
>> placeholders so they can be populated later? But if that's the case they
>> could be added when needed...
>>

I think you need those as you call them through
CameraSensorHelperFactory, and if you don't, then you will have
something like :
symbol lookup error: /usr/lib/x86_64-linux-gnu/libcamera/ipa_ipu3.so:
undefined symbol:
libcamera::ipa::CameraSensorHelperFactory::create(std::__1::basic_string<char,
std::__1::char_traits<char>, std::__1::allocator<char> > const&)

Even if I suppose we may have them declared by another mean ?

>>> +
>>> +/**
>>> + * CameraSensorHelper::GainCode(double gain)
>>> + * \brief Compute gain code from the analogue gain value
>>> + * \param[in] gain The real gain to pass
>>> + * \return the gain code to pass to V4l2
>>> + *
>>> + * This function aims to abstract the calculation of the gain letting the IPA
>>> + * use the real gain for its estimations.
>>> + *
>>> + * The parameters come from the MIPI Alliance Camera Specification for
>>> + * Camera Command Set (CCS).
>>> + */
>>> +uint32_t CameraSensorHelper::GainCode(double gain) const
>>> +{
>>> +	ASSERT((analogueGainConstants_.m0 == 0) || (analogueGainConstants_.m1 == 0));
>>> +	ASSERT(analogueGainConstants_.type == AnalogueGainLinear);
>>> +	
>>> +	return (analogueGainConstants_.c0 - analogueGainConstants_.c1 * gain) /
>>> +	       (analogueGainConstants_.m1 * gain - analogueGainConstants_.m0);
>>> +}
>>> +
>>> +/**
>>> + * CameraSensorHelper::Gain
>>> + * \brief Compute the real gain from the V4l2 subdev control gain
>>> + * \param[in] gainCode The V4l2 subdev control gain
>>> + * \return The real gain
>>> + *
>>> + * This function aims to abstract the calculation of the gain letting the IPA
>>> + * use the real gain for its estimations. It is the counterpart of the function
>>> + * CameraSensorHelper::getGainCode.
>>> + *
>>> + * The parameters come from the MIPI Alliance Camera Specification for
>>> + * Camera Command Set (CCS).
>>> + */
>>> +double CameraSensorHelper::Gain(uint32_t gainCode) const
>>> +{
>>> +	ASSERT((analogueGainConstants_.m0 == 0) || (analogueGainConstants_.m1 == 0));
>>> +	ASSERT(analogueGainConstants_.type == AnalogueGainLinear);
>>> +
>>> +	return (analogueGainConstants_.m0 * gainCode + analogueGainConstants_.c0) /
>>> +	       (analogueGainConstants_.m1 * gainCode + analogueGainConstants_.c1);
>>> +}
>>> +
>>> +/**
>>> + * \enum CameraSensorHelper::AnalogueGainType
>>> + * \brief Specify the Gain mode supported by the sensor
>>> + *
>>> + * Describes the image sensor analog Gain capabilities.
>>> + * Two modes are possible, depending on the sensor: Global and Alternate.
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::AnalogueGainLinear
>>> + * \brief Sensor supports linear gain
>>> + *
>>> + * The relationship between the integer Gain parameter and the resulting Gain
>>> + * multiplier is given by the following equation:
>>> + *
>>> + * \f$gain=\frac{m0x+c0}{m1x+c1}\f$
>>> + *
>>> + * Where 'x' is the gain control parameter, and m0, m1, c0 and c1 are
>>> + * image-sensor-specific constants exposed by the sensor.
>>> + * These constants are static parameters, and for any given image sensor either
>>> + * m0 or m1 shall be zero.
>>> + *
>>> + * The full Gain equation therefore reduces to either:
>>> + *
>>> + * \f$gain=\frac{c0}{m1x+c1}\f$ or \f$\frac{m0x+c0}{c1}\f$
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::AnalogueGainExponential
>>
>> This mixes Analogue and Analog below. We should at least be consistent
>> in the spelling type used.
>>
>> Same in many places.
>>
>>
>>> + * \brief Sensor supports exponential gain (introduced in CCS v1.1)
>>> + *
>>> + * Starting with CCS v1.1, Alternate Global Analog Gain is also available.
>>> + * If the image sensor supports it, then the global analog Gain can be controlled
>>> + * by linear and exponential gain formula:
>>> + *
>>> + * \f$gain = analogLinearGainGlobal * 2^{analogExponentialGainGlobal}\f$
>>> + * \todo not implemented in libipa
>>> + */
>>> +
>>> +/**
>>> + * \struct CameraSensorHelper::AnalogueGainConstants
>>> + * \brief Analogue gain constants used for gain calculation
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::AnalogueGainConstants::type
>>> + * \brief Analogue gain coding type
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::AnalogueGainConstants::m0
>>> + * \brief Constant used in the analog Gain control coding/decoding
>>> + *
>>> + * Note: either m0 or m1 shall be zero.
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::AnalogueGainConstants::c0
>>> + * \brief Constant used in the analog Gain control coding/decoding
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::AnalogueGainConstants::m1
>>> + * \brief Constant used in the analog Gain control coding/decoding
>>> + *
>>> + * Note: either m0 or m1 shall be zero.
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::AnalogueGainConstants::c1
>>> + * \brief Constant used in the analog Gain control coding/decoding
>>> + */
>>> +
>>> +/**
>>> + * \var CameraSensorHelper::analogueGainConstants_
>>> + * \brief The analogue gain parameters used for calculation
>>> + *
>>> + * The analogue gain is calculated through a formula, and its parameters are
>>> + * sensor specific. Use this variable to store the values at init time.
>>> + *
>>> + */
>>> +
>>> +/**
>>> + * \class CameraSensorHelperFactory
>>> + * \brief Registration of CameraSensorHelperFactory classes and creation of instances
>>> + *
>>> + * To facilitate discovery and instantiation of CameraSensorHelper classes, the
>>> + * CameraSensorHelperFactory class maintains a registry of camera sensor helper
>>> + * classes. Each CameraSensorHelper subclass shall register itself using the
>>> + * REGISTER_CAMERA_SENSOR_HELPER() macro, which will create a corresponding
>>> + * instance of a CameraSensorHelperFactory subclass and register it with the
>>> + * static list of factories.
>>> + */
>>> +
>>> +/**
>>> + * \brief Construct a camera sensor helper factory
>>> + * \param[in] name Name of the camera sensor helper class
>>> + *
>>> + * Creating an instance of the factory registers it with the global list of
>>> + * factories, accessible through the factories() function.
>>> + *
>>> + * The factory \a name is used for debug purpose and shall be unique.
>>> + */
>>> +CameraSensorHelperFactory::CameraSensorHelperFactory(const std::string name)
>>> +	: name_(name)
>>> +{
>>> +	registerType(this);
>>> +}
>>> +
>>> +/**
>>> + * \brief Create an instance of the CameraSensorHelper corresponding to the factory
>>> + *
>>> + * \return A unique pointer to a new instance of the CameraSensorHelper subclass
>>> + * corresponding to the factory
>>> + */
>>> +std::unique_ptr<CameraSensorHelper> CameraSensorHelperFactory::create(const std::string &name)
>>> +{
>>> +	std::vector<CameraSensorHelperFactory *> &factories =
>>> +		CameraSensorHelperFactory::factories();
>>> +
>>> +	for (CameraSensorHelperFactory *factory : factories) {
>>> +		if (name != factory->name_)
>>> +			continue;
>>> +
>>> +		CameraSensorHelper *helper = factory->createInstance();
>>> +		return std::unique_ptr<CameraSensorHelper>(helper);
>>> +	}
>>> +	return nullptr;
>>> +}
>>> +
>>> +/**
>>> + * \brief Add a camera sensor helper class to the registry
>>> + * \param[in] factory Factory to use to construct the camera sensor helper
>>> + *
>>> + * The caller is responsible to guarantee the uniqueness of the camera sensor helper
>>> + * name.
>>> + */
>>> +void CameraSensorHelperFactory::registerType(CameraSensorHelperFactory *factory)
>>> +{
>>> +	std::vector<CameraSensorHelperFactory *> &factories = CameraSensorHelperFactory::factories();
>>> +
>>> +	factories.push_back(factory);
>>> +}
>>> +
>>> +/**
>>> + * \brief Retrieve the list of all camera sensor helper factories
>>> + *
>>> + * The static factories map is defined inside the function to ensures it gets
>>> + * initialized on first use, without any dependency on link order.
>>> + *
>>> + * \return The list of camera sensor helper factories
>>> + */
>>> +std::vector<CameraSensorHelperFactory *> &CameraSensorHelperFactory::factories()
>>> +{
>>> +	static std::vector<CameraSensorHelperFactory *> factories;
>>> +	return factories;
>>> +}
>>> +
>>> +/**
>>> + * CameraSensorHelperFactory::createInstance()
>>> + * \brief Create an instance of the CameraSensorHelper corresponding to the factory
>>> + *
>>> + * This virtual function is implemented by the REGISTER_CAMERA_SENSOR_HELPER()
>>> + * macro. It creates a camera sensor helper instance associated with the camera
>>> + * sensor model.
>>> + *
>>> + * \return A pointer to a newly constructed instance of the CameraSensorHelper
>>> + * subclass corresponding to the factory
>>> + */
>>> +
>>> +/**
>>> + * \def REGISTER_CAMERA_SENSOR_HELPER
>>> + * \brief Register a camera sensor helper with the camera sensor helper factory
>>> + * \param[in] name Sensor model name used to register the class
>>> + * \param[in] helper Class name of CameraSensorHelper derived class to register
>>> + *
>>> + * Register a CameraSensorHelper subclass with the factory and make it available to
>>> + * try and match devices.
>>> + */
>>> +
>>> +/**
>>> + * \class CameraSensorHelperImx219
>>> + * \brief Create and give helpers for the imx219 sensor
>>> + */
>>> +class CameraSensorHelperImx219 : public CameraSensorHelper
>>> +{
>>> +public:
>>> +	CameraSensorHelperImx219()
>>> +	{
>>> +		analogueGainConstants_ = { AnalogueGainLinear, 0, -1, 256, 256 };
>>> +	}
>>> +};
>>> +REGISTER_CAMERA_SENSOR_HELPER("imx219", CameraSensorHelperImx219)
>>> +
>>> +/**
>>> + * \class CameraSensorHelperOv5670
>>> + * \brief Create and give helpers for the ov5670 sensor
>>> + */
>>> +class CameraSensorHelperOv5670 : public CameraSensorHelper
>>> +{
>>> +public:
>>> +	CameraSensorHelperOv5670()
>>> +	{
>>> +		analogueGainConstants_ = { AnalogueGainLinear, 1, 0, 0, 256 };
>>> +	}
>>> +};
>>> +REGISTER_CAMERA_SENSOR_HELPER("ov5670", CameraSensorHelperOv5670)
>>> +
>>> +/**
>>> + * \class CameraSensorHelperOv5693
>>> + * \brief Create and give helpers for the ov5693 sensor
>>> + */
>>> +class CameraSensorHelperOv5693 : public CameraSensorHelper
>>> +{
>>> +public:
>>> +	CameraSensorHelperOv5693()
>>> +	{
>>> +		analogueGainConstants_ = { AnalogueGainLinear, 1, 0, 0, 16 };
>>> +	}
>>> +};
>>> +REGISTER_CAMERA_SENSOR_HELPER("ov5693", CameraSensorHelperOv5693)
>>
>>
>> This is a lot of code for what so far is a table. I assume there will be
>> more complex requirements on the class though so I think it's ok.
>>
> 
> Thanks for all the remarks :-).
> Indeed the helper aims to do more than just calculate an analogue gain :-).
> 
>>
>>> +
>>> +} /* namespace ipa */
>>> +
>>> +} /* namespace libcamera */
>>> diff --git a/src/ipa/libipa/camera_sensor_helper.h b/src/ipa/libipa/camera_sensor_helper.h
>>> new file mode 100644
>>> index 00000000..cc486be8
>>> --- /dev/null
>>> +++ b/src/ipa/libipa/camera_sensor_helper.h
>>> @@ -0,0 +1,88 @@
>>> +/* SPDX-License-Identifier: BSD-2-Clause */
>>
>> Why is this BSD-2 instead of LGPL-2.1-or-later?
>>
>> Is this specifically derived from RPi code?
>> In which case should that be mentioned in the Copyright?
>>
>> Same for the .cpp I think.
>>
> 
> It is more coming from PipelineHandler :-).
> I changed it to LGPL-2.1-or-later to be consistent with the other IPA
> classes.
> 
>>
>>> +/*
>>> + * Copyright (C) 2021, Google Inc.
>>> + *
>>> + * camera_sensor_helper.h - Helper class providing camera information
>>> + */
>>> +#ifndef __LIBCAMERA_IPA_LIBIPA_CAMERA_SENSOR_HELPER_H__
>>> +#define __LIBCAMERA_IPA_LIBIPA_CAMERA_SENSOR_HELPER_H__
>>> +
>>> +#include <stdint.h>
>>> +
>>> +#include <string>
>>> +
>>> +#include <libcamera/class.h>
>>> +#include <libcamera/object.h>
>>> +
>>> +namespace libcamera {
>>> +
>>> +namespace ipa {
>>> +
>>> +class CameraSensorHelper;
>>> +
>>> +class CameraSensorHelper
>>> +{
>>> +public:
>>> +	CameraSensorHelper();
>>> +	virtual ~CameraSensorHelper();
>>> +
>>> +	uint32_t GainCode(double gain) const;
>>> +	double Gain(uint32_t gainCode) const;
>>> +
>>> +protected:
>>> +	enum AnalogueGainType {
>>> +		AnalogueGainLinear = 0,
>>> +		AnalogueGainExponential = 2,
>>> +	};
>>> +
>>> +	struct AnalogueGainConstants {
>>> +		uint16_t type;
>>> +		int16_t m0;
>>> +		int16_t c0;
>>> +		int16_t m1;
>>> +		int16_t c1;
>>> +	};
>>> +
>>> +	AnalogueGainConstants analogueGainConstants_;
>>> +
>>> +private:
>>> +	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraSensorHelper)
>>> +};
>>> +
>>> +class CameraSensorHelperFactory
>>> +{
>>> +public:
>>> +	CameraSensorHelperFactory(const std::string name);
>>> +	virtual ~CameraSensorHelperFactory() = default;
>>> +
>>> +	static std::unique_ptr<CameraSensorHelper> create(const std::string &name);
>>> +
>>> +	static void registerType(CameraSensorHelperFactory *factory);
>>> +	static std::vector<CameraSensorHelperFactory *> &factories();
>>> +
>>> +private:
>>> +	LIBCAMERA_DISABLE_COPY_AND_MOVE(CameraSensorHelperFactory)
>>> +	virtual CameraSensorHelper *createInstance() = 0;
>>> +
>>> +	std::string name_;
>>> +};
>>> +
>>> +#define REGISTER_CAMERA_SENSOR_HELPER(name, helper)               \
>>> +class helper##Factory final : public CameraSensorHelperFactory    \
>>> +{                                                                 \
>>> +public:                                                           \
>>> +	helper##Factory() : CameraSensorHelperFactory(name) {} \
>>> +								  \
>>> +private:                                                          \
>>> +	CameraSensorHelper *createInstance()                      \
>>> +	{                                                         \
>>> +		return new helper();                          \
>>> +	}                                                         \
>>> +};                                                                \
>>> +static helper##Factory global_##helper##Factory;
>>> +
>>> +} /* namespace ipa */
>>> +
>>> +} /* namespace libcamera */
>>> +
>>> +#endif /* __LIBCAMERA_IPA_LIBIPA_CAMERA_SENSOR_HELPER_H__ */
>>> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build
>>> index 038fc490..dc90542c 100644
>>> --- a/src/ipa/libipa/meson.build
>>> +++ b/src/ipa/libipa/meson.build
>>> @@ -2,11 +2,13 @@
>>>  
>>>  libipa_headers = files([
>>>      'algorithm.h',
>>> +    'camera_sensor_helper.h',
>>>      'histogram.h'
>>>  ])
>>>  
>>>  libipa_sources = files([
>>>      'algorithm.cpp',
>>> +    'camera_sensor_helper.cpp',
>>>      'histogram.cpp'
>>>  ])
>>>  
>>>
>>


More information about the libcamera-devel mailing list