123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- /*
- * Functions for auto gain.
- *
- * Copyright (C) 2010-2012 Hans de Goede <hdegoede@redhat.com>
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include "gspca.h"
- /* auto gain and exposure algorithm based on the knee algorithm described here:
- http://ytse.tricolour.net/docs/LowLightOptimization.html
- Returns 0 if no changes were made, 1 if the gain and or exposure settings
- where changed. */
- int gspca_expo_autogain(
- struct gspca_dev *gspca_dev,
- int avg_lum,
- int desired_avg_lum,
- int deadzone,
- int gain_knee,
- int exposure_knee)
- {
- s32 gain, orig_gain, exposure, orig_exposure;
- int i, steps, retval = 0;
- if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
- return 0;
- orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
- orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
- /* If we are of a multiple of deadzone, do multiple steps to reach the
- desired lumination fast (with the risc of a slight overshoot) */
- steps = abs(desired_avg_lum - avg_lum) / deadzone;
- PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
- avg_lum, desired_avg_lum, steps);
- for (i = 0; i < steps; i++) {
- if (avg_lum > desired_avg_lum) {
- if (gain > gain_knee)
- gain--;
- else if (exposure > exposure_knee)
- exposure--;
- else if (gain > gspca_dev->gain->default_value)
- gain--;
- else if (exposure > gspca_dev->exposure->minimum)
- exposure--;
- else if (gain > gspca_dev->gain->minimum)
- gain--;
- else
- break;
- } else {
- if (gain < gspca_dev->gain->default_value)
- gain++;
- else if (exposure < exposure_knee)
- exposure++;
- else if (gain < gain_knee)
- gain++;
- else if (exposure < gspca_dev->exposure->maximum)
- exposure++;
- else if (gain < gspca_dev->gain->maximum)
- gain++;
- else
- break;
- }
- }
- if (gain != orig_gain) {
- v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
- retval = 1;
- }
- if (exposure != orig_exposure) {
- v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
- retval = 1;
- }
- if (retval)
- PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
- gain, exposure);
- return retval;
- }
- EXPORT_SYMBOL(gspca_expo_autogain);
- /* Autogain + exposure algorithm for cameras with a coarse exposure control
- (usually this means we can only control the clockdiv to change exposure)
- As changing the clockdiv so that the fps drops from 30 to 15 fps for
- example, will lead to a huge exposure change (it effectively doubles),
- this algorithm normally tries to only adjust the gain (between 40 and
- 80 %) and if that does not help, only then changes exposure. This leads
- to a much more stable image then using the knee algorithm which at
- certain points of the knee graph will only try to adjust exposure,
- which leads to oscilating as one exposure step is huge.
- Returns 0 if no changes were made, 1 if the gain and or exposure settings
- where changed. */
- int gspca_coarse_grained_expo_autogain(
- struct gspca_dev *gspca_dev,
- int avg_lum,
- int desired_avg_lum,
- int deadzone)
- {
- s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure;
- int steps, retval = 0;
- if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
- return 0;
- orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
- orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
- gain_low = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
- 5 * 2 + gspca_dev->gain->minimum;
- gain_high = (s32)(gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
- 5 * 4 + gspca_dev->gain->minimum;
- /* If we are of a multiple of deadzone, do multiple steps to reach the
- desired lumination fast (with the risc of a slight overshoot) */
- steps = (desired_avg_lum - avg_lum) / deadzone;
- PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
- avg_lum, desired_avg_lum, steps);
- if ((gain + steps) > gain_high &&
- exposure < gspca_dev->exposure->maximum) {
- gain = gain_high;
- gspca_dev->exp_too_low_cnt++;
- gspca_dev->exp_too_high_cnt = 0;
- } else if ((gain + steps) < gain_low &&
- exposure > gspca_dev->exposure->minimum) {
- gain = gain_low;
- gspca_dev->exp_too_high_cnt++;
- gspca_dev->exp_too_low_cnt = 0;
- } else {
- gain += steps;
- if (gain > gspca_dev->gain->maximum)
- gain = gspca_dev->gain->maximum;
- else if (gain < gspca_dev->gain->minimum)
- gain = gspca_dev->gain->minimum;
- gspca_dev->exp_too_high_cnt = 0;
- gspca_dev->exp_too_low_cnt = 0;
- }
- if (gspca_dev->exp_too_high_cnt > 3) {
- exposure--;
- gspca_dev->exp_too_high_cnt = 0;
- } else if (gspca_dev->exp_too_low_cnt > 3) {
- exposure++;
- gspca_dev->exp_too_low_cnt = 0;
- }
- if (gain != orig_gain) {
- v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
- retval = 1;
- }
- if (exposure != orig_exposure) {
- v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
- retval = 1;
- }
- if (retval)
- PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
- gain, exposure);
- return retval;
- }
- EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain);
|