[PATCH v2 1/2] test: ipa: libipa: Add CameraSensorHelper Gain Model tests

Laurent Pinchart laurent.pinchart at ideasonboard.com
Mon Jun 3 18:32:08 CEST 2024


Hi Kieran,

Thank you for the patch.

On Fri, May 24, 2024 at 02:52:55PM +0100, Kieran Bingham wrote:
> Introduce a test that validates conversion of the gain model
> in both Linear and Exponential models.
> 
> It should be expected that if a gainCode produces a given gain
> value, then converting that gain back should produce the same
> result.

I think we have a general agreement on this.

> The test is known to fail and is marked as such. The test will be
> updated with the corresponding fix.
> 
> Signed-off-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
> ---
>  test/ipa/camera_sensor_helper.cpp | 101 ++++++++++++++++++++++++++++++
>  test/ipa/meson.build              |   5 +-
>  2 files changed, 105 insertions(+), 1 deletion(-)
>  create mode 100644 test/ipa/camera_sensor_helper.cpp
> 
> diff --git a/test/ipa/camera_sensor_helper.cpp b/test/ipa/camera_sensor_helper.cpp
> new file mode 100644
> index 000000000000..d8a8c6850a04
> --- /dev/null
> +++ b/test/ipa/camera_sensor_helper.cpp
> @@ -0,0 +1,101 @@
> +/* SPDX-License-Identifier: GPL-2.0-or-later */
> +/*
> + * Copyright (C) 2024, Ideas on Board Oy.
> + */
> +
> +#include "libipa/camera_sensor_helper.h"
> +
> +#include <iostream>
> +#include <string.h>
> +
> +#include "test.h"
> +
> +using namespace std;
> +using namespace libcamera;
> +using namespace libcamera::ipa;
> +
> +/*
> + * Helper function to compute the m parameter of the exponential gain model
> + * when the gain code is expressed in dB.
> + */
> +static constexpr double expGainDb(double step)
> +{
> +	constexpr double log2_10 = 3.321928094887362;
> +
> +	/*
> +         * The gain code is expressed in step * dB (e.g. in 0.1 dB steps):
> +         *
> +         * G_code = G_dB/step = 20/step*log10(G_linear)
> +         *
> +         * Inverting the formula, we get
> +         *
> +         * G_linear = 10^(step/20*G_code) = 2^(log2(10)*step/20*G_code)
> +         */
> +	return log2_10 * step / 20;
> +}
> +
> +class CameraSensorHelperExponential : public CameraSensorHelper
> +{
> +public:
> +	CameraSensorHelperExponential()
> +	{
> +		gainType_ = AnalogueGainExponential;
> +		gainConstants_.exp = { 1.0, expGainDb(0.3) };
> +	}
> +};
> +
> +class CameraSensorHelperLinear : public CameraSensorHelper
> +{
> +public:
> +	CameraSensorHelperLinear()
> +	{
> +		gainType_ = AnalogueGainLinear;
> +		gainConstants_.linear = { 0, 1024, -1, 1024 };
> +	}
> +};

Could we test all the helpers, instead of two particular implementations
? CameraSensorHelperFactoryBase provides a list of factories. We would
need to expose their name through a const accessor to be able to call
create(). Or do you think that would be overkill ? We have helpers that
don't use the two standard models, because someone decided to implement
something more "clever" in the hardware. It would be useful to cover
those in the tests I think.

> +
> +class CameraSensorHelperTest : public Test

As this tests the gain models only, I'd name the class
CameraSensorGainsTest or something similar. Same for the source file.

> +{
> +protected:
> +	int testGainModel(CameraSensorHelper &helper)
> +	{
> +		int ret = TestPass;
> +
> +		/*
> +		 * Arbitrarily test 255 code positions assuming 8 bit fields
> +		 * are the normal maximum for a gain code register.
> +		 */

This bothers me, mostly because the right solution would require
extending the helpers to expose the min/max gains, and *that* is
bothersome :-S So far we rely on kernel drivers to provide the
information. Do you think we should bite the bullet ?

One issue to consider is that the gain code may not be a continuous
value. A sensor I'm working on splits the analog gain in coarse and fine
gains, stored in bits [6:4] and [3:0]. The coarse gain is an exponential
gain, expressed as the exponent of a power of two, ranging from 2^0 to
2^4. The fine gain is an inverse linear gain following the formula

	1 / (1 - fine / 32)

The fine gain value is quantized differently depending on the coarse
gain. When the coarse gain is 2^0 or 2^2, the fine gain code can take
any value in the [0, 15] range. When the coarse gain is 2^1 or 2^3, the
fine gain code is restricted to even values. When the coarse gain is
2^4, the fine gain code is restricted to multiples of 4.

I believe the hardware will operate correctly when those constraints are
not met, but will ignore the LSB or 2 LSBs of the fine gain code
depending on the coarse gain. This leads to, for instance,

	coarse = 2^1, fine = 4 -> gain = 2.2857142857142856
	coarse = 2^1, fine = 5 -> gain = 2.2857142857142856

The code -> gain -> code conversion can lead to the same result for all
valid code values, but not for all code values in the [min, max] range.
I wonder how to handle that in the test.

> +		for (unsigned int i = 0; i < 255; i++) {
> +			float gain = helper.gain(i);
> +			uint32_t gainCode = helper.gainCode(gain);
> +
> +			if (i != gainCode) {
> +				std::cout << "Gain conversions failed: "
> +					  << i << " : " << gain << " : "
> +					  << gainCode << std::endl;
> +
> +				ret = TestFail;
> +			}
> +		};
> +
> +		return ret;
> +	}
> +
> +	int run() override
> +	{
> +		unsigned int failures = 0;
> +
> +		CameraSensorHelperExponential exponential;
> +		CameraSensorHelperLinear linear;
> +
> +		if (testGainModel(exponential) == TestFail)
> +			failures++;
> +
> +		if (testGainModel(linear) == TestFail)
> +			failures++;
> +
> +		return failures ? TestFail : TestPass;
> +	}
> +};
> +
> +TEST_REGISTER(CameraSensorHelperTest)
> diff --git a/test/ipa/meson.build b/test/ipa/meson.build
> index 180b0da0a51a..abc2456fc341 100644
> --- a/test/ipa/meson.build
> +++ b/test/ipa/meson.build
> @@ -1,6 +1,8 @@
>  # SPDX-License-Identifier: CC0-1.0
>  
>  ipa_test = [
> +    {'name': 'camera_sensor_helper', 'sources': ['camera_sensor_helper.cpp'],
> +     'should_fail': true},

If you make it multi-line, let's make it so properly :-)

    {
        'name': 'camera_sensor_helper',
        'sources': ['camera_sensor_helper.cpp'],
        'should_fail': true,
    },

>      {'name': 'ipa_module_test', 'sources': ['ipa_module_test.cpp']},
>      {'name': 'ipa_interface_test', 'sources': ['ipa_interface_test.cpp']},
>  ]
> @@ -11,5 +13,6 @@ foreach test : ipa_test
>                       link_with : [libipa, test_libraries],
>                       include_directories : [libipa_includes, test_includes_internal])
>  
> -    test(test['name'], exe, suite : 'ipa')
> +    test(test['name'], exe, suite : 'ipa',
> +         should_fail : test.get('should_fail', false))
>  endforeach

-- 
Regards,

Laurent Pinchart


More information about the libcamera-devel mailing list