/**
* @file white_balance.c
* @brief white balance algorithm
* @author Patrick Roth - roth@stettbacher.ch
* @copyright Stettbacher Signal Processing AG
*
* @remarks
*
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*
*/
#include
#include
#include
#include "color_pipe_private.h"
#include "color.h"
/**
* Minimum color gain
*/
#define GAIN_MIN 0.1
/**
* Maximum color gain
*/
#define GAIN_MAX 10.0
/**
* Calculate the average value of U and V component on given YUV image.
*
* @param img_yuv YUV image buffer
* @param height image height in number of pixels
* @param width image width in number of pixels
* @param threshold gray value threshold
* @param u_avg On return: average value from all gray pixels (U-component)
* @param v_avg On return: average value from all gray pixels (V-component)
* @return -1 if no gray pixel is available otherwise 0
*/
static int get_u_v_avg(const int16_t *img_yuv, const int height, const int width, const float threshold, float *u_avg, float *v_avg) {
int index, num_gray_pixel, y, x;
int sum_u, sum_v;
int pixel_y, pixel_u, pixel_v, color_val;
const int shift_fact = 10;
const int thresh = threshold * ((float)(1 << shift_fact));
/*
* Accumulate all gray pixels below given threshold
*/
index = 0;
num_gray_pixel = 0;
sum_u = 0;
sum_v = 0;
for(y = 0; y < height; y++) {
for(x = 0; x < width; x++) {
pixel_y = img_yuv[index];
if(pixel_y == 0) {
index += 3;
continue;
}
pixel_u = img_yuv[index+1];
pixel_v = img_yuv[index+2];
color_val = ((abs(pixel_u) + abs(pixel_v)) << shift_fact)/pixel_y;
if(color_val < thresh) {
// it's a gray pixel
num_gray_pixel++;
sum_u += pixel_u;
sum_v += pixel_v;
}
index += 3;
}
}
// check whether if there's any gray pixel
if(num_gray_pixel == 0) {
// no gray pixel --> nothing to do
return -1;
}
// calculate avarage of U and V component over all gray pixels
*u_avg = ((float)sum_u)/num_gray_pixel;
*v_avg = ((float)sum_v)/num_gray_pixel;
// printf("XXX num_gray_pixel = %d, u_avg = %f, v_avg = %f\n", num_gray_pixel, *u_avg, *v_avg);
return 0;
}
/**
* Apply red and blue pixel gain on input image.
*
* @param img_out On return: Gain is applied on this image
* @param img_in input RGB image
* @param height image height in number of pixels
* @param width image width in number of pixels
* @param bit_channel bits per color channel
* @param gain_blue blue gain to apply
* @param gain_red red gain to apply
*/
static void apply_pixel_gain_rgb(void *img_out, const void *img_in, const int height, const int width,
const int bit_channel, const float gain_blue, const float gain_red) {
int index, x, y, pixel_red, pixel_blue;
const int shift_fact = 8;
const int blue = gain_blue * ((float)(1 << shift_fact));
const int red = gain_red * ((float)(1 << shift_fact));
const uint8_t *img8_in = img_in;
const uint16_t *img16_in = img_in;
uint8_t *img8_out = img_out;
uint16_t *img16_out = img_out;
const int max_pix = (1 << bit_channel) - 1;
index = 0;
pixel_red = 0;
pixel_blue = 0;
for(y = 0; y < height; y++) {
for(x = 0; x < width; x++) {
// apply gain on red pixel
if(bit_channel <= 8) {
pixel_red = (img8_in[index]*red) >> shift_fact;
}
else if(bit_channel <= 16) {
pixel_red = (img16_in[index]*red) >> shift_fact;
}
if(pixel_red > max_pix) {
pixel_red = max_pix;
}
else if (pixel_red < 0) {
pixel_red = 0;
}
if(bit_channel <= 8) {
img8_out[index] = pixel_red;
}
else if(bit_channel <= 16) {
img16_out[index] = pixel_red;
}
index++;
// no gain is applied on green pixel
if(bit_channel <= 8) {
img8_out[index] = img8_in[index];
}
else if(bit_channel <= 16) {
img16_out[index] = img16_in[index];
}
index++;
// apply gain on blue pixel
if(bit_channel <= 8) {
pixel_blue = (img8_in[index]*blue) >> shift_fact;
}
else if(bit_channel <= 16) {
pixel_blue = (img16_in[index]*blue) >> shift_fact;
}
if(pixel_blue > max_pix) {
pixel_blue = max_pix;
}
else if (pixel_blue < 0) {
pixel_blue = 0;
}
if(bit_channel <= 8) {
img8_out[index] = pixel_blue;
}
else if(bit_channel <= 16) {
img16_out[index] = pixel_blue;
}
index++;
}
}
}
/**
* Local white balance algorithm.
*
* Reference:
* J.-y Huo et al, "Robust Automatic White Balance Algorithm using for Gray Color Points in Images", IEEE Xplore, 2006
*
* @param awb_data white balancing algorithm data
* @return 0 on success otherwise -1
*/
int white_balance(struct awb_data_t *awb_data) {
float u_avg, v_avg;
float ctrl_k, gray_threshold;
int bit_channel, height, width;
void *img_balanced, *img_in;
int16_t *img_yuv;
int uv_div;
// following parameter may be scaled
ctrl_k = awb_data->ctrl_k;
gray_threshold = awb_data->gray_threshold;
bit_channel = awb_data->bit_channel;
img_balanced = awb_data->img_rgb_balanced;
img_in = awb_data->img_in;
img_yuv = awb_data->img_yuv;
height = awb_data->height;
width = awb_data->width;
// Apply red and blue gain on input image.
apply_pixel_gain_rgb(img_balanced, img_in, height, width, bit_channel, awb_data->gain_blue, awb_data->gain_red);
// RGB to YUV color space conversion
if(color_rgb_to_yuv(img_yuv, img_balanced, height, width, bit_channel)) {
// conversion failed --> do abort
return -1;
}
if(get_u_v_avg(img_yuv, height, width, gray_threshold, &u_avg, &v_avg)) {
// no gray pixel --> nothing to do
return 0;
}
/*
* Norm U/V channel average value to 8 bit per color channel image.
*
*/
uv_div = 1 << (bit_channel - 8);
u_avg /= uv_div;
v_avg /= uv_div;
// adjust blue gain if image is bluish
if(fabs(u_avg) > 0.4) {
awb_data->gain_blue -= ctrl_k*u_avg;
}
// adjust red gain if image is reddish
if(fabs(v_avg) > 0.4) {
awb_data->gain_red -= ctrl_k*v_avg;
}
// range check of blue red
if(awb_data->gain_blue < GAIN_MIN) {
awb_data->gain_blue = GAIN_MIN;
}
else if(awb_data->gain_blue > GAIN_MAX) {
awb_data->gain_blue = 3.0;
}
// range check of red gain
if(awb_data->gain_red < GAIN_MIN) {
awb_data->gain_red = GAIN_MIN;
}
else if(awb_data->gain_red > GAIN_MAX) {
awb_data->gain_red = 3.0;
}
// printf("XXX u_avg = %.4f, v_avg = %.4f, red = %.4f, blue = %.4f, bit_channel = %d\n", u_avg, v_avg, awb_data->gain_red, awb_data->gain_blue, bit_channel);
return 0;
}