diff options
Diffstat (limited to 'v4l2-apps/libv4l/libv4lconvert')
11 files changed, 316 insertions, 624 deletions
diff --git a/v4l2-apps/libv4l/libv4lconvert/Makefile b/v4l2-apps/libv4l/libv4lconvert/Makefile index ffc2e337d..49d508694 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/rgbprocessing.o processing/bayerprocessing.o + processing/whitebalance.o TARGETS = $(CONVERT_LIB) libv4lconvert.pc INCLUDES = ../include/libv4lconvert.h diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h index 0157af280..14e4fe838 100644 --- a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol-priv.h @@ -24,16 +24,12 @@ #define V4LCONTROL_SHM_SIZE 4096 -#define V4LCONTROL_WANTS_WB (1 << V4LCONTROL_WHITEBALANCE) -#define V4LCONTROL_WANTS_NORM ((1 << V4LCONTROL_NORMALIZE) | \ - (1 << V4LCONTROL_NORM_LOW_BOUND) | \ - (1 << V4LCONTROL_NORM_HIGH_BOUND)) - struct v4lcontrol_data { int fd; /* Device fd */ int flags; /* Special flags for this device */ int controls; /* Which controls to use for this device */ unsigned int *shm_values; /* shared memory control value store */ + unsigned int old_values[V4LCONTROL_COUNT]; /* for controls_changed() */ }; struct v4lcontrol_flags_info { @@ -46,7 +42,6 @@ struct v4lcontrol_flags_info { const char *manufacturer; const char *product; */ int flags; - int controls; }; #endif diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c index b120aa2fc..4d227c366 100644 --- a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.c @@ -56,30 +56,32 @@ static const struct v4lcontrol_flags_info v4lcontrol_flags[] = { /* First: Upside down devices */ /* Philips SPC200NC */ - { 0x0471, 0x0325, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0 }, + { 0x0471, 0x0325, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, /* Philips SPC300NC */ - { 0x0471, 0x0326, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0 }, + { 0x0471, 0x0326, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, /* Philips SPC210NC */ - { 0x0471, 0x032d, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0 }, - /* Genius E-M 112 (also needs processing) */ - { 0x093a, 0x2476, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, - V4LCONTROL_WANTS_WB }, + { 0x0471, 0x032d, 0, NULL, NULL, V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, + /* Genius E-M 112 (also want whitebalance by default) */ + { 0x093a, 0x2476, 0, NULL, NULL, + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED|V4LCONTROL_WANTS_WB }, /* Asus N50Vn laptop */ { 0x04f2, 0xb106, 0, "ASUSTeK Computer Inc. ", "N50Vn ", - V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED, 0 }, -/* Second: devices which can benefit from software video processing */ + V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED }, +/* Second: devices which should use sw whitebalance by default */ /* Pac207 based devices */ - { 0x041e, 0x4028, 0, NULL, NULL, 0, V4LCONTROL_WANTS_WB }, - { 0x093a, 0x2460, 0x1f, NULL, NULL, 0, V4LCONTROL_WANTS_WB }, - { 0x145f, 0x013a, 0, NULL, NULL, 0, V4LCONTROL_WANTS_WB }, - { 0x2001, 0xf115, 0, NULL, NULL, 0, V4LCONTROL_WANTS_WB }, + { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + { 0x093a, 0x2460, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB }, + { 0x145f, 0x013a, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, + { 0x2001, 0xf115, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* Pac7302 based devices */ - { 0x093a, 0x2620, 0x0f, NULL, NULL, V4LCONTROL_ROTATED_90_JPEG, - V4LCONTROL_WANTS_WB }, + { 0x093a, 0x2620, 0x0f, NULL, NULL, + V4LCONTROL_ROTATED_90_JPEG|V4LCONTROL_WANTS_WB }, /* sq905 devices */ - { 0x2770, 0x9120, 0, NULL, NULL, 0, V4LCONTROL_WANTS_WB }, + { 0x2770, 0x9120, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, }; +static const struct v4l2_queryctrl fake_controls[]; + static void v4lcontrol_init_flags(struct v4lcontrol_data *data) { struct stat st; @@ -193,7 +195,6 @@ static void v4lcontrol_init_flags(struct v4lcontrol_data *data) (v4lcontrol_flags[i].dmi_board_name == NULL || !strcmp(v4lcontrol_flags[i].dmi_board_name, dmi_board_name))) { data->flags |= v4lcontrol_flags[i].flags; - data->controls = v4lcontrol_flags[i].controls; break; } } @@ -215,21 +216,21 @@ struct v4lcontrol_data *v4lcontrol_create(int fd, int always_needs_conversion) v4lcontrol_init_flags(data); + /* Allow overriding through environment */ + if ((s = getenv("LIBV4LCONTROL_FLAGS"))) + data->flags = strtol(s, NULL, 0); + /* 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)) { - ctrl.id = V4L2_CID_HFLIP; - if (syscall(SYS_ioctl, data->fd, VIDIOC_QUERYCTRL, &ctrl) == -1) - data->controls |= 1 << V4LCONTROL_HFLIP; - ctrl.id = V4L2_CID_VFLIP; - if (syscall(SYS_ioctl, data->fd, VIDIOC_QUERYCTRL, &ctrl) == -1) - data->controls |= 1 << V4LCONTROL_VFLIP; + for (i = 0; i < V4LCONTROL_COUNT; i++) { + ctrl.id = fake_controls[i].id; + if (syscall(SYS_ioctl, data->fd, VIDIOC_QUERYCTRL, &ctrl) == -1) + data->controls |= 1 << i; + } } /* Allow overriding through environment */ - if ((s = getenv("LIBV4LCONTROL_FLAGS"))) - data->flags = strtol(s, NULL, 0); - if ((s = getenv("LIBV4LCONTROL_CONTROLS"))) data->controls = strtol(s, NULL, 0); @@ -265,8 +266,8 @@ 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)); - data->shm_values[V4LCONTROL_WHITEBALANCE] = 1; - data->shm_values[V4LCONTROL_NORM_HIGH_BOUND] = 255; + if (data->flags & V4LCONTROL_WANTS_WB) + data->shm_values[V4LCONTROL_WHITEBALANCE] = 1; } return data; @@ -284,21 +285,11 @@ void v4lcontrol_destroy(struct v4lcontrol_data *data) } /* FIXME get better CID's for normalize */ -struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = { +static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = { { .id = V4L2_CID_AUTO_WHITE_BALANCE, .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Whitebalance", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - .flags = 0 -}, -{ - .id = V4L2_CID_DO_WHITE_BALANCE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Normalize", + .name = "Whitebalance (software)", .minimum = 0, .maximum = 1, .step = 1, @@ -306,26 +297,6 @@ struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = { .flags = 0 }, { - .id = V4L2_CID_BLACK_LEVEL, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Normalize: low bound", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 0, - .flags = 0 -}, -{ - .id = V4L2_CID_WHITENESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Normalize: high bound", - .minimum = 128, - .maximum = 255, - .step = 1, - .default_value = 255, - .flags = 0 -}, -{ .id = V4L2_CID_HFLIP, .type = V4L2_CTRL_TYPE_BOOLEAN, .name = "Horizontal flip", @@ -359,6 +330,11 @@ int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg) if ((data->controls & (1 << i)) && ctrl->id == fake_controls[i].id) { memcpy(ctrl, &fake_controls[i], sizeof(struct v4l2_queryctrl)); + /* Hmm, not pretty */ + if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE && + (data->flags & V4LCONTROL_WANTS_WB)) + ctrl->default_value = 1; + return 0; } @@ -434,6 +410,19 @@ int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl) return 0; } +int v4lcontrol_controls_changed(struct v4lcontrol_data *data) +{ + int res; + + res = memcmp(data->shm_values, data->old_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + memcpy(data->old_values, data->shm_values, + V4LCONTROL_COUNT * sizeof(unsigned int)); + + return res; +} + /* See the comment about this in libv4lconvert.h */ int v4lcontrol_needs_conversion(struct v4lcontrol_data *data) { return data->flags || data->controls; diff --git a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h index 43ef7c49f..85129ee4c 100644 --- a/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h +++ b/v4l2-apps/libv4l/libv4lconvert/control/libv4lcontrol.h @@ -26,13 +26,11 @@ #define V4LCONTROL_HFLIPPED 0x01 #define V4LCONTROL_VFLIPPED 0x02 #define V4LCONTROL_ROTATED_90_JPEG 0x04 +#define V4LCONTROL_WANTS_WB 0x08 /* Controls */ enum { V4LCONTROL_WHITEBALANCE, - V4LCONTROL_NORMALIZE, - V4LCONTROL_NORM_LOW_BOUND, - V4LCONTROL_NORM_HIGH_BOUND, V4LCONTROL_HFLIP, V4LCONTROL_VFLIP, V4LCONTROL_COUNT }; @@ -45,6 +43,9 @@ void v4lcontrol_destroy(struct v4lcontrol_data *data); /* Functions used by v4lprocessing to get the control state */ int v4lcontrol_get_flags(struct v4lcontrol_data *data); int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl); +/* Check if the controls have changed since the last time this function + was called */ +int v4lcontrol_controls_changed(struct v4lcontrol_data *data); /* Check if we must go through the conversion path (and thus alloc conversion buffers, etc. in libv4l2). Note this always return 1 if we *may* need rotate90 / flipping / processing, as if we actually need this may change diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h index 60e62af97..407be3435 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert-priv.h @@ -83,6 +83,7 @@ #define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') #endif +#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) #define V4LCONVERT_ERROR_MSG_SIZE 256 #define V4LCONVERT_MAX_FRAMESIZES 16 diff --git a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c index 94539e376..1a94b53ba 100644 --- a/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c +++ b/v4l2-apps/libv4l/libv4lconvert/libv4lconvert.c @@ -27,7 +27,6 @@ #include "libv4lconvert-priv.h" #define MIN(a,b) (((a)<(b))?(a):(b)) -#define ARRAY_SIZE(x) ((int)sizeof(x)/(int)sizeof((x)[0])) /* Note for proper functioning of v4lconvert_enum_fmt the first entries in supported_src_pixfmts must match with the entries in supported_dst_pixfmts */ @@ -706,6 +705,8 @@ static int v4lconvert_convert_pixfmt(struct v4lconvert_data *data, } /* Do processing on the tmp buffer, because doing it on bayer data is cheaper, and bayer == rgb and our dest_fmt may be yuv */ + tmpfmt.fmt.pix.bytesperline = width; + tmpfmt.fmt.pix.sizeimage = width * height; v4lprocessing_processing(data->processing, tmpbuf, &tmpfmt); /* Deliberate fall through to raw bayer fmt code! */ src_pix_fmt = tmpfmt.fmt.pix.pixelformat; diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/bayerprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/bayerprocessing.c deleted file mode 100644 index f4cc9922b..000000000 --- a/v4l2-apps/libv4l/libv4lconvert/processing/bayerprocessing.c +++ /dev/null @@ -1,244 +0,0 @@ -/* -# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> -# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> -# (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 "libv4lprocessing-priv.h" -#include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ - -void bayer_normalize_analyse(unsigned char *buf, int width, int height, - struct v4lprocessing_data *data) -{ - int value, max = 0, min = 255; - unsigned char *buf_end = buf + width * height; - - while (buf < buf_end) { - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - } - - data->comp1 = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min); - data->offset1 = min; - data->offset2 = data->norm_low_bound; -} - -void bayer_whitebalance_analyse(unsigned char *src_buffer, int width, - int height, unsigned int pix_fmt, struct v4lprocessing_data *data) -{ - int i, j, x1 = 0, x2 = 0, y1 = 0, y2 = 0; - float green_avg, x_avg, y_avg, avg_avg; - unsigned char *buf = src_buffer; - - int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 || - pix_fmt == V4L2_PIX_FMT_SGRBG8; - - for (i = 0; i < height; i += 2) { - for (j = 0; j < width; j += 2) { - x1 += *buf++; - x2 += *buf++; - } - for (j = 0; j < width; j += 2) { - y1 += *buf++; - y2 += *buf++; - } - } - - if (start_with_green) { - green_avg = (x1 + y2) / 2; - x_avg = x2; - y_avg = y1; - } else { - green_avg = (x2 + y1) / 2; - x_avg = x1; - y_avg = y2; - } - - avg_avg = (green_avg + x_avg + y_avg) / 3; - - data->comp1 = (avg_avg / green_avg) * 65536; - data->comp2 = (avg_avg / x_avg) * 65536; - data->comp3 = (avg_avg / y_avg) * 65536; -} - -void bayer_normalize_whitebalance_analyse(unsigned char *buf, int width, - int height, unsigned int pix_fmt, struct v4lprocessing_data *data) -{ - int i, j, value, max = 0, min = 255, n_fac, x1 = 0, x2 = 0, y1 = 0, y2 = 0; - float green_avg, x_avg, y_avg, avg_avg; - - int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 || - pix_fmt == V4L2_PIX_FMT_SGRBG8; - - for (i = 0; i < height; i += 2) { - for (j = 0; j < width; j += 2) { - x1 += *buf; - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - x2 += *buf; - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - } - for (j = 0; j < width; j += 2) { - y1 += *buf; - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - y2 += *buf; - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - } - } - - if (start_with_green) { - green_avg = (x1 + y2) / 2; - x_avg = x2; - y_avg = y1; - } else { - green_avg = (x2 + y1) / 2; - x_avg = x1; - y_avg = y2; - } - - n_fac = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min); - - avg_avg = (green_avg + x_avg + y_avg) / 3; - - data->comp1 = (avg_avg / green_avg) * n_fac; - data->comp2 = (avg_avg / x_avg) * n_fac; - data->comp3 = (avg_avg / y_avg) * n_fac; - - data->offset1 = min; - data->offset2 = data->norm_low_bound; -} - -#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) -#define TOP(color) (unsigned char)(((color)>0xff)?0xff:(color)) - -void bayer_normalize(unsigned char *buf, int width, - int height, struct v4lprocessing_data *data) -{ - int value; - unsigned char *buf_end = buf + width * height; - - while (buf < buf_end) { - value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - } -} - -void bayer_whitebalance(unsigned char *src_buffer, int width, - int height, unsigned int pix_fmt, - struct v4lprocessing_data *data) -{ - int i, j, value; - int limit = width * height; - unsigned char *buf = src_buffer; - - int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 || - pix_fmt == V4L2_PIX_FMT_SGRBG8; - - if (start_with_green) { - for (i = 0; i < height; i += 2) { - for (j = 0; j < width; j += 2) { - value = (*buf * data->comp1) >> 16; - *buf++ = TOP(value); - value = (*buf * data->comp2) >> 16; - *buf++ = TOP(value); - } - for (j = 0; j < width; j += 2) { - value = (*buf * data->comp3) >> 16; - *buf++ = TOP(value); - value = (*buf * data->comp1) >> 16; - *buf++ = TOP(value); - } - } - } else { - for (i = 0; i < height; i += 2) { - for (j = 0; j < width; j += 2) { - value = (*buf * data->comp2) >> 16; - *buf++ = TOP(value); - value = (*buf * data->comp1) >> 16; - *buf++ = TOP(value); - } - for (j = 0; j < width; j += 2) { - value = (*buf * data->comp1) >> 16; - *buf++ = TOP(value); - value = (*buf * data->comp3) >> 16; - *buf++ = TOP(value); - } - } - } -} - -void bayer_normalize_whitebalance(unsigned char *src_buffer, int width, - int height, unsigned int pix_fmt, - struct v4lprocessing_data *data) -{ - int i, j, value; - int limit = width * height; - unsigned char *buf = src_buffer; - - int start_with_green = pix_fmt == V4L2_PIX_FMT_SGBRG8 || - pix_fmt == V4L2_PIX_FMT_SGRBG8; - - if (start_with_green) { - for (i = 0; i < height; i += 2) { - for (j = 0; j < width; j += 2) { - value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - value = ((data->comp2 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - } - for (j = 0; j < width; j += 2) { - value = ((data->comp3 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - } - } - } else { - for (i = 0; i < height; i += 2) { - for (j = 0; j < width; j += 2) { - value = ((data->comp2 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - } - for (j = 0; j < width; j += 2) { - value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - value = ((data->comp3 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - } - } - } -} diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h index f2aae373d..fb514d7f5 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing-priv.h @@ -23,67 +23,31 @@ #include "../control/libv4lcontrol.h" -#define V4L2PROCESSING_PROCESS_NONE 0x00 -#define V4L2PROCESSING_PROCESS_NORMALIZE 0x01 -#define V4L2PROCESSING_PROCESS_WHITEBALANCE 0x02 -#define V4L2PROCESSING_PROCESS_NORMALIZE_WHITEBALANCE 0x03 -#define V4L2PROCESSING_PROCESS_RGB_NORMALIZE 0x01 -#define V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE 0x02 -#define V4L2PROCESSING_PROCESS_RGB_NORMALIZE_WHITEBALANCE 0x03 -#define V4L2PROCESSING_PROCESS_BAYER_NORMALIZE 0x11 -#define V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE 0x12 -#define V4L2PROCESSING_PROCESS_BAYER_NORMALIZE_WHITEBALANCE 0x13 - #define V4L2PROCESSING_UPDATE_RATE 10 struct v4lprocessing_data { struct v4lcontrol_data *control; int do_process; - /* Provides the current type of processing */ - int process; - int norm_low_bound; - int norm_high_bound; + /* True if any of the lookup tables does not contain + linear 0-255 */ + int lookup_table_active; /* Counts the number of processed frames until a V4L2PROCESSING_UPDATE_RATE overflow happens */ - int processing_data_update; - /* Multiplication factors and offsets from the analyse functions */ - int comp1; - int comp2; - int comp3; - int comp4; - int offset1; - int offset2; + int lookup_table_update_counter; + /* RGB/BGR lookup tables */ + unsigned char comp1[256]; + unsigned char green[256]; + unsigned char comp2[256]; }; -/* Processing Bayer */ -void bayer_normalize_analyse(unsigned char *src_buffer, int width, int height, - struct v4lprocessing_data *data); -void bayer_whitebalance_analyse(unsigned char *src_buffer, int width, - int height, unsigned int pix_fmt, - struct v4lprocessing_data *data); -void bayer_normalize_whitebalance_analyse(unsigned char *src_buffer, - int width, int height, unsigned int pix_fmt, - struct v4lprocessing_data *data); -void bayer_normalize(unsigned char *src_buffer, int width, int height, - struct v4lprocessing_data *data); -void bayer_whitebalance(unsigned char *src_buffer, int width, int height, - unsigned int pix_fmt, struct v4lprocessing_data *data); -void bayer_normalize_whitebalance(unsigned char *src_buffer, int width, - int height, unsigned int pix_fmt, - struct v4lprocessing_data *data); +struct v4lprocessing_filter { + /* Returns 1 if the filter is active */ + int (*active)(struct v4lprocessing_data *data); + /* Returns 1 if any of the lookup tables was changed */ + int (*calculate_lookup_tables)(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt); +}; -/* Processing RGB */ -void rgb_normalize_analyse(unsigned char *src_buffer, int width, int height, - struct v4lprocessing_data *data); -void rgb_whitebalance_analyse(unsigned char *src_buffer, int width, int height, - struct v4lprocessing_data *data); -void rgb_normalize_whitebalance_analyse(unsigned char *src_buffer, - int width, int height, struct v4lprocessing_data *data); -void rgb_normalize(unsigned char *src_buffer, int width, int height, - struct v4lprocessing_data *data); -void rgb_whitebalance(unsigned char *src_buffer, int width, int height, - struct v4lprocessing_data *data); -void rgb_normalize_whitebalance(unsigned char *src_buffer, int width, - int height, struct v4lprocessing_data *data); +extern struct v4lprocessing_filter whitebalance_filter; #endif diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c index f986da02d..3a3802aab 100644 --- a/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c +++ b/v4l2-apps/libv4l/libv4lconvert/processing/libv4lprocessing.c @@ -28,6 +28,10 @@ #include "libv4lprocessing-priv.h" #include "../libv4lconvert-priv.h" /* for PIX_FMT defines */ +static struct v4lprocessing_filter *filters[] = { + &whitebalance_filter, +}; + struct v4lprocessing_data *v4lprocessing_create(struct v4lcontrol_data* control) { struct v4lprocessing_data *data = @@ -46,136 +50,130 @@ void v4lprocessing_destroy(struct v4lprocessing_data *data) free(data); } -static int v4lprocessing_get_process(struct v4lprocessing_data *data, - unsigned int pix_fmt) +int v4lprocessing_pre_processing(struct v4lprocessing_data *data) { - int process = V4L2PROCESSING_PROCESS_NONE; + int i; - switch(pix_fmt) { - case V4L2_PIX_FMT_SBGGR8: - case V4L2_PIX_FMT_SGBRG8: - case V4L2_PIX_FMT_SGRBG8: - case V4L2_PIX_FMT_SRGGB8: - if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORMALIZE)) { - process |= V4L2PROCESSING_PROCESS_BAYER_NORMALIZE; - } - if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE)) { - process |= V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE; - } - break; - case V4L2_PIX_FMT_RGB24: - case V4L2_PIX_FMT_BGR24: - if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORMALIZE)) { - process |= V4L2PROCESSING_PROCESS_RGB_NORMALIZE; - } - if (v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE)) { - process |= V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE; - } - break; + data->do_process = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) + data->do_process = 1; } - return process; + return data->do_process; } -static void v4lprocessing_update_processing_data( - struct v4lprocessing_data *data, - unsigned int pix_fmt, unsigned char *buf, unsigned int width, - unsigned int height) +static void v4lprocessing_update_lookup_tables(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) { - switch (data->process) { - case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE: - bayer_normalize_analyse(buf, width, height, data); - break; + int i; - case V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE: - bayer_whitebalance_analyse(buf, width, height, pix_fmt, data); - break; + for (i = 0; i < 256; i++) { + data->comp1[i] = i; + data->green[i] = i; + data->comp2[i] = i; + } - case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE_WHITEBALANCE: - bayer_normalize_whitebalance_analyse(buf, width, height, pix_fmt, data); - break; + data->lookup_table_active = 0; + for (i = 0; i < ARRAY_SIZE(filters); i++) { + if (filters[i]->active(data)) { + if (filters[i]->calculate_lookup_tables(data, buf, fmt)) + data->lookup_table_active = 1; + } + } +} + +static void v4lprocessing_do_processing(struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + int x, y; - case V4L2PROCESSING_PROCESS_RGB_NORMALIZE: - rgb_normalize_analyse(buf, width, height, data); + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp1[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp2[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } break; - case V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE: - rgb_whitebalance_analyse(buf, width, height, data); + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + for (y = 0; y < fmt->fmt.pix.height / 2; y++) { + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width / 2; x++) { + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } break; - case V4L2PROCESSING_PROCESS_RGB_NORMALIZE_WHITEBALANCE: - rgb_normalize_whitebalance_analyse(buf, width, height, data); + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + for (y = 0; y < fmt->fmt.pix.height; y++) { + for (x = 0; x < fmt->fmt.pix.width; x++) { + *buf = data->comp1[*buf]; + buf++; + *buf = data->green[*buf]; + buf++; + *buf = data->comp2[*buf]; + buf++; + } + buf += fmt->fmt.pix.bytesperline - 3 * fmt->fmt.pix.width; + } break; } } -int v4lprocessing_pre_processing(struct v4lprocessing_data *data) -{ - data->do_process = - v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE) || - v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORMALIZE); - - if (!data->do_process) - data->process = V4L2PROCESSING_PROCESS_NONE; - - return data->do_process; -} - void v4lprocessing_processing(struct v4lprocessing_data *data, unsigned char *buf, const struct v4l2_format *fmt) { - int low_bound, high_bound, process; - if (!data->do_process) return; - process = v4lprocessing_get_process(data, fmt->fmt.pix.pixelformat); - if (process == V4L2PROCESSING_PROCESS_NONE) { - return; - } - - low_bound = v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORM_LOW_BOUND); - high_bound = v4lcontrol_get_ctrl(data->control, V4LCONTROL_NORM_HIGH_BOUND); - - if (process != data->process || low_bound != data->norm_low_bound || - high_bound != data->norm_high_bound) { - data->process = process; - data->norm_low_bound = low_bound; - data->norm_high_bound = high_bound; - data->processing_data_update = V4L2PROCESSING_UPDATE_RATE; + /* Do we support the current pixformat? */ + 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: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + break; + default: + return; /* Non supported pix format */ } - if (data->processing_data_update == V4L2PROCESSING_UPDATE_RATE) { - data->processing_data_update = 0; - v4lprocessing_update_processing_data(data, fmt->fmt.pix.pixelformat, buf, fmt->fmt.pix.width, fmt->fmt.pix.height); + if (v4lcontrol_controls_changed(data->control) || + data->lookup_table_update_counter == V4L2PROCESSING_UPDATE_RATE) { + v4lprocessing_update_lookup_tables(data, buf, fmt); + data->lookup_table_update_counter = 0; } else - data->processing_data_update++; - - switch (data->process) { - case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE: - bayer_normalize(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data); - break; - - case V4L2PROCESSING_PROCESS_BAYER_WHITEBALANCE: - bayer_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, fmt->fmt.pix.pixelformat, data); - break; - - case V4L2PROCESSING_PROCESS_BAYER_NORMALIZE_WHITEBALANCE: - bayer_normalize_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, fmt->fmt.pix.pixelformat, - data); - break; - - case V4L2PROCESSING_PROCESS_RGB_NORMALIZE: - rgb_normalize(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data); - break; + data->lookup_table_update_counter++; - case V4L2PROCESSING_PROCESS_RGB_WHITEBALANCE: - rgb_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data); - break; + if (data->lookup_table_active) + v4lprocessing_do_processing(data, buf, fmt); - case V4L2PROCESSING_PROCESS_RGB_NORMALIZE_WHITEBALANCE: - rgb_normalize_whitebalance(buf, fmt->fmt.pix.width, fmt->fmt.pix.height, data); - break; - } data->do_process = 0; } diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/rgbprocessing.c b/v4l2-apps/libv4l/libv4lconvert/processing/rgbprocessing.c deleted file mode 100644 index 4e0fc3f4a..000000000 --- a/v4l2-apps/libv4l/libv4lconvert/processing/rgbprocessing.c +++ /dev/null @@ -1,156 +0,0 @@ -/* -# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> -# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> -# (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 "libv4lprocessing-priv.h" - -void rgb_normalize_analyse(unsigned char *buf, int width, int height, - struct v4lprocessing_data *data) -{ - int value, max = 0, min = 255; - unsigned char *buf_end = buf + width * height * 3; - - while (buf < buf_end) { - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - } - - data->comp1 = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min); - data->offset1 = min; - data->offset2 = data->norm_low_bound; -} - -void rgb_whitebalance_analyse(unsigned char *buf, int width, int height, - struct v4lprocessing_data *data) -{ - int value, x = 0, y = 0, z = 0; - float x_avg, y_avg, z_avg, avg_avg; - unsigned char *buf_end = buf + width * height * 3; - - while (buf < buf_end) { - x += *buf++; - y += *buf++; - z += *buf++; - } - - x_avg = x; - y_avg = y; - z_avg = z; - avg_avg = (x_avg + y_avg + z_avg) / 3; - - data->comp1 = (avg_avg / x_avg) * 65536; - data->comp2 = (avg_avg / y_avg) * 65536; - data->comp3 = (avg_avg / z_avg) * 65536; -} - -void rgb_normalize_whitebalance_analyse(unsigned char *buf, - int width, int height, struct v4lprocessing_data *data) -{ - int value, max = 0, min = 255; - int n_fac, wb_max, x = 0, y = 0, z = 0; - float x_avg, y_avg, z_avg, avg_avg; - unsigned char *buf_end = buf + width * height * 3; - - while (buf < buf_end) { - x += *buf; - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - y += *buf; - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - z += *buf; - value = *buf++; - if (max < value) - max = value; - if (min > value) - min = value; - } - - x_avg = x; - y_avg = y; - z_avg = z; - avg_avg = (x_avg + y_avg + z_avg) / 3; - - n_fac = ((data->norm_high_bound - data->norm_low_bound) << 16) / (max - min); - - data->comp1 = (avg_avg / x_avg) * n_fac; - data->comp2 = (avg_avg / y_avg) * n_fac; - data->comp3 = (avg_avg / z_avg) * n_fac; - - data->offset1 = min; - data->offset2 = data->norm_low_bound; -} - -#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) -#define TOP(color) (unsigned char)(((color)>0xff)?0xff:(color)) - -void rgb_normalize(unsigned char *buf, int width, int height, - struct v4lprocessing_data *data) -{ - int value; - unsigned char *buf_end = buf + width * height * 3; - - while (buf < buf_end) { - value = ((data->comp1 * (*buf - data->offset1)) >> 16) + - data->offset2; - *buf++ = CLIP(value); - } -} - -void rgb_whitebalance(unsigned char *buf, int width, int height, - struct v4lprocessing_data *data) -{ - int value; - unsigned char *buf_end = buf + width * height * 3; - - while (buf < buf_end) { - value = (*buf * data->comp1) >> 16; - *buf++ = TOP(value); - value = (*buf * data->comp2) >> 16; - *buf++ = TOP(value); - value = (*buf * data->comp3) >> 16; - *buf++ = TOP(value); - } -} - -void rgb_normalize_whitebalance(unsigned char *buf, int width, int height, - struct v4lprocessing_data *data) -{ - int i, value; - int limit = width * height * 3; - unsigned char *buf_end = buf + width * height * 3; - - while (buf < buf_end) { - value = ((data->comp1 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - value = ((data->comp2 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - value = ((data->comp3 * (*buf - data->offset1)) >> 16) + data->offset2; - *buf++ = CLIP(value); - } -} diff --git a/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c new file mode 100644 index 000000000..f5bfd961c --- /dev/null +++ b/v4l2-apps/libv4l/libv4lconvert/processing/whitebalance.c @@ -0,0 +1,143 @@ +/* +# (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com> +# (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com> +# (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 */ + +#define CLIP(color) (unsigned char)(((color)>0xff)?0xff:(((color)<0)?0:(color))) + +static int whitebalance_active(struct v4lprocessing_data *data) { + return v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE); +} + +static int whitebalance_calculate_lookup_tables_bayer( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt, int starts_with_green) +{ + int x, y, a1 = 0, a2 = 0, b1 = 0, b2 = 0; + int green_avg, comp1_avg, comp2_avg, avg_avg; + + for (y = 0; y < fmt->fmt.pix.height; y += 2) { + for (x = 0; x < fmt->fmt.pix.width; x += 2) { + a1 += *buf++; + a2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + for (x = 0; x < fmt->fmt.pix.width; x += 2) { + b1 += *buf++; + b2 += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width; + } + + if (starts_with_green) { + green_avg = (a1 + b2) / 512; + comp1_avg = a2 / 256; + comp2_avg = b1 / 256; + } else { + green_avg = (a2 + b1) / 512; + comp1_avg = a1 / 256; + comp2_avg = b2 / 256; + } + + x = fmt->fmt.pix.width * fmt->fmt.pix.height / 64; + if (abs(green_avg - comp1_avg) < x && + abs(green_avg - comp2_avg) < x && + abs(comp1_avg - comp2_avg) < x) + return 0; + + avg_avg = (green_avg + comp1_avg + comp2_avg) / 3; + + for (x = 0; x < 256; x++) { + data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg); + data->green[x] = CLIP(data->green[x] * avg_avg / green_avg); + data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg); + } + + return 1; +} + +static int whitebalance_calculate_lookup_tables_rgb( + struct v4lprocessing_data *data, unsigned char *buf, + const struct v4l2_format *fmt) +{ + int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0, avg_avg; + + for (y = 0; y < fmt->fmt.pix.height; y++) { + for (x = 0; x < fmt->fmt.pix.width; x++) { + comp1_avg += *buf++; + green_avg += *buf++; + comp2_avg += *buf++; + } + buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3; + } + + x = fmt->fmt.pix.width * fmt->fmt.pix.height * 4; + if (abs(green_avg - comp1_avg) < x && + abs(green_avg - comp2_avg) < x && + abs(comp1_avg - comp2_avg) < x) + return 0; + + /* scale to avoid integer overflows */ + green_avg /= 256; + comp1_avg /= 256; + comp2_avg /= 256; + avg_avg = (green_avg + comp1_avg + comp2_avg) / 3; + + for (x = 0; x < 256; x++) { + data->comp1[x] = CLIP(data->comp1[x] * avg_avg / comp1_avg); + data->green[x] = CLIP(data->green[x] * avg_avg / green_avg); + data->comp2[x] = CLIP(data->comp2[x] * avg_avg / comp2_avg); + } + + return 1; +} + + +static int whitebalance_calculate_lookup_tables( + struct v4lprocessing_data *data, + unsigned char *buf, const struct v4l2_format *fmt) +{ + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_SGBRG8: + case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 1); + + case V4L2_PIX_FMT_SBGGR8: + case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */ + return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 0); + + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + return whitebalance_calculate_lookup_tables_rgb(data, buf, fmt); + } + + return 0; /* Should never happen */ +} + +struct v4lprocessing_filter whitebalance_filter = { + whitebalance_active, whitebalance_calculate_lookup_tables }; |