1 |
673 |
markom |
/*
|
2 |
|
|
* transform.c
|
3 |
|
|
* Calculate coefficients for tranformation equation
|
4 |
|
|
* Copyright (C) 1999 Bradley D. LaRonde <brad@ltc.com>
|
5 |
|
|
*
|
6 |
|
|
* This program is free software; you may redistribute it and/or modify
|
7 |
|
|
* it under the terms of the GNU General Public License as published by
|
8 |
|
|
* the Free Software Foundation; either version 2 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 General Public License for more details.
|
15 |
|
|
*
|
16 |
|
|
* You should have received a copy of the GNU General Public License
|
17 |
|
|
* along with this program; if not, write to the Free Software
|
18 |
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
19 |
|
|
*/
|
20 |
|
|
#include "windows.h"
|
21 |
|
|
#include "mou_tp.h"
|
22 |
|
|
#include "transform.h"
|
23 |
|
|
|
24 |
|
|
int CalcTransformationCoefficientsSimple(CALIBRATION_PAIRS *pcp, TRANSFORMATION_COEFFICIENTS *ptc)
|
25 |
|
|
{
|
26 |
|
|
/*
|
27 |
|
|
* This is my simple way of calculating some of the coefficients -
|
28 |
|
|
* enough to do a simple scale and translate.
|
29 |
|
|
* It ignores any dependencies between axiis (rotation and/or skew).
|
30 |
|
|
*/
|
31 |
|
|
|
32 |
|
|
int min_screen_x = pcp->ul.screen.x;
|
33 |
|
|
int min_screen_y = pcp->ul.screen.y;
|
34 |
|
|
int max_screen_x = pcp->lr.screen.x;
|
35 |
|
|
int max_screen_y = pcp->lr.screen.y;
|
36 |
|
|
|
37 |
|
|
int min_device_x = pcp->ul.device.x;
|
38 |
|
|
int min_device_y = pcp->ul.device.y;
|
39 |
|
|
int max_device_x = pcp->lr.device.x;
|
40 |
|
|
int max_device_y = pcp->lr.device.y;
|
41 |
|
|
|
42 |
|
|
ptc->s = (1 << 16);
|
43 |
|
|
ptc->a = ptc->s * (min_screen_x - max_screen_x) / (min_device_x - max_device_x);
|
44 |
|
|
ptc->b = 0;
|
45 |
|
|
ptc->c = (ptc->a * -min_device_x) + (ptc->s * min_screen_x);
|
46 |
|
|
ptc->d = 0;
|
47 |
|
|
ptc->e = ptc->s * (min_screen_y - max_screen_y) / (min_device_y - max_device_y);
|
48 |
|
|
ptc->f = (ptc->e * -min_device_y) + (ptc->s * min_screen_y);
|
49 |
|
|
|
50 |
|
|
return 0;
|
51 |
|
|
}
|
52 |
|
|
|
53 |
|
|
int CalcTransformationCoefficientsBetter(CALIBRATION_PAIRS *pcp, TRANSFORMATION_COEFFICIENTS *ptc)
|
54 |
|
|
{
|
55 |
|
|
/*
|
56 |
|
|
* Janus (the man) <janus@place.org> came up with a much better way
|
57 |
|
|
* to figure the coefficients. His original algorithm was written in MOO.
|
58 |
|
|
* Jay Carlson <> did the translation to C.
|
59 |
|
|
* This way takes into account inter-axis dependency like rotation and skew.
|
60 |
|
|
*/
|
61 |
|
|
|
62 |
|
|
double vector[3][2] =
|
63 |
|
|
{
|
64 |
|
|
{pcp->ul.screen.x, pcp->ul.screen.y},
|
65 |
|
|
{pcp->ur.screen.x, pcp->ur.screen.y},
|
66 |
|
|
{pcp->lr.screen.x, pcp->lr.screen.y}
|
67 |
|
|
};
|
68 |
|
|
|
69 |
|
|
double matrix[3][3] =
|
70 |
|
|
{
|
71 |
|
|
{pcp->ul.device.x, pcp->ul.device.y, 1.0},
|
72 |
|
|
{pcp->ur.device.x, pcp->ur.device.y, 1.0},
|
73 |
|
|
{pcp->lr.device.x, pcp->lr.device.y, 1.0}
|
74 |
|
|
};
|
75 |
|
|
|
76 |
|
|
int i, j, r, k;
|
77 |
|
|
double p, q;
|
78 |
|
|
|
79 |
|
|
for (i = 0; i < 3; i++) {
|
80 |
|
|
p = matrix[i][i];
|
81 |
|
|
|
82 |
|
|
for (j = 0; j < 3; j++) {
|
83 |
|
|
matrix[i][j] = matrix[i][j] / p;
|
84 |
|
|
}
|
85 |
|
|
|
86 |
|
|
for (j = 0; j < 2; j++) {
|
87 |
|
|
vector[i][j] = vector[i][j] / p;
|
88 |
|
|
}
|
89 |
|
|
|
90 |
|
|
for (r = 0; r < 3; r++) {
|
91 |
|
|
if (r != i) {
|
92 |
|
|
q = matrix[r][i];
|
93 |
|
|
|
94 |
|
|
matrix[r][i] = 0.0;
|
95 |
|
|
|
96 |
|
|
for (k = i + 1; k < 3; k++) {
|
97 |
|
|
matrix[r][k] = matrix[r][k] - (q * matrix[i][k]);
|
98 |
|
|
}
|
99 |
|
|
|
100 |
|
|
for (k = 0; k < 2; k++) {
|
101 |
|
|
vector[r][k] = vector[r][k] - (q * vector[i][k]);
|
102 |
|
|
}
|
103 |
|
|
}
|
104 |
|
|
}
|
105 |
|
|
}
|
106 |
|
|
|
107 |
|
|
ptc->s = 1 << 16;
|
108 |
|
|
ptc->a = vector[0][0] * ptc->s;
|
109 |
|
|
ptc->b = vector[1][0] * ptc->s;
|
110 |
|
|
ptc->c = vector[2][0] * ptc->s;
|
111 |
|
|
ptc->d = vector[0][1] * ptc->s;
|
112 |
|
|
ptc->e = vector[1][1] * ptc->s;
|
113 |
|
|
ptc->f = vector[2][1] * ptc->s;
|
114 |
|
|
|
115 |
|
|
return 0;
|
116 |
|
|
}
|
117 |
|
|
|
118 |
|
|
int CalcTransformationCoefficientsEvenBetter(CALIBRATION_PAIRS *pcp, TRANSFORMATION_COEFFICIENTS *ptc)
|
119 |
|
|
{
|
120 |
|
|
/*
|
121 |
|
|
* Mike Klar <> added the an xy term to correct for trapezoidial distortion.
|
122 |
|
|
*/
|
123 |
|
|
|
124 |
|
|
double vector[4][2] =
|
125 |
|
|
{
|
126 |
|
|
{pcp->ul.screen.x, pcp->ul.screen.y},
|
127 |
|
|
{pcp->ur.screen.x, pcp->ur.screen.y},
|
128 |
|
|
{pcp->lr.screen.x, pcp->lr.screen.y},
|
129 |
|
|
{pcp->ll.screen.x, pcp->ll.screen.y}
|
130 |
|
|
};
|
131 |
|
|
|
132 |
|
|
double matrix[4][4] =
|
133 |
|
|
{
|
134 |
|
|
{pcp->ul.device.x, pcp->ul.device.x * pcp->ul.device.y, pcp->ul.device.y, 1.0},
|
135 |
|
|
{pcp->ur.device.x, pcp->ur.device.x * pcp->ur.device.y, pcp->ur.device.y, 1.0},
|
136 |
|
|
{pcp->lr.device.x, pcp->lr.device.x * pcp->lr.device.y, pcp->lr.device.y, 1.0},
|
137 |
|
|
{pcp->ll.device.x, pcp->ll.device.x * pcp->ll.device.y, pcp->ll.device.y, 1.0}
|
138 |
|
|
};
|
139 |
|
|
|
140 |
|
|
int i, j, r, k;
|
141 |
|
|
double p, q;
|
142 |
|
|
|
143 |
|
|
for (i = 0; i < 4; i++) {
|
144 |
|
|
p = matrix[i][i];
|
145 |
|
|
|
146 |
|
|
for (j = 0; j < 4; j++) {
|
147 |
|
|
matrix[i][j] = matrix[i][j] / p;
|
148 |
|
|
}
|
149 |
|
|
|
150 |
|
|
for (j = 0; j < 2; j++) {
|
151 |
|
|
vector[i][j] = vector[i][j] / p;
|
152 |
|
|
}
|
153 |
|
|
|
154 |
|
|
for (r = 0; r < 4; r++) {
|
155 |
|
|
if (r != i) {
|
156 |
|
|
q = matrix[r][i];
|
157 |
|
|
|
158 |
|
|
matrix[r][i] = 0.0;
|
159 |
|
|
|
160 |
|
|
for (k = i + 1; k < 4; k++) {
|
161 |
|
|
matrix[r][k] = matrix[r][k] - (q * matrix[i][k]);
|
162 |
|
|
}
|
163 |
|
|
|
164 |
|
|
for (k = 0; k < 2; k++) {
|
165 |
|
|
vector[r][k] = vector[r][k] - (q * vector[i][k]);
|
166 |
|
|
}
|
167 |
|
|
}
|
168 |
|
|
}
|
169 |
|
|
}
|
170 |
|
|
|
171 |
|
|
/* I just drop the xy coefficient since it is so small. */
|
172 |
|
|
ptc->s = 1 << 16;
|
173 |
|
|
ptc->a = vector[0][0] * ptc->s;
|
174 |
|
|
ptc->b = vector[2][0] * ptc->s;
|
175 |
|
|
ptc->c = vector[3][0] * ptc->s;
|
176 |
|
|
ptc->d = vector[0][1] * ptc->s;
|
177 |
|
|
ptc->e = vector[2][1] * ptc->s;
|
178 |
|
|
ptc->f = vector[3][1] * ptc->s;
|
179 |
|
|
|
180 |
|
|
return 0;
|
181 |
|
|
}
|
182 |
|
|
|
183 |
|
|
int CalcTransformationCoefficientsBest(CALIBRATION_PAIRS *pcp, TRANSFORMATION_COEFFICIENTS *ptc)
|
184 |
|
|
{
|
185 |
|
|
/*
|
186 |
|
|
* Mike Klar <> came up with a best-fit solution that works best.
|
187 |
|
|
*/
|
188 |
|
|
|
189 |
|
|
const int first_point = 0;
|
190 |
|
|
const int last_point = 4;
|
191 |
|
|
int i;
|
192 |
|
|
|
193 |
|
|
double Sx=0, Sy=0, Sxy=0, Sx2=0, Sy2=0, Sm=0, Sn=0, Smx=0, Smy=0, Snx=0, Sny=0, S=0;
|
194 |
|
|
double t1, t2, t3, t4, t5, t6, q;
|
195 |
|
|
|
196 |
|
|
/* cast the struct to an array - hacky but ok */
|
197 |
|
|
CALIBRATION_PAIR *cp = (CALIBRATION_PAIR*)pcp;
|
198 |
|
|
|
199 |
|
|
/*
|
200 |
|
|
* Do a best-fit calculation for as many points as we want, as
|
201 |
|
|
* opposed to an exact fit, which can only be done against 3 points.
|
202 |
|
|
*
|
203 |
|
|
* The following calculates various sumnations of the sample data
|
204 |
|
|
* coordinates. For purposes of naming convention, x and y
|
205 |
|
|
* refer to device coordinates, m and n refer to screen
|
206 |
|
|
* coordinates, S means sumnation. x2 and y2 are x squared and
|
207 |
|
|
* y squared, S by itself is just a count of points (= sumnation
|
208 |
|
|
* of 1).
|
209 |
|
|
*/
|
210 |
|
|
|
211 |
|
|
for (i = first_point; i < last_point + 1; i++) {
|
212 |
|
|
Sx += cp[i].device.x;
|
213 |
|
|
Sy += cp[i].device.y;
|
214 |
|
|
Sxy += cp[i].device.x * cp[i].device.y;
|
215 |
|
|
Sx2 += cp[i].device.x * cp[i].device.x;
|
216 |
|
|
Sy2 += cp[i].device.y * cp[i].device.y;
|
217 |
|
|
Sm += cp[i].screen.x;
|
218 |
|
|
Sn += cp[i].screen.y;
|
219 |
|
|
Smx += cp[i].screen.x * cp[i].device.x;
|
220 |
|
|
Smy += cp[i].screen.x * cp[i].device.y;
|
221 |
|
|
Snx += cp[i].screen.y * cp[i].device.x;
|
222 |
|
|
Sny += cp[i].screen.y * cp[i].device.y;
|
223 |
|
|
S += 1;
|
224 |
|
|
}
|
225 |
|
|
|
226 |
|
|
#if 0
|
227 |
|
|
printf("%f, %f, %f, %f, "
|
228 |
|
|
"%f, %f, %f, %f, "
|
229 |
|
|
"%f, %f, %f, %f\n",
|
230 |
|
|
Sx, Sy, Sxy, Sx2, Sy2, Sm, Sn, Smx, Smy, Snx, Sny, S);
|
231 |
|
|
#endif
|
232 |
|
|
|
233 |
|
|
/*
|
234 |
|
|
* Next we solve the simultaneous equations (these equations minimize
|
235 |
|
|
* the sum of the square of the m and n error):
|
236 |
|
|
*
|
237 |
|
|
* | Sx2 Sxy Sx | | a d | | Smx Snx |
|
238 |
|
|
* | Sxy Sy2 Sy | * | b e | = | Smy Sny |
|
239 |
|
|
* | Sx Sy S | | c f | | Sm Sn |
|
240 |
|
|
*
|
241 |
|
|
* We could do the matrix solution in code, but that leads to several
|
242 |
|
|
* divide by 0 conditions for cases where the data is truly solvable
|
243 |
|
|
* (becuase those terms cancel out of the final solution), so we just
|
244 |
|
|
* give the final solution instread. t1 through t6 and q are just
|
245 |
|
|
* convenience variables for terms that are used repeatedly - we could
|
246 |
|
|
* calculate each of the coefficients directly at this point with a
|
247 |
|
|
* nasty long equation, but that would be extremly inefficient.
|
248 |
|
|
*/
|
249 |
|
|
|
250 |
|
|
t1 = Sxy * Sy - Sx * Sy2;
|
251 |
|
|
t2 = Sxy * Sx - Sx2 * Sy;
|
252 |
|
|
t3 = Sx2 * Sy2 - Sxy * Sxy;
|
253 |
|
|
t4 = Sy2 * S - Sy * Sy;
|
254 |
|
|
t5 = Sx * Sy - Sxy * S;
|
255 |
|
|
t6 = Sx2 * S - Sx * Sx;
|
256 |
|
|
|
257 |
|
|
q = t1 * Sx + t2 * Sy + t3 * S;
|
258 |
|
|
|
259 |
|
|
/*
|
260 |
|
|
* If q = 0, then the data is unsolvable. This should only happen
|
261 |
|
|
* when there are not enough unique data points (less than 3 points
|
262 |
|
|
* will give infinite solutions), or at least one of the
|
263 |
|
|
* coefficients is infinite (which would indicate that the same
|
264 |
|
|
* device point represents an infinite area of the screen, probably
|
265 |
|
|
* as a result of the same device data point given for 2 different
|
266 |
|
|
* screen points). The first condition should never happen, since
|
267 |
|
|
* we're always feeding in at least 3 unique screen points. The
|
268 |
|
|
* second condition would probably indicate bad user input or the
|
269 |
|
|
* touchpanel device returning bad data.
|
270 |
|
|
*/
|
271 |
|
|
|
272 |
|
|
if (q == 0)
|
273 |
|
|
return -1;
|
274 |
|
|
|
275 |
|
|
ptc->s = 1 << 16;
|
276 |
|
|
ptc->a = ((t4 * Smx + t5 * Smy + t1 * Sm) / q + 0.5/65536) * ptc->s;
|
277 |
|
|
ptc->b = ((t5 * Smx + t6 * Smy + t2 * Sm) / q + 0.5/65536) * ptc->s;
|
278 |
|
|
ptc->c = ((t1 * Smx + t2 * Smy + t3 * Sm) / q + 0.5/65536) * ptc->s;
|
279 |
|
|
ptc->d = ((t4 * Snx + t5 * Sny + t1 * Sn) / q + 0.5/65536) * ptc->s;
|
280 |
|
|
ptc->e = ((t5 * Snx + t6 * Sny + t2 * Sn) / q + 0.5/65536) * ptc->s;
|
281 |
|
|
ptc->f = ((t1 * Snx + t2 * Sny + t3 * Sn) / q + 0.5/65536) * ptc->s;
|
282 |
|
|
|
283 |
|
|
/*
|
284 |
|
|
* Finally, we check for overflow on the fp to integer conversion,
|
285 |
|
|
* which would also probably indicate bad data.
|
286 |
|
|
*/
|
287 |
|
|
|
288 |
|
|
if ( (ptc->a == 0x80000000) || (ptc->b == 0x80000000) ||
|
289 |
|
|
(ptc->c == 0x80000000) || (ptc->d == 0x80000000) ||
|
290 |
|
|
(ptc->e == 0x80000000) || (ptc->f == 0x80000000) )
|
291 |
|
|
return -1;
|
292 |
|
|
|
293 |
|
|
return 0;
|
294 |
|
|
}
|
295 |
|
|
|