1 /*
2 #             (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
3 #             (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
4 #             (C) 2008-2009 Hans de Goede <hdegoede@redhat.com>
5 
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU Lesser General Public License as published by
8 # the Free Software Foundation; either version 2.1 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU Lesser General Public License for more details.
15 #
16 # You should have received a copy of the GNU Lesser General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA  02110-1335  USA
19  */
20 
21 #include <errno.h>
22 #include <string.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <unistd.h>
26 #include "libv4lprocessing.h"
27 #include "libv4lprocessing-priv.h"
28 #include "../libv4lconvert-priv.h" /* for PIX_FMT defines */
29 
30 #define CLIP256(color) (((color) > 0xff) ? 0xff : (((color) < 0) ? 0 : (color)))
31 #define CLIP(color, min, max) (((color) > (max)) ? (max) : (((color) < (min)) ? (min) : (color)))
32 
whitebalance_active(struct v4lprocessing_data * data)33 static int whitebalance_active(struct v4lprocessing_data *data)
34 {
35 	int wb;
36 
37 	wb = v4lcontrol_get_ctrl(data->control, V4LCONTROL_WHITEBALANCE);
38 	if (!wb) {
39 		/* Reset cached color averages */
40 		data->green_avg = 0;
41 	}
42 
43 	return wb;
44 }
45 
whitebalance_calculate_lookup_tables_generic(struct v4lprocessing_data * data,int green_avg,int comp1_avg,int comp2_avg)46 static int whitebalance_calculate_lookup_tables_generic(
47 		struct v4lprocessing_data *data, int green_avg, int comp1_avg, int comp2_avg)
48 {
49 	int i, avg_avg;
50 	const int threshold = 64;
51 	const int max_step = 128;
52 
53 	/* Clip averages (restricts maximum white balance correction) */
54 	green_avg = CLIP(green_avg, 512, 3072);
55 	comp1_avg = CLIP(comp1_avg, 512, 3072);
56 	comp2_avg = CLIP(comp2_avg, 512, 3072);
57 
58 	/* First frame ? */
59 	if (data->green_avg == 0) {
60 		data->green_avg = green_avg;
61 		data->comp1_avg = comp1_avg;
62 		data->comp2_avg = comp2_avg;
63 	} else {
64 		/* Slowly adjust the averages used for the correction, so that
65 		   we do not get a sudden change in colors */
66 		int throttling = 0;
67 
68 		if (abs(data->green_avg - green_avg) > max_step) {
69 			if (data->green_avg < green_avg)
70 				data->green_avg += max_step;
71 			else
72 				data->green_avg -= max_step;
73 			throttling = 1;
74 		} else
75 			data->green_avg = green_avg;
76 
77 		if (abs(data->comp1_avg - comp1_avg) > max_step) {
78 			if (data->comp1_avg < comp1_avg)
79 				data->comp1_avg += max_step;
80 			else
81 				data->comp1_avg -= max_step;
82 			throttling = 1;
83 		} else
84 			data->comp1_avg = comp1_avg;
85 
86 		if (abs(data->comp2_avg - comp2_avg) > max_step) {
87 			if (data->comp2_avg < comp2_avg)
88 				data->comp2_avg += max_step;
89 			else
90 				data->comp2_avg -= max_step;
91 			throttling = 1;
92 		} else
93 			data->comp2_avg = comp2_avg;
94 
95 		/*
96 		 * If we are still converging to a stable update situation,
97 		 * re-calc the lookup tables next frame, but only if no
98 		 * other processing plugin has already asked for a shorter
99 		 * update cycle, as asking for an update each frame while
100 		 * some other pluging is trying to adjust hw settings is bad.
101 		 */
102 		if (throttling && data->lookup_table_update_counter == 0)
103 			data->lookup_table_update_counter =
104 						V4L2PROCESSING_UPDATE_RATE;
105 	}
106 
107 	if (abs(data->green_avg - data->comp1_avg) < threshold &&
108 			abs(data->green_avg - data->comp2_avg) < threshold &&
109 			abs(data->comp1_avg - data->comp2_avg) < threshold)
110 		return 0;
111 
112 	avg_avg = (data->green_avg + data->comp1_avg + data->comp2_avg) / 3;
113 
114 	for (i = 0; i < 256; i++) {
115 		data->comp1[i] = CLIP256(data->comp1[i] * avg_avg / data->comp1_avg);
116 		data->green[i] = CLIP256(data->green[i] * avg_avg / data->green_avg);
117 		data->comp2[i] = CLIP256(data->comp2[i] * avg_avg / data->comp2_avg);
118 	}
119 
120 	return 1;
121 }
122 
whitebalance_calculate_lookup_tables_bayer(struct v4lprocessing_data * data,unsigned char * buf,const struct v4l2_format * fmt,int starts_with_green)123 static int whitebalance_calculate_lookup_tables_bayer(
124 		struct v4lprocessing_data *data, unsigned char *buf,
125 		const struct v4l2_format *fmt, int starts_with_green)
126 {
127 	int x, y, a1 = 0, a2 = 0, b1 = 0, b2 = 0;
128 	int green_avg, comp1_avg, comp2_avg;
129 
130 	for (y = 0; y < fmt->fmt.pix.height; y += 2) {
131 		for (x = 0; x < fmt->fmt.pix.width; x += 2) {
132 			a1 += *buf++;
133 			a2 += *buf++;
134 		}
135 		buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
136 		for (x = 0; x < fmt->fmt.pix.width; x += 2) {
137 			b1 += *buf++;
138 			b2 += *buf++;
139 		}
140 		buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width;
141 	}
142 
143 	if (starts_with_green) {
144 		green_avg = a1 / 2 + b2 / 2;
145 		comp1_avg = a2;
146 		comp2_avg = b1;
147 	} else {
148 		green_avg = a2 / 2 + b1 / 2;
149 		comp1_avg = a1;
150 		comp2_avg = b2;
151 	}
152 
153 	/* Norm avg to ~ 0 - 4095 */
154 	green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64;
155 	comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64;
156 	comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 64;
157 
158 	return whitebalance_calculate_lookup_tables_generic(data, green_avg,
159 			comp1_avg, comp2_avg);
160 }
161 
whitebalance_calculate_lookup_tables_rgb(struct v4lprocessing_data * data,unsigned char * buf,const struct v4l2_format * fmt)162 static int whitebalance_calculate_lookup_tables_rgb(
163 		struct v4lprocessing_data *data, unsigned char *buf,
164 		const struct v4l2_format *fmt)
165 {
166 	int x, y, green_avg = 0, comp1_avg = 0, comp2_avg = 0;
167 
168 	for (y = 0; y < fmt->fmt.pix.height; y++) {
169 		for (x = 0; x < fmt->fmt.pix.width; x++) {
170 			comp1_avg += *buf++;
171 			green_avg += *buf++;
172 			comp2_avg += *buf++;
173 		}
174 		buf += fmt->fmt.pix.bytesperline - fmt->fmt.pix.width * 3;
175 	}
176 
177 	/* Norm avg to ~ 0 - 4095 */
178 	green_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16;
179 	comp1_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16;
180 	comp2_avg /= fmt->fmt.pix.width * fmt->fmt.pix.height / 16;
181 
182 	return whitebalance_calculate_lookup_tables_generic(data, green_avg,
183 			comp1_avg, comp2_avg);
184 }
185 
186 
whitebalance_calculate_lookup_tables(struct v4lprocessing_data * data,unsigned char * buf,const struct v4l2_format * fmt)187 static int whitebalance_calculate_lookup_tables(
188 		struct v4lprocessing_data *data,
189 		unsigned char *buf, const struct v4l2_format *fmt)
190 {
191 	switch (fmt->fmt.pix.pixelformat) {
192 	case V4L2_PIX_FMT_SGBRG8:
193 	case V4L2_PIX_FMT_SGRBG8: /* Bayer patterns starting with green */
194 		return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 1);
195 
196 	case V4L2_PIX_FMT_SBGGR8:
197 	case V4L2_PIX_FMT_SRGGB8: /* Bayer patterns *NOT* starting with green */
198 		return whitebalance_calculate_lookup_tables_bayer(data, buf, fmt, 0);
199 
200 	case V4L2_PIX_FMT_RGB24:
201 	case V4L2_PIX_FMT_BGR24:
202 		return whitebalance_calculate_lookup_tables_rgb(data, buf, fmt);
203 	}
204 
205 	return 0; /* Should never happen */
206 }
207 
208 struct v4lprocessing_filter whitebalance_filter = {
209 	whitebalance_active, whitebalance_calculate_lookup_tables
210 };
211