/* * OMAP3 image quality tuning * * Copyright (C) 2010-2012 Ideas on board SPRL * * Contact: Laurent Pinchart * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program 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 General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include "isp/omap3isp.h" #include "isp/stats.h" #include "isp/tools.h" #include "iq.h" struct iq_tuning { struct omap3_isp_device *isp; unsigned int frame_count; unsigned int pix_max; struct iq_params params; }; void iq_aewb_process(struct iq_tuning *iq, const struct omap3_isp_aewb_stats *stats) { struct iq_params *iqp = &iq->params; unsigned int i; float gains[4]; double factor; double gain; double mean; /* Automatic White Balance */ mean = stats->accum[0] + stats->accum[3] + (stats->accum[1] + stats->accum[2]) / 2; mean /= 3; for (i = 0; i < 4; ++i) gains[i] = mean / stats->accum[i]; omap3_isp_preview_set_white_balance(iq->isp, gains); /* Automatic Exposure * * Skip ae_delay frames at stream startup, and only run AE every * ae_interval frames to take sensor exposure internal delays into * account. */ iq->frame_count++; if (iq->frame_count <= iqp->ae_delay) return; if ((iq->frame_count - iqp->ae_delay - 1) % iqp->ae_interval) return; mean = stats->accum[0] + stats->accum[1] + stats->accum[2] + stats->accum[3]; mean /= 4 * stats->npix; factor = (iq->pix_max * iqp->mean_level) / mean; gain = factor * iqp->exposure * iqp->gain / iqp->gain_min; iqp->exposure = clamp((unsigned int)gain, iqp->exposure_min, iqp->exposure_max); gain = gain / iqp->exposure * iqp->gain_min; iqp->gain = clamp((unsigned int)gain, iqp->gain_min, iqp->gain_max); printf("AE: factor %.4f exposure %u sensor gain %u\n", factor, iqp->exposure, iqp->gain); omap3_isp_sensor_set_exposure(iq->isp, iqp->exposure); omap3_isp_sensor_set_gain(iq->isp, iqp->gain); } /** * iq_params_init - Initialize IQ parameters * @params: IQ parameters * * Fill the passed params structure with the default IQ parameters. */ void iq_params_init(struct iq_params *params) { params->window.left = 0.0; params->window.top = 0.0; params->window.width = 1.0; params->window.height = 1.0; params->ae_delay = 1; params->ae_interval = 2; params->mean_level = 0.15; params->exposure = 1000; params->exposure_min = 10; params->exposure_max = 2000; params->gain = 8; params->gain_min = 8; params->gain_max = 1024; params->black_level = 64; params->saturation = 1.0; } /** * iq_params_parse - Parse an IQ parameter string * @params: IQ parameters * @arg: Parameter string * * Parse the given parameter string and store the parameter value in the params * structure. The parameter string must have the form * * name=value * * Return 0 on success or a negative error code otherwise: * * -EINVAL: The string format or the parameter name is invalid * -ERANGE: The parameter value is out of range for the given parameter */ int iq_params_parse(struct iq_params *params, const char *arg) { unsigned int *val_uint = NULL; float *val_float = NULL; const char *value; unsigned int size; char *endp; value = strchr(arg, '='); if (value == NULL) return -EINVAL; size = value - arg; value++; if (strncmp(arg, "ae-delay", size) == 0) val_uint = ¶ms->ae_delay; else if (strncmp(arg, "ae-interval", size) == 0) val_uint = ¶ms->ae_interval; else if (strncmp(arg, "exposure-def", size) == 0) val_uint = ¶ms->exposure; else if (strncmp(arg, "exposure-min", size) == 0) val_uint = ¶ms->exposure_min; else if (strncmp(arg, "exposure-max", size) == 0) val_uint = ¶ms->exposure_max; else if (strncmp(arg, "gain-def", size) == 0) val_uint = ¶ms->gain; else if (strncmp(arg, "gain-min", size) == 0) val_uint = ¶ms->gain_min; else if (strncmp(arg, "gain-max", size) == 0) val_uint = ¶ms->gain_max; else if (strncmp(arg, "mean-level", size) == 0) val_float = ¶ms->mean_level; else if (strncmp(arg, "window-left", size) == 0) val_float = ¶ms->window.left; else if (strncmp(arg, "window-top", size) == 0) val_float = ¶ms->window.top; else if (strncmp(arg, "window-width", size) == 0) val_float = ¶ms->window.width; else if (strncmp(arg, "window-height", size) == 0) val_float = ¶ms->window.height; else return -EINVAL; if (val_uint != NULL) { *val_uint = strtoul(value, &endp, 10); if (*endp != '\0') return -EINVAL; } else { *val_float = strtof(value, &endp); if (*endp != '\0') return -EINVAL; if (*val_float < 0 || *val_float > 1) return -ERANGE; } return 0; } struct iq_tuning *iq_init(struct omap3_isp_device *isp, const struct iq_params *params) { struct v4l2_mbus_framefmt format; struct v4l2_rect window; struct iq_tuning *iq; iq = malloc(sizeof *iq); if (iq == NULL) return NULL; iq->isp = isp; iq->frame_count = 0; iq->pix_max = (1 << 10) - 1; iq->params = *params; omap3_isp_sensor_set_gain(isp, iq->params.gain); omap3_isp_sensor_set_exposure(isp, iq->params.exposure); omap3_isp_ccdc_set_black_level(isp, iq->params.black_level); omap3_isp_preview_set_saturation(isp, iq->params.saturation); omap3_isp_stats_get_format(isp, &format); window.left = params->window.left * format.width; window.top = params->window.top * format.height; window.width = params->window.width * format.width; window.height = params->window.height * format.height; omap3_isp_aewb_configure(isp, &window, iq->pix_max - iq->params.black_level - 1); omap3_isp_stats_enable(isp, true); return iq; } void iq_cleanup(struct iq_tuning *iq) { free(iq); }