/* # (C) 2008-2009 Hans de Goede # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser 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 #include #include #include #include #include "libv4lprocessing.h" #include "libv4lprocessing-priv.h" #include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ #include "../libv4lsyscall-priv.h" static int autogain_active(struct v4lprocessing_data *data) { return v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN); } /* auto gain and exposure algorithm based on the knee algorithm described here: http://ytse.tricolour.net/docs/LowLightOptimization.html */ static int autogain_calculate_lookup_tables( struct v4lprocessing_data *data, unsigned char *buf, const struct v4l2_format *fmt) { int x, y, target, steps, avg_lum = 0; int gain, exposure, orig_gain, orig_exposure; struct v4l2_control ctrl; struct v4l2_queryctrl gainctrl, expoctrl; const int deadzone = 8; ctrl.id = V4L2_CID_EXPOSURE; expoctrl.id = V4L2_CID_EXPOSURE; if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &expoctrl) || SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) return 0; exposure = orig_exposure = ctrl.value; ctrl.id = V4L2_CID_GAIN; gainctrl.id = V4L2_CID_GAIN; if (SYS_IOCTL(data->fd, VIDIOC_QUERYCTRL, &gainctrl) || SYS_IOCTL(data->fd, VIDIOC_G_CTRL, &ctrl)) return 0; gain = orig_gain = ctrl.value; switch (fmt->fmt.pix.pixelformat) { case V4L2_PIX_FMT_SGBRG8: case V4L2_PIX_FMT_SGRBG8: case V4L2_PIX_FMT_SBGGR8: case V4L2_PIX_FMT_SRGGB8: buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + fmt->fmt.pix.width / 4; for (y = 0; y < fmt->fmt.pix.height / 2; y++) { for (x = 0; x < fmt->fmt.pix.width / 2; x++) { avg_lum += *buf++; } buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width / 2; } avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width / 4; break; case V4L2_PIX_FMT_RGB24: case V4L2_PIX_FMT_BGR24: buf += fmt->fmt.pix.height * fmt->fmt.pix.bytesperline / 4 + fmt->fmt.pix.width * 3 / 4; for (y = 0; y < fmt->fmt.pix.height / 2; y++) { for (x = 0; x < fmt->fmt.pix.width / 2; x++) { avg_lum += *buf++; avg_lum += *buf++; avg_lum += *buf++; } buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3 / 2; } avg_lum /= fmt->fmt.pix.height * fmt->fmt.pix.width * 3 / 4; break; } /* If we are off a multiple of deadzone, do multiple steps to reach the desired lumination fast (with the risc of a slight overshoot) */ target = v4lcontrol_get_ctrl(data->control, V4LCONTROL_AUTOGAIN_TARGET); steps = abs(target - avg_lum) / deadzone; for (x = 0; x < steps; x++) { if (avg_lum > target) { if (exposure > expoctrl.default_value) exposure--; else if (gain > gainctrl.default_value) gain--; else if (exposure > expoctrl.minimum) exposure--; else if (gain > gainctrl.minimum) gain--; else break; } else { if (gain < gainctrl.default_value) gain++; else if (exposure < expoctrl.default_value) exposure++; else if (gain < gainctrl.maximum) gain++; else if (exposure < expoctrl.maximum) exposure++; else break; } } if (gain != orig_gain) { ctrl.id = V4L2_CID_GAIN; ctrl.value = gain; SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); } if (exposure != orig_exposure) { ctrl.id = V4L2_CID_EXPOSURE; ctrl.value = exposure; SYS_IOCTL(data->fd, VIDIOC_S_CTRL, &ctrl); } return 0; } struct v4lprocessing_filter autogain_filter = { autogain_active, autogain_calculate_lookup_tables };