1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* Copyright (C) 2021, Google Inc.
*
* Helper class that performs sensor-specific
* parameter computations
*/
#include "camera_sensor_helper.h"
#include <cmath>
#include <libcamera/base/log.h>
/**
* \file camera_sensor_helper.h
* \brief Helper class that performs sensor-specific parameter computations
*
* Computation of sensor configuration parameters is a sensor specific
* operation. Each CameraHelper derived class computes the value of
* configuration parameters, for example the analogue gain value, using
* sensor-specific functions and constants.
*
* 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 Base class for computing sensor tuning parameters using
* sensor-specific constants
*
* Instances derived from CameraSensorHelper class are sensor-specific.
* Each supported sensor will have an associated base class defined.
*/
/**
* \brief Construct a CameraSensorHelper instance
*
* CameraSensorHelper derived class instances shall never be constructed
* manually but always through the CameraSensorHelperFactoryBase::create()
* function.
*/
/**
* \fn CameraSensorHelper::blackLevel()
* \brief Fetch the black level of the sensor
*
* This function returns the black level of the sensor scaled to a 16bit pixel
* width. If it is unknown an empty optional is returned.
*
* \todo Fill the blanks and add pedestal values for all supported sensors. Once
* done, drop the std::optional<>.
*
* Black levels are typically the result of the following phenomena:
* - Pedestal added by the sensor to pixel values. They are typically fixed,
* sometimes programmable and should be reported in datasheets (but
* documentation is not always available).
* - Dark currents and other physical effects that add charge to pixels in the
* absence of light. Those can depend on the integration time and the sensor
* die temperature, and their contribution to pixel values depend on the
* sensor gains.
*
* The pedestal is usually the value with the biggest contribution to the
* overall black level. In most cases it is either known before or in rare cases
* (there is not a single driver with such a control in the linux kernel) can be
* queried from the sensor. This function provides that fixed, known value.
*
* \return The black level of the sensor, or std::nullopt if not known
*/
/**
* \brief Compute gain code from the analogue gain absolute value
* \param[in] gain The real gain to pass
*
* This function aims to abstract the calculation of the gain letting the IPA
* use the real gain for its estimations.
*
* \return The gain code to pass to V4L2
*/
uint32_t CameraSensorHelper::gainCode(double gain) const
{
const AnalogueGainConstants &k = gainConstants_;
switch (gainType_) {
case AnalogueGainLinear:
ASSERT(k.linear.m0 == 0 || k.linear.m1 == 0);
return (k.linear.c0 - k.linear.c1 * gain) /
(k.linear.m1 * gain - k.linear.m0);
case AnalogueGainExponential:
ASSERT(k.exp.a != 0 && k.exp.m != 0);
return std::log2(gain / k.exp.a) / k.exp.m;
default:
ASSERT(false);
return 0;
}
}
/**
* \brief Compute the real gain from the V4L2 subdev control gain code
* \param[in] gainCode The V4L2 subdev control 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::gainCode.
*
* \return The real gain
*/
double CameraSensorHelper::gain(uint32_t gainCode) const
{
const AnalogueGainConstants &k = gainConstants_;
double gain = static_cast<double>(gainCode);
switch (gainType_) {
case AnalogueGainLinear:
ASSERT(k.linear.m0 == 0 || k.linear.m1 == 0);
return (k.linear.m0 * gain + k.linear.c0) /
(k.linear.m1 * gain + k.linear.c1);
case AnalogueGainExponential:
ASSERT(k.exp.a != 0 && k.exp.m != 0);
return k.exp.a * std::exp2(k.exp.m * gain);
default:
ASSERT(false);
return 0.0;
}
}
/**
* \enum CameraSensorHelper::AnalogueGainType
* \brief The gain calculation modes as defined by the MIPI CCS
*
* Describes the image sensor analogue gain capabilities.
* Two modes are possible, depending on the sensor: Linear and Exponential.
*/
/**
* \var CameraSensorHelper::AnalogueGainLinear
* \brief Gain is computed using linear gain estimation
*
* 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 of 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
* \brief Gain is expressed using an exponential model
*
* The relationship between the integer gain parameter and the resulting gain
* multiplier is given by the following equation:
*
* \f$gain = a \cdot 2^{m \cdot x}\f$
*
* Where 'x' is the gain control parameter, and 'a' and 'm' are image
* sensor-specific constants.
*
* This is a subset of the MIPI CCS exponential gain model with the linear
* factor 'a' being a constant, but with the exponent being configurable
* through the 'm' coefficient.
*
* When the gain is expressed in dB, 'a' is equal to 1 and 'm' to
* \f$log_{2}{10^{\frac{1}{20}}}\f$.
*/
/**
* \struct CameraSensorHelper::AnalogueGainLinearConstants
* \brief Analogue gain constants for the linear gain model
*
* \var CameraSensorHelper::AnalogueGainLinearConstants::m0
* \brief Constant used in the linear gain coding/decoding
*
* \note Either m0 or m1 shall be zero.
*
* \var CameraSensorHelper::AnalogueGainLinearConstants::c0
* \brief Constant used in the linear gain coding/decoding
*
* \var CameraSensorHelper::AnalogueGainLinearConstants::m1
* \brief Constant used in the linear gain coding/decoding
*
* \note Either m0 or m1 shall be zero.
*
* \var CameraSensorHelper::AnalogueGainLinearConstants::c1
* \brief Constant used in the linear gain coding/decoding
*/
/**
* \struct CameraSensorHelper::AnalogueGainExpConstants
* \brief Analogue gain constants for the exponential gain model
*
* \var CameraSensorHelper::AnalogueGainExpConstants::a
* \brief Constant used in the exponential gain coding/decoding
*
* \var CameraSensorHelper::AnalogueGainExpConstants::m
* \brief Constant used in the exponential gain coding/decoding
*/
/**
* \struct CameraSensorHelper::AnalogueGainConstants
* \brief Analogue gain model constants
*
* This union stores the constants used to calculate the analogue gain. The
* CameraSensorHelper::gainType_ variable selects which union member is valid.
*
* \var CameraSensorHelper::AnalogueGainConstants::linear
* \brief Constants for the linear gain model
*
* \var CameraSensorHelper::AnalogueGainConstants::exp
* \brief Constants for the exponential gain model
*/
/**
* \var CameraSensorHelper::blackLevel_
* \brief The black level of the sensor
*/
/**
* \var CameraSensorHelper::gainType_
* \brief The analogue gain model type
*/
/**
* \var CameraSensorHelper::gainConstants_
* \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 CameraSensorHelperFactoryBase
* \brief Base class for camera sensor helper factories
*
* The CameraSensorHelperFactoryBase class is the base of all specializations of
* the CameraSensorHelperFactory class template. It implements the factory
* registration, maintains a registry of factories, and provides access to the
* registered factories.
*/
/**
* \brief Construct a camera sensor helper factory base
* \param[in] name Name of the camera sensor helper class
*
* Creating an instance of the factory base registers it with the global list of
* factories, accessible through the factories() function.
*
* The factory \a name is used to look up factories and shall be unique.
*/
CameraSensorHelperFactoryBase::CameraSensorHelperFactoryBase(const std::string name)
: name_(name)
{
registerType(this);
}
/**
* \brief Create an instance of the CameraSensorHelper corresponding to
* a named factory
* \param[in] name Name of the factory
*
* \return A unique pointer to a new instance of the CameraSensorHelper subclass
* corresponding to the named factory or a null pointer if no such factory
* exists
*/
std::unique_ptr<CameraSensorHelper> CameraSensorHelperFactoryBase::create(const std::string &name)
{
const std::vector<CameraSensorHelperFactoryBase *> &factories =
CameraSensorHelperFactoryBase::factories();
for (const CameraSensorHelperFactoryBase *factory : factories) {
if (name != factory->name_)
continue;
return factory->createInstance();
}
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 CameraSensorHelperFactoryBase::registerType(CameraSensorHelperFactoryBase *factory)
{
std::vector<CameraSensorHelperFactoryBase *> &factories =
CameraSensorHelperFactoryBase::factories();
factories.push_back(factory);
}
/**
* \brief Retrieve the list of all camera sensor helper factories
* \return The list of camera sensor helper factories
*/
std::vector<CameraSensorHelperFactoryBase *> &CameraSensorHelperFactoryBase::factories()
{
/*
* The static factories map is defined inside the function to ensure
* it gets initialized on first use, without any dependency on link
* order.
*/
static std::vector<CameraSensorHelperFactoryBase *> factories;
return factories;
}
/**
* \class CameraSensorHelperFactory
* \brief Registration of CameraSensorHelperFactory classes and creation of instances
* \tparam _Helper The camera sensor helper class type for this factory
*
* To facilitate discovery and instantiation of CameraSensorHelper classes, the
* CameraSensorHelperFactory class implements auto-registration of camera sensor
* helpers. 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.
*/
/**
* \fn CameraSensorHelperFactory::CameraSensorHelperFactory(const char *name)
* \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 CameraSensorHelperFactoryBase::factories()
* function.
*
* The factory \a name is used to look up factories and shall be unique.
*/
/**
* \fn CameraSensorHelperFactory::createInstance() const
* \brief Create an instance of the CameraSensorHelper corresponding to the
* factory
*
* \return A unique 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 sensors.
*/
/* -----------------------------------------------------------------------------
* Sensor-specific subclasses
*/
#ifndef __DOXYGEN__
/*
* 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 CameraSensorHelperAr0521 : public CameraSensorHelper
{
public:
uint32_t gainCode(double gain) const override
{
gain = std::clamp(gain, 1.0, 15.5);
unsigned int coarse = std::log2(gain);
unsigned int fine = (gain / (1 << coarse) - 1) * kStep_;
return (coarse << 4) | (fine & 0xf);
}
double gain(uint32_t gainCode) const override
{
unsigned int coarse = gainCode >> 4;
unsigned int fine = gainCode & 0xf;
return (1 << coarse) * (1 + fine / kStep_);
}
private:
static constexpr double kStep_ = 16;
};
REGISTER_CAMERA_SENSOR_HELPER("ar0521", CameraSensorHelperAr0521)
class CameraSensorHelperImx219 : public CameraSensorHelper
{
public:
CameraSensorHelperImx219()
{
/* From datasheet: 64 at 10bits. */
blackLevel_ = 4096;
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 0, 256, -1, 256 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx219", CameraSensorHelperImx219)
class CameraSensorHelperImx258 : public CameraSensorHelper
{
public:
CameraSensorHelperImx258()
{
/* From datasheet: 0x40 at 10bits. */
blackLevel_ = 4096;
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 0, 512, -1, 512 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx258", CameraSensorHelperImx258)
class CameraSensorHelperImx283 : public CameraSensorHelper
{
public:
CameraSensorHelperImx283()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 0, 2048, -1, 2048 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx283", CameraSensorHelperImx283)
class CameraSensorHelperImx290 : public CameraSensorHelper
{
public:
CameraSensorHelperImx290()
{
gainType_ = AnalogueGainExponential;
gainConstants_.exp = { 1.0, expGainDb(0.3) };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx290", CameraSensorHelperImx290)
class CameraSensorHelperImx296 : public CameraSensorHelper
{
public:
CameraSensorHelperImx296()
{
gainType_ = AnalogueGainExponential;
gainConstants_.exp = { 1.0, expGainDb(0.1) };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx296", CameraSensorHelperImx296)
class CameraSensorHelperImx327 : public CameraSensorHelperImx290
{
};
REGISTER_CAMERA_SENSOR_HELPER("imx327", CameraSensorHelperImx327)
class CameraSensorHelperImx335 : public CameraSensorHelper
{
public:
CameraSensorHelperImx335()
{
/* From datasheet: 0x32 at 10bits. */
blackLevel_ = 3200;
gainType_ = AnalogueGainExponential;
gainConstants_.exp = { 1.0, expGainDb(0.3) };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx335", CameraSensorHelperImx335)
class CameraSensorHelperImx415 : public CameraSensorHelper
{
public:
CameraSensorHelperImx415()
{
gainType_ = AnalogueGainExponential;
gainConstants_.exp = { 1.0, expGainDb(0.3) };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx415", CameraSensorHelperImx415)
class CameraSensorHelperImx477 : public CameraSensorHelper
{
public:
CameraSensorHelperImx477()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 0, 1024, -1, 1024 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("imx477", CameraSensorHelperImx477)
class CameraSensorHelperOv2685 : public CameraSensorHelper
{
public:
CameraSensorHelperOv2685()
{
/*
* The Sensor Manual doesn't appear to document the gain model.
* This has been validated with some empirical testing only.
*/
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov2685", CameraSensorHelperOv2685)
class CameraSensorHelperOv2740 : public CameraSensorHelper
{
public:
CameraSensorHelperOv2740()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov2740", CameraSensorHelperOv2740)
class CameraSensorHelperOv4689 : public CameraSensorHelper
{
public:
CameraSensorHelperOv4689()
{
/* From datasheet: 0x40 at 12bits. */
blackLevel_ = 1024;
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov4689", CameraSensorHelperOv4689)
class CameraSensorHelperOv5640 : public CameraSensorHelper
{
public:
CameraSensorHelperOv5640()
{
/* From datasheet: 0x10 at 10bits. */
blackLevel_ = 1024;
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 16 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5640", CameraSensorHelperOv5640)
class CameraSensorHelperOv5647 : public CameraSensorHelper
{
public:
CameraSensorHelperOv5647()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 16 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5647", CameraSensorHelperOv5647)
class CameraSensorHelperOv5670 : public CameraSensorHelper
{
public:
CameraSensorHelperOv5670()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5670", CameraSensorHelperOv5670)
class CameraSensorHelperOv5675 : public CameraSensorHelper
{
public:
CameraSensorHelperOv5675()
{
/* From Linux kernel driver: 0x40 at 10bits. */
blackLevel_ = 4096;
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5675", CameraSensorHelperOv5675)
class CameraSensorHelperOv5693 : public CameraSensorHelper
{
public:
CameraSensorHelperOv5693()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 16 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov5693", CameraSensorHelperOv5693)
class CameraSensorHelperOv64a40 : public CameraSensorHelper
{
public:
CameraSensorHelperOv64a40()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov64a40", CameraSensorHelperOv64a40)
class CameraSensorHelperOv8858 : public CameraSensorHelper
{
public:
CameraSensorHelperOv8858()
{
gainType_ = AnalogueGainLinear;
/*
* \todo Validate the selected 1/128 step value as it differs
* from what the sensor manual describes.
*
* See: https://patchwork.linuxtv.org/project/linux-media/patch/20221106171129.166892-2-nicholas@rothemail.net/#142267
*/
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov8858", CameraSensorHelperOv8858)
class CameraSensorHelperOv8865 : public CameraSensorHelper
{
public:
CameraSensorHelperOv8865()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov8865", CameraSensorHelperOv8865)
class CameraSensorHelperOv13858 : public CameraSensorHelper
{
public:
CameraSensorHelperOv13858()
{
gainType_ = AnalogueGainLinear;
gainConstants_.linear = { 1, 0, 0, 128 };
}
};
REGISTER_CAMERA_SENSOR_HELPER("ov13858", CameraSensorHelperOv13858)
#endif /* __DOXYGEN__ */
} /* namespace ipa */
} /* namespace libcamera */
|