diff options
Diffstat (limited to 'v4l2-apps/libv4l/libv4lconvert')
8 files changed, 196 insertions, 8 deletions
diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile index 49d508694..f4f529c0d 100644 --- a/v4l2-apps/libv4l/libv4lconvert/Makefile +++ b/v4l2-apps/libv4l/libv4lconvert/Makefile @@ -16,7 +16,7 @@ CONVERT_OBJS = libv4lconvert.o tinyjpeg.o sn9c10x.o sn9c20x.o pac207.o \ mr97310a.o flip.o crop.o jidctflt.o spca561-decompress.o \ rgbyuv.o spca501.o sq905c.o bayer.o hm12.o \ control/libv4lcontrol.o processing/libv4lprocessing.o \ - processing/whitebalance.o + processing/whitebalance.o processing/autogain.o TARGETS = $(CONVERT_LIB) libv4lconvert.pc INCLUDES = ../include/libv4lconvert.h diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c index 4d227c366..edf86483d 100644 --- a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c @@ -67,7 +67,7 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { /* Asus N50Vn laptop */ { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vn ", V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, -/* Second: devices which should use sw whitebalance by default */ +/* Second: devices which should use some software processing by default */ /* Pac207 based devices */ { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, { 0x093a, 0x2460, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB }, @@ -78,6 +78,9 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { V4LCONTROL_ROTATED_90_JPEG|V4LCONTROL_WANTS_WB }, /* sq905 devices */ { 0x2770, 0x9120, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + /* spca561 revison 12a devices */ + { 0x041e, 0x403b, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, + { 0x046d, 0x0928, 7, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN }, }; static const struct v4l2_queryctrl fake_controls[]; @@ -223,13 +226,17 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) /* If the device always needs conversion, we can add fake controls at no cost (no cost when not activated by the user that is) */ if (always_needs_conversion || v4lcontrol_needs_conversion(data)) { - for (i = 0; i < V4LCONTROL_COUNT; i++) { + for (i = 0; i < V4LCONTROL_AUTO_ENABLE_COUNT; i++) { ctrl.id = fake_controls[i].id; if (syscall(SYS_ioctl, data->fd, VIDIOC_QUERYCTRL, &ctrl) == -1) data->controls |= 1 << i; } } + if (data->flags & V4LCONTROL_WANTS_AUTOGAIN) + data->controls |= 1 << V4LCONTROL_AUTOGAIN | + 1 << V4LCONTROL_AUTOGAIN_TARGET; + /* Allow overriding through environment */ if ((s = getenv("LIBV4LCONTROL_CONTROLS"))) data->controls = strtol(s, NULL, 0); @@ -266,6 +273,10 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) if (init) { /* Initialize the new shm object we created */ memset(data->shm_values, 0, sizeof(V4LCONTROL_SHM_SIZE)); + + for (i = 0; i < V4LCONTROL_COUNT; i++) + data->shm_values[i] = fake_controls[i].default_value; + if (data->flags & V4LCONTROL_WANTS_WB) data->shm_values[V4LCONTROL_WHITEBALANCE] = 1; } @@ -316,6 +327,27 @@ static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = { .default_value = 0, .flags = 0 }, +{}, /* Dummy place holder for V4LCONTROL_AUTO_ENABLE_COUNT */ +{ + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain (software)", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + .flags = 0 +}, +{ + .id = V4L2_CTRL_CLASS_USER + 0x2000, /* FIXME */ + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Auto Gain target", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 100, + .flags = 0 +}, }; int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg) diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h index 85129ee4c..a3640038b 100644 --- a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h @@ -12,7 +12,7 @@ # 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. +# 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 @@ -27,13 +27,22 @@ #define V4LCONTROL_VFLIPPED 0x02 #define V4LCONTROL_ROTATED_90_JPEG 0x04 #define V4LCONTROL_WANTS_WB 0x08 +#define V4LCONTROL_WANTS_AUTOGAIN 0x10 + +/* Masks */ +#define V4LCONTROL_WANTS_WB_AUTOGAIN (V4LCONTROL_WANTS_WB | V4LCONTROL_WANTS_AUTOGAIN) /* Controls */ enum { V4LCONTROL_WHITEBALANCE, V4LCONTROL_HFLIP, V4LCONTROL_VFLIP, - V4LCONTROL_COUNT }; + /* All fake controls above here are auto enabled when not present in hw */ + V4LCONTROL_AUTO_ENABLE_COUNT, + V4LCONTROL_AUTOGAIN, + V4LCONTROL_AUTOGAIN_TARGET, + V4LCONTROL_COUNT + }; struct v4lcontrol_data; diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c index afb0c854c..58d77e5ef 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c @@ -127,6 +127,9 @@ struct v4lconvert_data *v4lconvert_create(int fd) data->flags |= V4LCONVERT_IS_UVC; else if (!strcmp((char *)cap.driver, "sn9c20x")) data->flags |= V4LCONVERT_IS_SN9C20X; + + if ((cap.capabilities & 0xff) & ~V4L2_CAP_VIDEO_CAPTURE) + always_needs_conversion = 0; } data->control = v4lcontrol_create(fd, always_needs_conversion); @@ -136,7 +139,7 @@ struct v4lconvert_data *v4lconvert_create(int fd) } data->control_flags = v4lcontrol_get_flags(data->control); - data->processing = v4lprocessing_create(data->control); + data->processing = v4lprocessing_create(fd, data->control); if (!data->processing) { v4lcontrol_destroy(data->control); free(data); diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c b/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c new file mode 100644 index 000000000..ed4b8224f --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/autogain.c @@ -0,0 +1,140 @@ +/* +# (C) 2008-2009 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 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 <errno.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <syscall.h> +#include <unistd.h> + +#include "libv4lprocessing.h" +#include "libv4lprocessing-priv.h" +#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ + +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 (syscall(SYS_ioctl, data->fd, VIDIOC_QUERYCTRL, &expoctrl) || + syscall(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 (syscall(SYS_ioctl, data->fd, VIDIOC_QUERYCTRL, &gainctrl) || + syscall(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; + syscall(SYS_ioctl, data->fd, VIDIOC_S_CTRL, &ctrl); + } + if (exposure != orig_exposure) { + ctrl.id = V4L2_CID_EXPOSURE; + ctrl.value = exposure; + syscall(SYS_ioctl, data->fd, VIDIOC_S_CTRL, &ctrl); + } + + return 0; +} + +struct v4lprocessing_filter autogain_filter = { + autogain_active, autogain_calculate_lookup_tables }; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h index fb514d7f5..b848317f7 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h @@ -27,6 +27,7 @@ struct v4lprocessing_data { struct v4lcontrol_data *control; + int fd; int do_process; /* True if any of the lookup tables does not contain linear 0-255 */ @@ -49,5 +50,6 @@ struct v4lprocessing_filter { }; extern struct v4lprocessing_filter whitebalance_filter; +extern struct v4lprocessing_filter autogain_filter; #endif diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c index 3a3802aab..01de6c111 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c @@ -30,9 +30,10 @@ static struct v4lprocessing_filter *filters[] = { &whitebalance_filter, + &autogain_filter, }; -struct v4lprocessing_data *v4lprocessing_create(struct v4lcontrol_data* control) +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data* control) { struct v4lprocessing_data *data = calloc(1, sizeof(struct v4lprocessing_data)); @@ -40,6 +41,7 @@ struct v4lprocessing_data *v4lprocessing_create(struct v4lcontrol_data* control) if (!data) return NULL; + data->fd = fd; data->control = control; return data; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h index 3c50d9fb9..f1892a279 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.h @@ -32,7 +32,7 @@ struct v4lprocessing_data; struct v4lcontrol_data; -struct v4lprocessing_data *v4lprocessing_create(struct v4lcontrol_data *data); +struct v4lprocessing_data *v4lprocessing_create(int fd, struct v4lcontrol_data *data); void v4lprocessing_destroy(struct v4lprocessing_data *data); /* Prepare to process 1 frame, returns 1 if processing is necesary, |