1 /*
2 * STV0680 Vision Camera Chipset Driver
3 * Copyright 2000 Adam Harrison <adam@antispin.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301 USA
19 */
20
21 #include <stdio.h>
22 #include <math.h>
23 #include <stdlib.h>
24 #include "bayer.h"
25
26 /* Enhanced by Kurt Garloff to do scaling and debayering at the same time. */
bayer_unshuffle_preview(unsigned int w,unsigned int h,unsigned int scale,unsigned char * raw,unsigned char * output)27 void bayer_unshuffle_preview(unsigned int w, unsigned int h, unsigned int scale, unsigned char *raw, unsigned char *output)
28 {
29
30 int x, y, nx, ny;
31 int colour; int rgb[3];
32 int nw = w >> scale;
33 int nh = h >> scale;
34 int incr = 1<<scale;
35
36 for (ny = 0; ny < nh; ++ny, raw += w<<scale) {
37 for (nx = 0; nx < nw; ++nx, output += 3) {
38 rgb[0] = 0; rgb[1] = 0; rgb[2] = 0;
39 for (y = 0; y < incr; ++y) {
40 for (x = 0; x < incr; ++x) {
41 colour = 1 - (x&1) + (y&1);
42 rgb[colour] += raw[y*w + (nx<<(scale-1))+(x>>1) + ((x&1)? 0: (w>>1))];
43 }
44 }
45 output[0] = rgb[0]>>(2*scale-2);
46 output[1] = rgb[1]>>(2*scale-1);
47 output[2] = rgb[2]>>(2*scale-2);
48 }
49 }
50 }
51
52 /****** gamma correction from trans[], plus hardcoded white balance */
53 /* Thanks to Alexander Schwartx <alexander.schwartx@gmx.net> for this code.
54 Gamma correction (trans[] values generated by (pow((i-17)/239, GAMMA)*254)
55 where GAMMA=0.5x, 1<i<255. */
56 /* KG: Looking at very dark parts of images, the sensor seems to produce
57 * only very few points below 0x11 and almost none below 14. Therefore we map everything
58 * below 14 to 0 and ev'thing below 17 to 1; then the power function reigns.
59 */
60
61 #define ZERO0 14 /* 0--13 mapped to 0 */
62 #define ZERO1 17 /* 14--16 mapped to 1 */
63
64 typedef struct _rgbgamma {
65 float ampl, gamma;
66 } rgbgamma;
67
68
69 /* KG: Some notes on these:
70 * - Try to avoid strong deviations from 1.00 for the amplification,
71 * because this potentially results in not using the full range
72 * of colours (<1) or in clipping (>1) multiple colours to max,
73 * which would be a loss of information.
74 * - The gamma mainly determines how fast values increase after ZERO1.
75 * Influence on the highlights is small; therefore the description
76 * with amplifiaction and gamma seems not very appropriate; a better
77 * correction function would allow to influence the slope for small
78 * and for large values independently without incurring loss of
79 * accuracy/information. It should not be hard to construct such a
80 * thing. (Splines or B�zier or Triginometric/Hyperbolic functions
81 * could be used, e.g.)
82 * - The below parameters have been found by lots of experiments with
83 * pictures taken at different light levels. They're optimized for
84 * my PenCam (and my screens), of course. No theory behind this;
85 * I don't have insight into the physics of the imaging sensor.
86 * CCDs are linear, basically; but higher order effects may play
87 * a role as well as the electronics that controls the shutter
88 * and the one doing the readout.
89 */
90 static const rgbgamma gampar[6][3] = {
91 { { 1.02, 0.56 }, { 1.00, 0.61 }, { 0.99, 0.65 } }, /* cold */
92 { { 1.01, 0.56 }, { 1.00, 0.58 }, { 1.00, 0.61 } }, /* coldish */
93 { { 1.00, 0.55 }, { 1.00, 0.57 }, { 1.00, 0.59 } }, /* mid */
94 { { 1.00, 0.55 }, { 1.00, 0.56 }, { 1.01, 0.55 } }, /* warmish */
95 { { 1.01, 0.56 }, { 0.99, 0.57 }, { 1.03, 0.50 } }, /* warm */
96 { { 1.03, 0.52 }, { 0.97, 0.57 }, { 1.04, 0.49 } } /* warm bright */
97 };
98
light_enhance(unsigned int vw,unsigned int vh,unsigned int coarse,unsigned int fine,unsigned char avg_pix,unsigned char * output)99 void light_enhance(unsigned int vw, unsigned int vh, unsigned int coarse, unsigned int fine,
100 unsigned char avg_pix, unsigned char *output)
101 {
102 unsigned long int i;
103 int lt=3; /* 3 is auto */
104 /* float wb[3][3]; */
105 unsigned char trans[3][256];
106 unsigned char col;
107 /* int tmp1, tmp2, tmp3, whitex=20, whitey=20, j, k; */
108
109 double brightness = 1.00; /* FIXME: configurable? */
110
111 /* fprintf(stderr, "(FineExp=%i CoarseExp=%i => filter=", fine, coarse); */
112
113 #if 0
114 if (fine >= (coarse<<1)) {
115 lt = 0;
116 /* fprintf(stderr, "natural)\n"); */
117 } else if (((fine<<1) < coarse) && (coarse < 400)) {
118 lt = 2;
119 /* fprintf(stderr, "incandescent)\n"); */
120 } else {
121 lt = 1;
122 /* fprintf(stderr, "fluorescent)\n"); */
123 }
124 wb[0][0] = 1.08 * x; wb[0][1] = 1.00 * x; wb[0][2] = 0.95 * x; /* natural */
125 wb[1][0] = 1.00 * x; wb[1][1] = 1.00 * x; wb[1][2] = 1.00 * x; /* fluorescent */
126 wb[2][0] = 0.90 * x; wb[2][1] = 1.00 * x; wb[2][2] = 1.11 * x; /* incandescent */
127 #else
128 if (fine > coarse) {
129 lt = 0; /* fprintf (stderr, "cold)\n"); */
130 } else if (coarse < 100) {
131 lt = 1; /* fprintf (stderr, "coldish)\n"); */
132 } else if (coarse < 200) {
133 lt = 2; /* fprintf (stderr, "mid)\n"); */
134 } else if (coarse < 400) {
135 lt = 3; /* fprintf (stderr, "warmish)\n"); */
136 } else if (avg_pix < 94) {
137 lt = 4; /* fprintf (stderr, "warm)\n"); */
138 } else {
139 lt = 5; /* fprintf (stderr, "warm, bright)\n"); */
140 }
141 #endif
142
143 #if 0
144 /* find white pixel */
145 for (j=0;j<vh;j++)
146 {
147 for (k=0; k<vw; k++)
148 {
149 i = (j*vw + k)*3;
150 tmp1 = abs(*(output+i) - *(output+i+1));
151 tmp2 = abs(*(output+i) - *(output+i+2));
152 tmp3 = abs(*(output+i+1) - *(output+i+2));
153 if ((tmp1<16) && (tmp2<16) && (tmp3<16) && (*(output+i)>=160)) {
154 whitex = k; whitey = j;
155 break;
156 }
157 }
158 }
159 #endif
160
161 for (col = 0; col < 3; col++) {
162 double y;
163 const rgbgamma *gp = gampar[lt] + col;
164 for(i=0; i<256; ++i) {
165 if (i < ZERO0)
166 y = 0;
167 else if (i < ZERO1)
168 y = 1;
169 else
170 y = brightness * gp->ampl * (2 + pow((i-ZERO1)/((double)254-ZERO1),gp->gamma) * 253.5);
171 if (y > 255.0)
172 y = 255.0;
173 trans[col][i] = (unsigned char) y;
174 }
175 }
176
177 for (i=0;i<(vw*vh*3);i+=3)
178 {
179 int r,g,b;
180 r = *(output+i);
181 g = *(output+i+1);
182 b = *(output+i+2);
183 /* this (adjusting white) isn't quite right yet, so I turned it off */
184 if ( 0 && (abs(r-g) < 8) &&
185 (abs(r-b) < 8) &&
186 (abs(b-g) < 8)) {
187 int v = trans[1][(r+b+g+1)/3];
188 *(output+i) = (unsigned char) (v);
189 *(output+i+1) = (unsigned char) (v);
190 *(output+i+2) = (unsigned char) (v);
191 fprintf(stderr,"Adjusting white\n");
192 } else { /* this is OK */
193 *(output+i) = trans[0][r];
194 *(output+i+1) = trans[1][g];
195 *(output+i+2) = trans[2][b];
196 }
197 } /* for */
198 } /* light_enhance */
199