Paparazzi UAS  v5.14.0_stable-0-g3f680d1
Paparazzi is a free software Unmanned Aircraft System.
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages
bebop_ae_awb.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) Freek van Tienen
3  *
4  * This file is part of paparazzi
5  *
6  * paparazzi is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * paparazzi is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with paparazzi; see the file COPYING. If not, see
18  * <http://www.gnu.org/licenses/>.
19  */
26 #include "bebop_ae_awb.h"
27 #include "boards/bebop.h"
28 #include "boards/bebop/mt9f002.h"
29 #include "lib/isp/libisp.h"
31 
32 #define MAX_HIST_Y 255
33 #define MIN_HIST_Y 1
34 
35 #ifndef BEBOP_AE_AWB_CAMERA
36 #define BEBOP_AE_AWB_CAMERA front_camera
37 #endif
38 PRINT_CONFIG_VAR(BEBOP_AE_AWB_CAMERA)
39 
40 #ifndef BEBOP_AE_AWB_VERBOSE
41 #define BEBOP_AE_AWB_VERBOSE 0
42 #endif
43 PRINT_CONFIG_VAR(BEBOP_AE_AWB_VERBOSE)
44 
45 #define PRINT(string,...) fprintf(stderr, "[bebop_ae_awb->%s()] " string,__FUNCTION__ , ##__VA_ARGS__)
46 
47 #if BEBOP_AE_AWB_VERBOSE
48 #include "stdio.h"
49 #define VERBOSE_PRINT PRINT
50 #else
51 #define VERBOSE_PRINT(...)
52 #endif
53 
54 // thread nice setting
55 #ifndef BEBOP_AE_AWB_NICE
56 #define BEBOP_AE_AWB_NICE 5
57 #endif
58 
59 #ifndef BEBOP_AUTO_EXPOSURE
60 #define BEBOP_AUTO_EXPOSURE true
61 #endif
62 
63 // Gain to apply to autoexposure change
64 #ifndef BEBOP_AE_EXPOSURE_GAIN
65 #define BEBOP_AE_EXPOSURE_GAIN 0.8
66 #endif
67 PRINT_CONFIG_VAR(BEBOP_AE_EXPOSURE_GAIN)
68 
69 // Bin index to be considered as the center of the brightness bins
70 #ifndef BEBOP_AE_MIDDLE_INDEX
71 #define BEBOP_AE_MIDDLE_INDEX 180
72 #endif
73 PRINT_CONFIG_VAR(BEBOP_AE_MIDDLE_INDEX)
74 
75 // Proportion of dark bins to ignore in the computation of the autoexposure
76 #ifndef BEBOP_AE_DARK_IGNORE
77 #define BEBOP_AE_DARK_IGNORE 0.4
78 #endif
79 PRINT_CONFIG_VAR(BEBOP_AE_DARK_IGNORE)
80 
81 // Proportion of bright bins to ignore in the computation of the autoexposure
82 #ifndef BEBOP_AE_BRIGHT_IGNORE
83 #define BEBOP_AE_BRIGHT_IGNORE 0.2
84 #endif
85 PRINT_CONFIG_VAR(BEBOP_AE_BRIGHT_IGNORE)
86 
87 // Number of dark bins to consider as dark in the autoexposure
88 #ifndef BEBOP_AE_DARK_BINS
89 #define BEBOP_AE_DARK_BINS 80
90 #endif
91 PRINT_CONFIG_VAR(BEBOP_AE_DARK_BINS)
92 
93 // Number of bright bins to consider as bright in autoexposure
94 #ifndef BEBOP_AE_BRIGHT_BINS
95 #define BEBOP_AE_BRIGHT_BINS 20
96 #endif
97 PRINT_CONFIG_VAR(BEBOP_AE_BRIGHT_BINS)
98 
99 #ifndef BEBOP_AUTO_WHITE_BALANCE
100 #define BEBOP_AUTO_WHITE_BALANCE true
101 #endif
102 
103 // Minimum gain for AWB
104 #ifndef BEBOP_AWB_MIN_GAINS
105 #define BEBOP_AWB_MIN_GAINS 1.0
106 #endif
107 PRINT_CONFIG_VAR(BEBOP_AE_MIN_GAINS)
108 
109 // Maximum gain for AWB
110 #ifndef BEBOP_AWB_MAX_GAINS
111 #define BEBOP_AWB_MAX_GAINS 63.5
112 #endif
113 PRINT_CONFIG_VAR(BEBOP_AWB_MAX_GAINS)
114 
115 //Gain to apply to AWB changes
116 #ifndef BEBOP_AWB_GAIN
117 #define BEBOP_AWB_GAIN 0.25
118 #endif
119 PRINT_CONFIG_VAR(BEBOP_AWB_GAIN)
120 
121 // Activates gain scheduling for the AWB, helps to handle fast changes in brightness
122 #ifndef BEBOP_AWB_GAIN_SCHEDULING
123 #define BEBOP_AWB_GAIN_SCHEDULING false
124 #endif
125 PRINT_CONFIG_VAR(BEBOP_AWB_GAIN_SCHEDULING)
126 
127 // Target white balance
128 #ifndef BEBOP_AWB_GAIN_SCHEDULING_TARGET
129 #define BEBOP_AWB_GAIN_SCHEDULING_TARGET 10.0
130 #endif
131 PRINT_CONFIG_VAR(BEBOP_AWB_GAIN_SCHEDULING_TARGET)
132 
133 // Tolerance around the target exposure outside of which gain scheduling is activated
134 #ifndef BEBOP_AWB_GAIN_SCHEDULING_TOLERANCE
135 #define BEBOP_AWB_GAIN_SCHEDULING_TOLERANCE 7.5
136 #endif
137 PRINT_CONFIG_VAR(BEBOP_AWB_GAIN_SCHEDULING_TOLERANCE)
138 
139 // Step size of the AWB gain scheduling correction
140 #ifndef BEBOP_AWB_GAIN_SCHEDULING_STEP
141 #define BEBOP_AWB_GAIN_SCHEDULING_STEP 0.02
142 #endif
143 PRINT_CONFIG_VAR(BEBOP_AWB_GAIN_SCHEDULING_STEP)
144 
145 // Minimum number of grey pixels in image, when lower, the AWB is reset
146 #ifndef BEBOP_AWB_MIN_GREY_PIXELS
147 #define BEBOP_AWB_MIN_GREY_PIXELS 1000
148 #endif
149 PRINT_CONFIG_VAR(BEBOP_AWB_MIN_GREY_PIXELS)
150 
153 
154 static void bebop_awb_reset(void)
155 {
160  // Set gains
162 }
163 
164 static void bebop_ae_reset(void)
165 {
168 }
169 
170 static struct image_t *update_ae_awb(struct image_t *img)
171 {
172  static struct isp_yuv_stats_t yuv_stats;
173 
174  if (isp_get_statistics_yuv(&yuv_stats) == 0 && yuv_stats.nb_valid_Y > 1) {
175  // get timing stats
176  static uint32_t prev_time = 0;
177  float dt = (img->pprz_ts - prev_time) * 1e-6f;
178  Bound(dt, 0.f, 1.f);
179  prev_time = img->pprz_ts;
180 
181  // handle any change in active setting
182  if (ae_set.prev_active != ae_set.active) {
183  bebop_ae_reset();
185  }
186 
188  bebop_awb_reset();
190  }
191 
192  /*
193  * Auto-exposure (Histogram median centering)
194  */
195  if (ae_set.active) {
196  static float prev_real_exposure = 0.f;
197  static uint8_t prev_exposure_stuck = 0;
198 
199  // Calculate the cummulative histogram based on the histogram
200  static uint32_t cdf[MAX_HIST_Y];
201  cdf[MIN_HIST_Y - 1] = 0;
202 
203  for (int i = MIN_HIST_Y; i < MAX_HIST_Y; i++) {
204  cdf[i] = cdf[i - 1] + yuv_stats.ae_histogram_Y[i];
205  }
206 
207  // Calculate the indices of the dark and bright bins
208  uint8_t dark_index = MIN_HIST_Y + ae_set.dark_bins;
209  if (dark_index > MAX_HIST_Y) {
210  dark_index = MAX_HIST_Y;
211  }
212  uint8_t bright_index = MAX_HIST_Y - ae_set.bright_bins;
213  if (bright_index < MIN_HIST_Y || bright_index > MAX_HIST_Y) {
214  bright_index = dark_index;
215  }
216 
217  // Calculate the median number of pixels ignoring the dark_ignore % and bright-ignore %
218  uint32_t median_pixels = (uint32_t) roundf(((1.f - ae_set.dark_ignore) * cdf[dark_index] +
219  cdf[bright_index - 1] - cdf[dark_index] +
220  (1.f - ae_set.bright_ignore) * (cdf[MAX_HIST_Y - 1] - cdf[bright_index - 1]))
221  / 2.0f);
222 
223  // Find the level that contains the median
224  uint32_t current_pixels = 0;
225  uint8_t ae_current_ind = MIN_HIST_Y - 1;
226  while (current_pixels < median_pixels && ae_current_ind < MAX_HIST_Y) {
227  ae_current_ind++;
228  if (ae_current_ind <= dark_index) {
229  // In dark bin - ignore dark_ignore %
230  current_pixels += (1 - ae_set.dark_ignore) * yuv_stats.ae_histogram_Y[ae_current_ind];
231  } else if (ae_current_ind >= bright_index) {
232  // In bright bin - ignore bright_ignore %
233  current_pixels += (1 - ae_set.bright_ignore) * yuv_stats.ae_histogram_Y[ae_current_ind];
234  } else {
235  // In centre bin - 100%
236  current_pixels += yuv_stats.ae_histogram_Y[ae_current_ind];
237  }
238  }
239 
240  float ae_current_level = (float)ae_current_ind;
241  if (yuv_stats.ae_histogram_Y[ae_current_ind] > 0) {
242  // Calculate decimal level
243  ae_current_level -= (current_pixels - median_pixels) / (float)yuv_stats.ae_histogram_Y[ae_current_ind];
244  }
245 
246  if (yuv_stats.awb_sum_Y > ae_current_level * yuv_stats.awb_nb_grey_pixels) {
247  // too many saturated pixels so median no longer good estimate
248  ae_current_level = (float)yuv_stats.awb_sum_Y / yuv_stats.awb_nb_grey_pixels;
249  }
250 
251  // that level is supposed to be 'middle_index'
252  float adjustment = 1.f;
253  if (ae_current_level > 1e-5) {
254  adjustment = 1.f + ae_set.exposure_gain * (ae_set.middle_index / ae_current_level - 1.f) * dt;
255  Bound(adjustment, 1 / 16.f, 16.f);
256  }
257 
258  // Calculate exposure based on adjustment
260  Bound(mt9f002.target_exposure, 1 / 128.f, 80.f);
261 
262  // Sometimes the exposure seems to freeze and it isn't updated. Try this hack to fix it
263  // 10 consecutive YUV statistics leading to the exact same real_exposure is highly unlikely
264  if (prev_real_exposure == mt9f002.real_exposure && prev_exposure_stuck < 10) {
265  prev_exposure_stuck++;
266  } else if (prev_real_exposure == mt9f002.real_exposure && prev_exposure_stuck >= 10) {
267  // Bump the target exposure in order to unfreeze the exposure
269  mt9f002.target_exposure *= 1.125f;
270  } else {
271  mt9f002.target_exposure /= 1.125f;
272  }
273  } else {
274  prev_exposure_stuck = 0;
275  }
276  prev_real_exposure = mt9f002.real_exposure;
277 
278  // Verbose prints
279  VERBOSE_PRINT("AE lvl: target %d, actual %3.2f (%d pixels)\n", ae_set.middle_index, ae_current_level,
280  yuv_stats.nb_valid_Y);
281  VERBOSE_PRINT("AE exp: target %5.2f ms, real %5.2f ms (%f)\n", mt9f002.target_exposure, mt9f002.real_exposure,
282  adjustment);
283 
285  }
286 
287  /*
288  * Auto white-balance (Robust Automatic White Balance Algorithm using Gray Color Points in Images - Huo et al.)
289  */
290  if (awb_set.active) {
291  if (yuv_stats.awb_nb_grey_pixels) {
292  VERBOSE_PRINT("avgU = %d / %d = %d\n", yuv_stats.awb_sum_U, yuv_stats.awb_nb_grey_pixels,
293  yuv_stats.awb_sum_U / yuv_stats.awb_nb_grey_pixels);
294  VERBOSE_PRINT("avgV = %d / %d = %d\n", yuv_stats.awb_sum_V, yuv_stats.awb_nb_grey_pixels,
295  yuv_stats.awb_sum_V / yuv_stats.awb_nb_grey_pixels);
296  }
297  // |B| = |Y| + |U| && |R| = |Y| + |V|
298  // ideal:
299  // |U| = |V| = 0 && |B| = |R| = |Y|
300  // so:
301  // gain_blue *= |Y| / (|Y| + |U|) --> ^0.25 in order to make less aggressive updates
302  // gain_red *= |Y| / (|Y| + |V|) --> ^0.25 in order to make less aggressive updates
303  float blue_adj = ((float)(yuv_stats.awb_sum_Y)) / ((float)(yuv_stats.awb_sum_Y + yuv_stats.awb_sum_U - 128.f *
304  yuv_stats.awb_nb_grey_pixels)) - 1.f;
305  mt9f002.gain_blue *= 1 + awb_set.gain * blue_adj * dt;
308  }
309 
310  float red_adj = ((float)(yuv_stats.awb_sum_Y)) / ((float)(yuv_stats.awb_sum_Y + yuv_stats.awb_sum_V - 128.f *
311  yuv_stats.awb_nb_grey_pixels)) - 1.f;
312  mt9f002.gain_red *= 1 + awb_set.gain * red_adj * dt;
315  }
316 
317  /*
318  * Gain scheduling
319  */
320  if (awb_set.gain_scheduling) {
321  int8_t gs_adjustDir = 0;
322  // Let's try to center the exposure, that way we'll be able to account for fast changes in brightness
324  gs_adjustDir = +1;
326  gs_adjustDir = -1;
327  }
328  mt9f002.gain_blue *= 1 + gs_adjustDir * awb_set.gain_scheduling_step;
329  mt9f002.gain_red *= 1 + gs_adjustDir * awb_set.gain_scheduling_step;
330  mt9f002.gain_green1 *= 1 + gs_adjustDir * awb_set.gain_scheduling_step;
331  mt9f002.gain_green2 *= 1 + gs_adjustDir * awb_set.gain_scheduling_step;
332  }
333 
334  // Bound gains
339 
340  // check that gains have not exploded
344  bebop_awb_reset();
345  } else {
346  VERBOSE_PRINT("blue %f, red %f, green 1 %f green 2 %f\n", mt9f002.gain_blue, mt9f002.gain_red, mt9f002.gain_green1,
348  // Set gains
350  }
351  }
352  }
353 
354  return NULL;
355 }
356 
358 {
367 
375 
377 
378  bebop_ae_reset();
379  bebop_awb_reset();
380 }
float dark_ignore
Definition: bebop_ae_awb.h:36
static struct image_t * update_ae_awb(struct image_t *img)
Definition: bebop_ae_awb.c:170
static void bebop_awb_reset(void)
Definition: bebop_ae_awb.c:154
uint32_t nb_valid_Y
Definition: libisp.h:99
void mt9f002_set_exposure(struct mt9f002_t *mt)
Set the exposure configuration Depends on the blanking (and therefore the FPS)
Definition: mt9f002.c:666
uint8_t dark_bins
Definition: bebop_ae_awb.h:38
#define MAX_HIST_Y
Definition: bebop_ae_awb.c:32
uint32_t awb_sum_Y
Definition: libisp.h:95
#define BEBOP_AE_MIDDLE_INDEX
Definition: bebop_ae_awb.c:71
float gain_scheduling_step
Definition: bebop_ae_awb.h:50
#define MIN_HIST_Y
Definition: bebop_ae_awb.c:33
#define MT9F002_GAIN_GREEN2
Definition: mt9f002.h:84
uint8_t middle_index
Definition: bebop_ae_awb.h:37
Definition: image.h:43
float gain_scheduling_tolerance
Definition: bebop_ae_awb.h:49
#define BEBOP_AWB_MIN_GAINS
Definition: bebop_ae_awb.c:105
#define BEBOP_AWB_MAX_GAINS
Definition: bebop_ae_awb.c:111
#define BEBOP_AE_AWB_NICE
Definition: bebop_ae_awb.c:56
#define BEBOP_AUTO_EXPOSURE
Definition: bebop_ae_awb.c:60
int isp_get_statistics_yuv(struct isp_yuv_stats_t *yuv_stats)
Definition: libisp.c:201
float gain_blue
Gain for the Blue pixels [1.5 -> 63.50].
Definition: mt9f002.h:154
uint32_t pprz_ts
The timestamp in us since system startup.
Definition: image.h:49
uint32_t awb_nb_grey_pixels
Definition: libisp.h:98
bool gain_scheduling
Definition: bebop_ae_awb.h:47
#define MT9F002_GAIN_GREEN1
Definition: mt9f002.h:80
#define BEBOP_AWB_GAIN_SCHEDULING_TARGET
Definition: bebop_ae_awb.c:129
void bebop_ae_awb_init(void)
Definition: bebop_ae_awb.c:357
#define BEBOP_AE_DARK_IGNORE
Definition: bebop_ae_awb.c:77
#define MT9F002_TARGET_EXPOSURE
Exposure of the front camera of the bebop.
Definition: mt9f002.h:71
#define BEBOP_AE_BRIGHT_BINS
Definition: bebop_ae_awb.c:95
#define BEBOP_AE_AWB_VERBOSE
Definition: bebop_ae_awb.c:41
unsigned long uint32_t
Definition: types.h:18
bool prev_active
Definition: bebop_ae_awb.h:33
float gain_green1
Gain for the GreenR pixels [1.5 -> 63.50].
Definition: mt9f002.h:153
Computer vision framework for onboard processing.
uint32_t awb_sum_V
Definition: libisp.h:97
uint32_t awb_sum_U
Definition: libisp.h:96
#define BEBOP_AWB_GAIN
Definition: bebop_ae_awb.c:117
#define VERBOSE_PRINT(...)
Definition: bebop_ae_awb.c:51
#define BEBOP_AWB_GAIN_SCHEDULING
Definition: bebop_ae_awb.c:123
void mt9f002_set_gains(struct mt9f002_t *mt)
Sets the GreenR, Blue, Red and GreenB gains.
Definition: mt9f002.c:772
#define BEBOP_AWB_GAIN_SCHEDULING_TOLERANCE
Definition: bebop_ae_awb.c:135
#define BEBOP_AWB_GAIN_SCHEDULING_STEP
Definition: bebop_ae_awb.c:141
struct video_listener * cv_add_to_device_async(struct video_config_t *device, cv_function func, int nice_level, uint16_t fps)
Definition: cv.c:82
float gain_red
Gain for the Red pixels [1.5 -> 63.50].
Definition: mt9f002.h:155
#define BEBOP_AWB_MIN_GREY_PIXELS
Definition: bebop_ae_awb.c:147
#define BEBOP_AUTO_WHITE_BALANCE
Definition: bebop_ae_awb.c:100
#define BEBOP_AE_DARK_BINS
Definition: bebop_ae_awb.c:89
#define BEBOP_AE_EXPOSURE_GAIN
Definition: bebop_ae_awb.c:65
float gain_green2
Gain for the GreenB pixels [1.5 -> 63.50].
Definition: mt9f002.h:156
Initialization and configuration of the MT9F002 CMOS Chip.
float real_exposure
Real exposure time in ms.
Definition: mt9f002.h:151
float gain_scheduling_target
Definition: bebop_ae_awb.h:48
static void bebop_ae_reset(void)
Definition: bebop_ae_awb.c:164
unsigned char uint8_t
Definition: types.h:14
struct mt9f002_t mt9f002
Definition: board.c:39
float exposure_gain
Definition: bebop_ae_awb.h:34
#define BEBOP_AE_AWB_CAMERA
Definition: bebop_ae_awb.c:36
#define MT9F002_GAIN_RED
Definition: mt9f002.h:88
struct awb_setting_t awb_set
Definition: bebop_ae_awb.c:152
float bright_ignore
Definition: bebop_ae_awb.h:35
uint8_t bright_bins
Definition: bebop_ae_awb.h:39
signed char int8_t
Definition: types.h:15
#define MT9F002_GAIN_BLUE
Definition: mt9f002.h:92
uint32_t ae_histogram_Y[256]
Definition: libisp.h:100
#define BEBOP_AE_BRIGHT_IGNORE
Definition: bebop_ae_awb.c:83
float target_exposure
Target exposure time in ms.
Definition: mt9f002.h:150
struct ae_setting_t ae_set
Definition: bebop_ae_awb.c:151