2
# (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU Lesser General Public License as published by
6
# the Free Software Foundation; either version 2.1 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU Lesser General Public License for more details.
14
# You should have received a copy of the GNU Lesser General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25
#include "libv4lprocessing.h"
26
#include "libv4lprocessing-priv.h"
27
#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
28
#include "../libv4lsyscall-priv.h"
30
static int autogain_active(struct v4lprocessing_data *data) {
33
autogain = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN);
35
/* Reset last_correction val */
36
data->last_gain_correction = 0;
42
/* Adjust ctrl value with steps steps, while not crossing limit */
43
static void autogain_adjust(struct v4l2_queryctrl *ctrl, int *value,
46
int ctrl_range = (ctrl->maximum - ctrl->minimum) / ctrl->step;
48
/* If we are of 3 * deadzone or more, and we have a very fine grained
49
control, take larger steps, otherwise we take ages to get to the
50
right setting point. We use 256 as tripping point for determineing fine
51
grained controls here, as avg_lum has a range of 0 - 255. */
52
if (abs(steps) >= 3 && ctrl_range > 256)
53
*value += steps * ctrl->step * (ctrl_range / 256);
55
*value += steps * ctrl->step;
66
/* auto gain and exposure algorithm based on the knee algorithm described here:
67
http://ytse.tricolour.net/docs/LowLightOptimization.html */
68
static int autogain_calculate_lookup_tables(
69
struct v4lprocessing_data *data,
70
unsigned char *buf, const struct v4l2_format *fmt)
72
int x, y, target, steps, avg_lum = 0;
73
int gain, exposure, orig_gain, orig_exposure, exposure_low;
74
struct v4l2_control ctrl;
75
struct v4l2_queryctrl gainctrl, expoctrl;
76
const int deadzone = 6;
78
ctrl.id = V4L2_CID_EXPOSURE;
79
expoctrl.id = V4L2_CID_EXPOSURE;
80
if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &expoctrl) ||
81
SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl))
83
exposure = orig_exposure = ctrl.value;
84
/* Determine a value below which we try to not lower the exposure,
85
as most exposure controls tend to jump with big steps in the low
86
range, causing oscilation, so we prefer to use gain when exposure
88
exposure_low = expoctrl.maximum / 10;
89
/* If we have a fine grained exposure control only avoid the last 10 steps */
90
steps = exposure_low / expoctrl.step;
93
exposure_low = steps * expoctrl.step + expoctrl.minimum;
95
ctrl.id = V4L2_CID_GAIN;
96
gainctrl.id = V4L2_CID_GAIN;
97
if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &gainctrl) ||
98
SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl))
100
gain = orig_gain = ctrl.value;
102
switch (fmt->fmt.pix.pixelformat) {
103
case V4L2_PIX_FMT_SGBRG8:
104
case V4L2_PIX_FMT_SGRBG8:
105
case V4L2_PIX_FMT_SBGGR8:
106
case V4L2_PIX_FMT_SRGGB8:
107
buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 +
108
fmt->fmt.pix.width / 4;
110
for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
111
for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
114
buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width / 2;
116
avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width / 4;
119
case V4L2_PIX_FMT_RGB24:
120
case V4L2_PIX_FMT_BGR24:
121
buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 +
122
fmt->fmt.pix.width * 3 / 4;
124
for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
125
for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
130
buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3 / 2;
132
avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width * 3 / 4;
136
/* If we are off a multiple of deadzone, do multiple steps to reach the
137
desired lumination fast (with the risc of a slight overshoot) */
138
target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET);
139
steps = (target - avg_lum) / deadzone;
141
/* If we were decreasing and are now increasing, or vica versa, half the
142
number of steps to avoid overshooting and oscilating */
143
if ((steps > 0 && data->last_gain_correction < 0) ||
144
(steps < 0 && data->last_gain_correction > 0))
148
return 0; /* Nothing to do */
151
if (exposure > expoctrl.default_value)
152
autogain_adjust(&expoctrl, &exposure, steps, expoctrl.default_value);
153
else if (gain > gainctrl.default_value)
154
autogain_adjust(&gainctrl, &gain, steps, gainctrl.default_value);
155
else if (exposure > exposure_low)
156
autogain_adjust(&expoctrl, &exposure, steps, exposure_low);
157
else if (gain > gainctrl.minimum)
158
autogain_adjust(&gainctrl, &gain, steps, gainctrl.minimum);
159
else if (exposure > expoctrl.minimum)
160
autogain_adjust(&expoctrl, &exposure, steps, expoctrl.minimum);
164
if (exposure < exposure_low)
165
autogain_adjust(&expoctrl, &exposure, steps, exposure_low);
166
else if (gain < gainctrl.default_value)
167
autogain_adjust(&gainctrl, &gain, steps, gainctrl.default_value);
168
else if (exposure < expoctrl.default_value)
169
autogain_adjust(&expoctrl, &exposure, steps, expoctrl.default_value);
170
else if (gain < gainctrl.maximum)
171
autogain_adjust(&gainctrl, &gain, steps, gainctrl.maximum);
172
else if (exposure < expoctrl.maximum)
173
autogain_adjust(&expoctrl, &exposure, steps, expoctrl.maximum);
179
data->last_gain_correction = steps;
180
/* We are still settling down, force the next update sooner. Note we
181
skip the next frame as that is still captured with the old settings,
182
and another one just to be sure (because if we re-adjust based
183
on the old settings we might overshoot). */
184
data->lookup_table_update_counter = V4L2PROCESSING_UPDATE_RATE - 2;
187
if (gain != orig_gain) {
188
ctrl.id = V4L2_CID_GAIN;
190
SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl);
192
if (exposure != orig_exposure) {
193
ctrl.id = V4L2_CID_EXPOSURE;
194
ctrl.value = exposure;
195
SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl);
201
struct v4lprocessing_filter autogain_filter = {
202
autogain_active, autogain_calculate_lookup_tables };