Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
autogain_functions.c
Go to the documentation of this file.
1 /*
2  * Functions for auto gain.
3  *
4  * Copyright (C) 2010-2012 Hans de Goede <[email protected]>
5  *
6  * This program 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 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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 this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19  */
20 #include "gspca.h"
21 
22 /* auto gain and exposure algorithm based on the knee algorithm described here:
23  http://ytse.tricolour.net/docs/LowLightOptimization.html
24 
25  Returns 0 if no changes were made, 1 if the gain and or exposure settings
26  where changed. */
28  struct gspca_dev *gspca_dev,
29  int avg_lum,
30  int desired_avg_lum,
31  int deadzone,
32  int gain_knee,
33  int exposure_knee)
34 {
35  s32 gain, orig_gain, exposure, orig_exposure;
36  int i, steps, retval = 0;
37 
38  if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
39  return 0;
40 
41  orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
42  orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
43 
44  /* If we are of a multiple of deadzone, do multiple steps to reach the
45  desired lumination fast (with the risc of a slight overshoot) */
46  steps = abs(desired_avg_lum - avg_lum) / deadzone;
47 
48  PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
49  avg_lum, desired_avg_lum, steps);
50 
51  for (i = 0; i < steps; i++) {
52  if (avg_lum > desired_avg_lum) {
53  if (gain > gain_knee)
54  gain--;
55  else if (exposure > exposure_knee)
56  exposure--;
57  else if (gain > gspca_dev->gain->default_value)
58  gain--;
59  else if (exposure > gspca_dev->exposure->minimum)
60  exposure--;
61  else if (gain > gspca_dev->gain->minimum)
62  gain--;
63  else
64  break;
65  } else {
66  if (gain < gspca_dev->gain->default_value)
67  gain++;
68  else if (exposure < exposure_knee)
69  exposure++;
70  else if (gain < gain_knee)
71  gain++;
72  else if (exposure < gspca_dev->exposure->maximum)
73  exposure++;
74  else if (gain < gspca_dev->gain->maximum)
75  gain++;
76  else
77  break;
78  }
79  }
80 
81  if (gain != orig_gain) {
82  v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
83  retval = 1;
84  }
85  if (exposure != orig_exposure) {
86  v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
87  retval = 1;
88  }
89 
90  if (retval)
91  PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
92  gain, exposure);
93  return retval;
94 }
96 
97 /* Autogain + exposure algorithm for cameras with a coarse exposure control
98  (usually this means we can only control the clockdiv to change exposure)
99  As changing the clockdiv so that the fps drops from 30 to 15 fps for
100  example, will lead to a huge exposure change (it effectively doubles),
101  this algorithm normally tries to only adjust the gain (between 40 and
102  80 %) and if that does not help, only then changes exposure. This leads
103  to a much more stable image then using the knee algorithm which at
104  certain points of the knee graph will only try to adjust exposure,
105  which leads to oscilating as one exposure step is huge.
106 
107  Returns 0 if no changes were made, 1 if the gain and or exposure settings
108  where changed. */
110  struct gspca_dev *gspca_dev,
111  int avg_lum,
112  int desired_avg_lum,
113  int deadzone)
114 {
115  s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure;
116  int steps, retval = 0;
117 
118  if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0)
119  return 0;
120 
121  orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain);
122  orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure);
123 
124  gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
125  5 * 2 + gspca_dev->gain->minimum;
126  gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) /
127  5 * 4 + gspca_dev->gain->minimum;
128 
129  /* If we are of a multiple of deadzone, do multiple steps to reach the
130  desired lumination fast (with the risc of a slight overshoot) */
131  steps = (desired_avg_lum - avg_lum) / deadzone;
132 
133  PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
134  avg_lum, desired_avg_lum, steps);
135 
136  if ((gain + steps) > gain_high &&
137  exposure < gspca_dev->exposure->maximum) {
138  gain = gain_high;
139  gspca_dev->exp_too_low_cnt++;
140  gspca_dev->exp_too_high_cnt = 0;
141  } else if ((gain + steps) < gain_low &&
142  exposure > gspca_dev->exposure->minimum) {
143  gain = gain_low;
144  gspca_dev->exp_too_high_cnt++;
145  gspca_dev->exp_too_low_cnt = 0;
146  } else {
147  gain += steps;
148  if (gain > gspca_dev->gain->maximum)
149  gain = gspca_dev->gain->maximum;
150  else if (gain < gspca_dev->gain->minimum)
151  gain = gspca_dev->gain->minimum;
152  gspca_dev->exp_too_high_cnt = 0;
153  gspca_dev->exp_too_low_cnt = 0;
154  }
155 
156  if (gspca_dev->exp_too_high_cnt > 3) {
157  exposure--;
158  gspca_dev->exp_too_high_cnt = 0;
159  } else if (gspca_dev->exp_too_low_cnt > 3) {
160  exposure++;
161  gspca_dev->exp_too_low_cnt = 0;
162  }
163 
164  if (gain != orig_gain) {
165  v4l2_ctrl_s_ctrl(gspca_dev->gain, gain);
166  retval = 1;
167  }
168  if (exposure != orig_exposure) {
169  v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure);
170  retval = 1;
171  }
172 
173  if (retval)
174  PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
175  gain, exposure);
176  return retval;
177 }