17
17
#include < libcamera/control_ids.h>
18
18
#include < libcamera/ipa/core_ipa_interface.h>
19
19
20
+ #include " libcamera/internal/yaml_parser.h"
21
+
20
22
#include " libipa/histogram.h"
21
23
22
24
/* *
@@ -36,6 +38,85 @@ namespace ipa::rkisp1::algorithms {
36
38
37
39
LOG_DEFINE_CATEGORY (RkISP1Agc)
38
40
41
+ int Agc::parseMeteringModes (IPAContext &context, const YamlObject &tuningData)
42
+ {
43
+ if (!tuningData.isDictionary ()) {
44
+ LOG (RkISP1Agc, Error)
45
+ << " 'AeMeteringMode' parameter not found in tuning file" ;
46
+ return -EINVAL;
47
+ }
48
+
49
+ for (const auto &[key, value] : tuningData.asDict ()) {
50
+ if (controls::AeMeteringModeNameValueMap.find (key) ==
51
+ controls::AeMeteringModeNameValueMap.end ()) {
52
+ LOG (RkISP1Agc, Warning)
53
+ << " Skipping unknown metering mode '" << key << " '" ;
54
+ continue ;
55
+ }
56
+
57
+ std::vector<uint8_t > weights =
58
+ value.getList <uint8_t >().value_or (std::vector<uint8_t >{});
59
+ if (weights.size () != context.hw ->numHistogramWeights ) {
60
+ LOG (RkISP1Agc, Warning)
61
+ << " Failed to read metering mode'" << key << " '" ;
62
+ continue ;
63
+ }
64
+
65
+ meteringModes_[controls::AeMeteringModeNameValueMap.at (key)] = weights;
66
+ }
67
+
68
+ if (meteringModes_.empty ()) {
69
+ LOG (RkISP1Agc, Warning)
70
+ << " No metering modes read from tuning file; defaulting to matrix" ;
71
+ int32_t meteringModeId = controls::AeMeteringModeNameValueMap.at (" MeteringMatrix" );
72
+ std::vector<uint8_t > weights (context.hw ->numHistogramWeights , 1 );
73
+
74
+ meteringModes_[meteringModeId] = weights;
75
+ }
76
+
77
+ return 0 ;
78
+ }
79
+
80
+ uint8_t Agc::computeHistogramPredivider (Size &size, enum rkisp1_cif_isp_histogram_mode mode)
81
+ {
82
+ /*
83
+ * The maximum number of pixels that could potentially be in one bin is
84
+ * if all the pixels of the image are in it, multiplied by 3 for the
85
+ * three color channels. The counter for each bin is 16 bits wide, so
86
+ * `factor` thus contains the number of times we'd wrap around. This is
87
+ * obviously the number of pixels that we need to skip to make sure
88
+ * that we don't wrap around, but we compute the square root of it
89
+ * instead, as the skip that we need to program is for both the x and y
90
+ * directions.
91
+ *
92
+ * Even though it looks like dividing into a counter of 65536 would
93
+ * overflow by 1, this is apparently fine according to the hardware
94
+ * documentation, and this successfully gets the expected documented
95
+ * predivider size for cases where:
96
+ * (width / predivider) * (height / predivider) * 3 == 65536.
97
+ *
98
+ * There's a bit of extra rounding math to make sure the rounding goes
99
+ * the correct direction so that the square of the step is big enough
100
+ * to encompass the `factor` number of pixels that we need to skip.
101
+ *
102
+ * \todo Take into account weights. That is, if the weights are low
103
+ * enough we can potentially reduce the predivider to increase
104
+ * precision. This needs some investigation however, as this hardware
105
+ * behavior is undocumented and is only an educated guess.
106
+ */
107
+ int count = mode == RKISP1_CIF_ISP_HISTOGRAM_MODE_RGB_COMBINED ? 3 : 1 ;
108
+ double factor = size.width * size.height * count / 65536.0 ;
109
+ double root = std::sqrt (factor);
110
+ uint8_t predivider;
111
+
112
+ if (std::pow (std::floor (root), 2 ) < factor)
113
+ predivider = static_cast <uint8_t >(std::ceil (root));
114
+ else
115
+ predivider = static_cast <uint8_t >(std::floor (root));
116
+
117
+ return std::clamp<uint8_t >(predivider, 3 , 127 );
118
+ }
119
+
39
120
Agc::Agc ()
40
121
{
41
122
supportsRaw_ = true ;
@@ -59,6 +140,11 @@ int Agc::init(IPAContext &context, const YamlObject &tuningData)
59
140
if (ret)
60
141
return ret;
61
142
143
+ const YamlObject &yamlMeteringModes = tuningData[" AeMeteringMode" ];
144
+ ret = parseMeteringModes (context, yamlMeteringModes);
145
+ if (ret)
146
+ return ret;
147
+
62
148
context.ctrlMap .merge (controls ());
63
149
64
150
return 0 ;
@@ -160,6 +246,7 @@ void Agc::prepare(IPAContext &context, const uint32_t frame,
160
246
frameContext.agc .gain = context.activeState .agc .automatic .gain ;
161
247
}
162
248
249
+ /* \todo Remove this when we can set the below with controls */
163
250
if (frame > 0 )
164
251
return ;
165
252
@@ -178,14 +265,21 @@ void Agc::prepare(IPAContext &context, const uint32_t frame,
178
265
params->meas .hst_config .meas_window = context.configuration .agc .measureWindow ;
179
266
/* Produce the luminance histogram. */
180
267
params->meas .hst_config .mode = RKISP1_CIF_ISP_HISTOGRAM_MODE_Y_HISTOGRAM;
268
+
181
269
/* Set an average weighted histogram. */
182
270
Span<uint8_t > weights{
183
271
params->meas .hst_config .hist_weight ,
184
272
context.hw ->numHistogramWeights
185
273
};
186
- std::fill (weights.begin (), weights.end (), 1 );
187
- /* Step size can't be less than 3. */
188
- params->meas .hst_config .histogram_predivider = 4 ;
274
+ /* \todo Get this from control */
275
+ std::vector<uint8_t > &modeWeights = meteringModes_.at (controls::MeteringMatrix);
276
+ std::copy (modeWeights.begin (), modeWeights.end (), weights.begin ());
277
+
278
+ struct rkisp1_cif_isp_window window = params->meas .hst_config .meas_window ;
279
+ Size windowSize = { window.h_size , window.v_size };
280
+ params->meas .hst_config .histogram_predivider =
281
+ computeHistogramPredivider (windowSize,
282
+ static_cast <rkisp1_cif_isp_histogram_mode>(params->meas .hst_config .mode ));
189
283
190
284
/* Update the configuration for histogram. */
191
285
params->module_cfg_update |= RKISP1_CIF_ISP_MODULE_HST;
0 commit comments