~ubuntu-branches/ubuntu/precise/v4l-utils/precise

« back to all changes in this revision

Viewing changes to lib/libv4lconvert/processing/autogain.c

  • Committer: Bazaar Package Importer
  • Author(s): Gregor Jasny
  • Date: 2010-02-28 19:44:15 UTC
  • Revision ID: james.westby@ubuntu.com-20100228194415-067hdj8rvawj91zw
Tags: upstream-0.7.90
ImportĀ upstreamĀ versionĀ 0.7.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
#             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
 
3
 
 
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.
 
8
#
 
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.
 
13
#
 
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
 
17
*/
 
18
 
 
19
#include <errno.h>
 
20
#include <string.h>
 
21
#include <stdio.h>
 
22
#include <stdlib.h>
 
23
#include <unistd.h>
 
24
 
 
25
#include "libv4lprocessing.h"
 
26
#include "libv4lprocessing-priv.h"
 
27
#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
 
28
#include "../libv4lsyscall-priv.h"
 
29
 
 
30
static int autogain_active(struct v4lprocessing_data *data) {
 
31
  int autogain;
 
32
 
 
33
  autogain = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN);
 
34
  if (!autogain) {
 
35
    /* Reset last_correction val */
 
36
    data->last_gain_correction = 0;
 
37
  }
 
38
 
 
39
  return autogain;
 
40
}
 
41
 
 
42
/* Adjust ctrl value with steps steps, while not crossing limit */
 
43
static void autogain_adjust(struct v4l2_queryctrl *ctrl, int *value,
 
44
  int steps, int limit)
 
45
{
 
46
  int ctrl_range = (ctrl->maximum - ctrl->minimum) / ctrl->step;
 
47
 
 
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);
 
54
  else
 
55
    *value += steps * ctrl->step;
 
56
 
 
57
  if (steps > 0) {
 
58
    if (*value > limit)
 
59
      *value = limit;
 
60
  } else {
 
61
    if (*value < limit)
 
62
      *value = limit;
 
63
  }
 
64
}
 
65
 
 
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)
 
71
{
 
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;
 
77
 
 
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))
 
82
    return 0;
 
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
 
87
     has hit this value */
 
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;
 
91
  if (steps > 10)
 
92
    steps = 10;
 
93
  exposure_low = steps * expoctrl.step + expoctrl.minimum;
 
94
 
 
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))
 
99
    return 0;
 
100
  gain = orig_gain = ctrl.value;
 
101
 
 
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;
 
109
 
 
110
      for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
 
111
        for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
 
112
          avg_lum += *buf++;
 
113
        }
 
114
        buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width / 2;
 
115
      }
 
116
      avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width / 4;
 
117
      break;
 
118
 
 
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;
 
123
 
 
124
      for (y = 0; y < fmt->fmt.pix.height / 2; y++) {
 
125
        for (x = 0; x < fmt->fmt.pix.width / 2; x++) {
 
126
          avg_lum += *buf++;
 
127
          avg_lum += *buf++;
 
128
          avg_lum += *buf++;
 
129
        }
 
130
        buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3 / 2;
 
131
      }
 
132
      avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width * 3 / 4;
 
133
      break;
 
134
  }
 
135
 
 
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;
 
140
 
 
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))
 
145
    steps /= 2;
 
146
 
 
147
  if (steps == 0)
 
148
    return 0; /* Nothing to do */
 
149
 
 
150
  if (steps < 0) {
 
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);
 
161
    else
 
162
      steps = 0;
 
163
  } else {
 
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);
 
174
    else
 
175
      steps = 0;
 
176
  }
 
177
 
 
178
  if (steps) {
 
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;
 
185
  }
 
186
 
 
187
  if (gain != orig_gain) {
 
188
    ctrl.id = V4L2_CID_GAIN;
 
189
    ctrl.value = gain;
 
190
    SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl);
 
191
  }
 
192
  if (exposure != orig_exposure) {
 
193
    ctrl.id = V4L2_CID_EXPOSURE;
 
194
    ctrl.value = exposure;
 
195
    SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl);
 
196
  }
 
197
 
 
198
  return 0;
 
199
}
 
200
 
 
201
struct v4lprocessing_filter autogain_filter = {
 
202
  autogain_active, autogain_calculate_lookup_tables };