expanding README: copyright
[sparrow.git] / edges.c
blob4a80b77b7e82af953cf5205b7b5098e10189086f
1 /* Copyright (C) <2010> Douglas Bagnall <douglas@halo.gen.nz>
3 * This library is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Library General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) any later version.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Library General Public License for more details.
13 * You should have received a copy of the GNU Library General Public
14 * License along with this library; if not, write to the
15 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 * Boston, MA 02111-1307, USA.
20 #include "sparrow.h"
21 #include "gstsparrow.h"
22 #include "edges.h"
24 #include <string.h>
25 #include <math.h>
26 #include <unistd.h>
28 #include "cv.h"
29 #include "median.h"
31 static GStaticMutex serial_mutex = G_STATIC_MUTEX_INIT;
33 static int global_number_of_edge_finders = 0;
35 static void dump_edges_info(GstSparrow *sparrow, sparrow_find_lines_t *fl, const char *filename){
36 GST_DEBUG("about to save to %s\n", filename);
37 FILE *f = fopen(filename, "w");
38 sparrow_fl_condensed_t condensed;
39 condensed.n_vlines = fl->n_vlines;
40 condensed.n_hlines = fl->n_hlines;
42 /* simply write fl, map, clusters and mesh in sequence */
43 GST_DEBUG("fl is %p, file is %p\n", fl, f);
44 GST_DEBUG("fl: %d x %d\n", sizeof(sparrow_find_lines_t), 1);
45 fwrite(&condensed, sizeof(sparrow_fl_condensed_t), 1, f);
46 GST_DEBUG("fl->map %d x %d\n", sizeof(sparrow_intersect_t), sparrow->in.pixcount);
47 fwrite(fl->map, sizeof(sparrow_intersect_t), sparrow->in.pixcount, f);
48 GST_DEBUG("fl->clusters %d x %d\n", sizeof(sparrow_cluster_t), fl->n_hlines * fl->n_vlines);
49 fwrite(fl->clusters, sizeof(sparrow_cluster_t), fl->n_hlines * fl->n_vlines, f);
50 GST_DEBUG("fl->mesh %d x %d\n", sizeof(sparrow_corner_t), fl->n_hlines * fl->n_vlines);
51 fwrite(fl->mesh, sizeof(sparrow_corner_t), fl->n_hlines * fl->n_vlines, f);
52 /*and write the mask too */
53 GST_DEBUG("sparrow->screenmask\n");
54 fwrite(sparrow->screenmask, 1, sparrow->in.pixcount, f);
55 fclose(f);
58 static void read_edges_info(GstSparrow *sparrow, sparrow_find_lines_t *fl, const char *filename){
59 FILE *f = fopen(filename, "r");
60 sparrow_fl_condensed_t condensed;
61 size_t read = fread(&condensed, sizeof(sparrow_fl_condensed_t), 1, f);
62 assert(condensed.n_hlines == fl->n_hlines);
63 assert(condensed.n_vlines == fl->n_vlines);
65 guint n_corners = fl->n_hlines * fl->n_vlines;
66 read += fread(fl->map, sizeof(sparrow_intersect_t), sparrow->in.pixcount, f);
67 read += fread(fl->clusters, sizeof(sparrow_cluster_t), n_corners, f);
68 read += fread(fl->mesh, sizeof(sparrow_corner_t), n_corners, f);
69 read += fread(sparrow->screenmask, 1, sparrow->in.pixcount, f);
70 fclose(f);
73 static void
74 debug_map_lut(GstSparrow *sparrow, sparrow_find_lines_t *fl){
75 guint32 *map_lut = sparrow->map_lut;
76 if (sparrow->debug){
77 debug_frame(sparrow, (guint8*)map_lut, sparrow->out.width, sparrow->out.height, PIXSIZE);
81 #if USE_FLOAT_COORDS
83 #define COORD_TO_INT(x)((int)((x) + 0.5))
84 #define COORD_TO_FLOAT(x)((double)(x))
85 #define INT_TO_COORD(x)((coord_t)(x))
87 static inline int
88 coord_to_int_clamp(coord_t x, const int max_plus_one){
89 if (x < 0)
90 return 0;
91 if (x >= max_plus_one - 1.5)
92 return max_plus_one - 1;
93 return (int)(x + 0.5);
96 static inline int
97 coord_to_int_clamp_dither(sparrow_find_lines_t *fl, coord_t x,
98 const int max_plus_one, const int i){
99 if (x < 0)
100 return 0;
101 x += fl->dither[i];
102 if (x >= max_plus_one)
103 return max_plus_one - 1;
104 return (int)x;
108 static inline int
109 coord_in_range(coord_t x, const int max_plus_one){
110 return x >= 0 && (x + 0.5 < max_plus_one);
113 #else
115 #define COORD_TO_INT(x)((x) / (1 << SPARROW_FIXED_POINT))
116 #define COORD_TO_FLOAT(x)(((double)(x)) / (1 << SPARROW_FIXED_POINT))
117 #define INT_TO_COORD(x)((x) * (1 << SPARROW_FIXED_POINT))
119 static inline int
120 coord_to_int_clamp(coord_t x, const int max_plus_one){
121 if (x < 0)
122 return 0;
123 x >>= SPARROW_FIXED_POINT;
124 if (x >= max_plus_one)
125 return max_plus_one - 1;
126 return x;
129 static inline int
130 coord_in_range(coord_t x, const int max_plus_one){
131 return x >= 0 && (x < max_plus_one << SPARROW_FIXED_POINT);
134 #endif
136 //these ones are common
137 static inline int
138 coords_to_index(coord_t x, coord_t y, int w, int h){
139 int iy = coord_to_int_clamp(y, h);
140 int ix = coord_to_int_clamp(x, w);
141 return iy * w + ix;
144 #define C2I COORD_TO_INT
145 #define C2F COORD_TO_FLOAT
147 /********************************************/
149 static void
150 corners_to_full_lut(GstSparrow *sparrow, sparrow_find_lines_t *fl){
151 DEBUG_FIND_LINES(fl);
152 sparrow_corner_t *mesh = fl->mesh; /*maps regular points in ->out to points in ->in */
153 guint32 *map_lut = sparrow->map_lut;
154 int mesh_w = fl->n_vlines;
155 int mesh_h = fl->n_hlines;
156 int mcy, mmy, mcx, mmx; /*Mesh Corner|Modulus X|Y*/
157 int y = H_LINE_OFFSET;
158 sparrow_corner_t *mesh_row = mesh;
160 for(mcy = 0; mcy < mesh_h - 1; mcy++){
161 for (mmy = 0; mmy < LINE_PERIOD; mmy++, y++){
162 sparrow_corner_t *mesh_square = mesh_row;
163 int i = y * sparrow->out.width + V_LINE_OFFSET;
164 for(mcx = 0; mcx < mesh_w - 1; mcx++){
165 coord_t iy = mesh_square->y + mmy * mesh_square->dyd;
166 coord_t ix = mesh_square->x + mmy * mesh_square->dxd;
167 for (mmx = 0; mmx < LINE_PERIOD; mmx++, i++){
168 int ixx = coord_to_int_clamp_dither(fl, ix, sparrow->in.width, i);
169 int iyy = coord_to_int_clamp_dither(fl, iy, sparrow->in.height, i);
170 guint32 inpos = iyy * sparrow->in.width + ixx;
171 if(sparrow->screenmask[inpos]){
172 map_lut[i] = inpos;
174 ix += mesh_square->dxr;
175 iy += mesh_square->dyr;
177 mesh_square++;
180 mesh_row += mesh_w;
182 sparrow->map_lut = map_lut;
183 debug_map_lut(sparrow, fl);
186 static void
187 debug_corners_image(GstSparrow *sparrow, sparrow_find_lines_t *fl){
188 sparrow_corner_t *mesh = fl->mesh;
189 guint32 *data = (guint32*)fl->debug->imageData;
190 guint w = fl->debug->width;
191 guint h = fl->debug->height;
192 memset(data, 0, sparrow->in.size);
193 guint32 colours[4] = {0xff0000ff, 0x00ff0000, 0x0000ff00, 0xffffffff};
194 for (int i = 0; i < fl->n_vlines * fl->n_hlines; i++){
195 sparrow_corner_t *c = &mesh[i];
196 coord_t x = c->x;
197 coord_t y = c->y;
198 coord_t txr = x;
199 coord_t txd = x;
200 coord_t tyr = y;
201 coord_t tyd = y;
202 for (int j = 1; j < LINE_PERIOD; j+= 2){
203 txr += c->dxr * 2;
204 txd += c->dxd * 2;
205 tyr += c->dyr * 2;
206 tyd += c->dyd * 2;
207 guint hl = coords_to_index(txr, tyr, w, h);
208 data[hl] = 0x88000088;
209 guint vl = coords_to_index(txd, tyd, w, h);
210 data[vl] = 0x00663300;
212 data[coords_to_index(x, y, w, h)] = colours[c->status];
214 MAYBE_DEBUG_IPL(fl->debug);
218 static void
219 debug_clusters(GstSparrow *sparrow, sparrow_find_lines_t *fl){
220 guint32 *data = (guint32*)fl->debug->imageData;
221 memset(data, 0, sparrow->in.size);
222 int width = fl->n_vlines;
223 int height = fl->n_hlines;
224 sparrow_cluster_t *clusters = fl->clusters;
225 int i, j;
226 guint32 colour;
227 guint32 colours[4] = {0xff0000ff, 0x0000ff00, 0x00ff0000,
228 0x00ff00ff};
229 for (i = 0; i < width * height; i++){
230 colour = colours[i % 5];
231 sparrow_voter_t *v = clusters[i].voters;
232 for (j = 0; j < clusters[i].n; j++){
233 data[coords_to_index(v[j].x, v[j].y,
234 sparrow->in.width, sparrow->in.height)] = (colour * (v[j].signal / 2)) / 256;
237 MAYBE_DEBUG_IPL(fl->debug);
241 #define SIGNAL_QUANT 1
243 /*maximum number of pixels in a cluster */
244 #define CLUSTER_SIZE 8
247 /*find map points with common intersection data, and collect them into clusters */
248 static void
249 make_clusters(GstSparrow *sparrow, sparrow_find_lines_t *fl){
250 sparrow_cluster_t *clusters = fl->clusters;
251 int x, y;
252 /*special case: spurious values collect up at 0,0 */
253 fl->map[0].signal[SPARROW_VERTICAL] = 0;
254 fl->map[0].signal[SPARROW_HORIZONTAL] = 0;
255 /*each point in fl->map is in a vertical line, a horizontal line, both, or
256 neither. Only the "both" case matters. */
257 for (y = 0; y < sparrow->in.height; y++){
258 for (x = 0; x < sparrow->in.width; x++){
259 sparrow_intersect_t *p = &fl->map[y * sparrow->in.width + x];
260 guint vsig = p->signal[SPARROW_VERTICAL];
261 guint hsig = p->signal[SPARROW_HORIZONTAL];
262 /*remembering that 0 is valid as a line number, but not as a signal */
263 if (! (vsig && hsig)){
264 continue;
266 /*This one is lobbying for the position of a corner.*/
267 int vline = p->lines[SPARROW_VERTICAL];
268 int hline = p->lines[SPARROW_HORIZONTAL];
269 if (vline == BAD_PIXEL || hline == BAD_PIXEL){
270 GST_DEBUG("ignoring bad pixel %d, %d\n", x, y);
271 continue;
273 sparrow_cluster_t *cluster = &clusters[hline * fl->n_vlines + vline];
274 sparrow_voter_t *voters = cluster->voters;
275 int n = cluster->n;
276 guint signal = (vsig * hsig) / SIGNAL_QUANT;
277 GST_DEBUG("signal at %p (%d, %d): %dv %dh, product %u, lines: %dv %dh\n"
278 "cluster is %p, n is %d\n", p, x, y,
279 vsig, hsig, signal, vline, hline, cluster, n);
280 if (signal == 0){
281 GST_WARNING("signal at %p (%d, %d) is %d following quantisation!\n",
282 p, x, y, signal);
285 if (n < CLUSTER_SIZE){
286 voters[n].x = INT_TO_COORD(x);
287 voters[n].y = INT_TO_COORD(y);
288 voters[n].signal = signal;
289 cluster->n++;
291 else {
292 /*duplicate x, y, signal, so they aren't mucked up */
293 guint ts = signal;
294 coord_t tx = x;
295 coord_t ty = y;
296 /*replaced one ends up here */
297 guint ts2;
298 coord_t tx2;
299 coord_t ty2;
300 for (int j = 0; j < CLUSTER_SIZE; j++){
301 if (voters[j].signal < ts){
302 ts2 = voters[j].signal;
303 tx2 = voters[j].x;
304 ty2 = voters[j].y;
305 voters[j].signal = ts;
306 voters[j].x = tx;
307 voters[j].y = ty;
308 ts = ts2;
309 tx = tx2;
310 ty = ty2;
313 GST_DEBUG("more than %d pixels at cluster for corner %d, %d."
314 "Dropped %u for %u\n",
315 CLUSTER_SIZE, vline, hline, ts2, signal);
319 if (sparrow->debug){
320 debug_clusters(sparrow, fl);
325 static inline int
326 drop_cluster_voter(sparrow_voter_t *voters, int n, int k)
328 int i;
329 if (k < n){
330 n--;
331 for (i = k; i < n; i++){
332 voters[i] = voters[i + 1];
335 return n;
338 static inline int sort_median(coord_t *a, guint n)
340 guint i, j;
341 /*stupid sort, but n is very small*/
342 for (i = 0; i < n; i++){
343 for (j = i + 1; j < n; j++){
344 if (a[i] > a[j]){
345 coord_t tmp = a[j];
346 a[j] = a[i];
347 a[i] = tmp;
351 guint middle = n / 2;
352 coord_t answer = a[middle];
354 if ((n & 1) == 0){
355 answer += a[middle - 1];
356 answer /= 2;
358 return answer;
361 #define EUCLIDEAN_D2(ax, ay, bx, by)((ax - bx) * (ax - bx) + (ay - by) * (ay - by))
362 #define EUCLIDEAN_THRESHOLD 7
364 static inline int
365 euclidean_discard_cluster_outliers(sparrow_voter_t *voters, int n)
367 /* Calculate distance between each pair. Discard points with maximum sum,
368 then recalculate until all are within threshold.
370 GST_DEBUG("cleansing a cluster of size %d using sum of distances", n);
371 int i, j;
372 coord_t dsums[n];
373 for (i = 0; i < n; i++){
374 dsums[i] = 0;
375 for (j = i + 1; j < n; j++){
376 coord_t d = EUCLIDEAN_D2(voters[i].x, voters[i].y,
377 voters[j].x, voters[j].y);
378 dsums[i] += d;
379 dsums[j] += d;
383 int worst_i;
384 coord_t worst_d, threshold;
385 while (n > 1){
386 threshold = EUCLIDEAN_THRESHOLD * n;
387 worst_i = 0;
388 worst_d = 0;
389 for (i = 0; i < n; i++){
390 if (dsums[i] > worst_d){
391 worst_d = dsums[i];
392 worst_i = i;
395 if (worst_d > threshold){
396 GST_DEBUG("failing point %d, distance sq %d, threshold %d\n",
397 worst_i, C2I(worst_d), C2I(threshold));
398 //subtract this one from the sums, or they'll all go
399 for (i = 0; i < n; i++){
400 dsums[i] -= EUCLIDEAN_D2(voters[i].x, voters[i].y,
401 voters[worst_i].x, voters[worst_i].y);
403 n = drop_cluster_voter(voters, n, worst_i);
405 else{
406 GST_DEBUG("worst %d, was only %d, threshold %d\n",
407 worst_i, C2I(worst_d), C2I(threshold));
408 break;
411 return n;
414 static inline int
415 median_discard_cluster_outliers(sparrow_voter_t *voters, int n)
417 coord_t xvals[n];
418 coord_t yvals[n];
419 int i;
420 for (i = 0; i < n; i++){
421 /*XXX could sort here*/
422 xvals[i] = voters[i].x;
423 yvals[i] = voters[i].y;
425 const coord_t xmed = sort_median(xvals, n);
426 const coord_t ymed = sort_median(yvals, n);
428 for (i = 0; i < n; i++){
429 coord_t dx = voters[i].x - xmed;
430 coord_t dy = voters[i].y - ymed;
431 if (dx * dx + dy * dy > OUTLIER_THRESHOLD){
432 n = drop_cluster_voter(voters, n, i);
435 return n;
438 /* */
439 static inline void
440 make_corners(GstSparrow *sparrow, sparrow_find_lines_t *fl){
441 //DEBUG_FIND_LINES(fl);
442 int width = fl->n_vlines;
443 int height = fl->n_hlines;
444 sparrow_cluster_t *clusters = fl->clusters;
445 sparrow_corner_t *mesh = fl->mesh;
446 int x, y, i;
448 i = 0;
449 for (y = 0; y < height; y++){
450 for (x = 0; x < width; x++, i++){
451 sparrow_cluster_t *cluster = clusters + i;
452 if (cluster->n == 0){
453 continue;
455 #if 1
456 /*discard outliers based on sum of squared distances: good points should
457 be in a cluster, and have lowest sum*/
458 cluster->n = euclidean_discard_cluster_outliers(cluster->voters, cluster->n);
459 #else
460 /*discard values away from median x, y values.
461 (each dimension is calculated independently)*/
462 cluster->n = median_discard_cluster_outliers(cluster->voters, cluster->n);
463 #endif
464 /* now find a weighted average position */
465 /*With int coord_t, coord_sum_t is
466 64 bit to avoid overflow -- should probably just use floating point
467 (or reduce signal)*/
468 coord_sum_t xsum, ysum;
469 coord_t xmean, ymean;
470 guint64 votes;
471 int j;
472 xsum = 0;
473 ysum = 0;
474 votes = 0;
475 for (j = 0; j < cluster->n; j++){
476 votes += cluster->voters[j].signal;
477 ysum += cluster->voters[j].y * cluster->voters[j].signal;
478 xsum += cluster->voters[j].x * cluster->voters[j].signal;
480 if (votes){
481 xmean = xsum / votes;
482 ymean = ysum / votes;
484 else {
485 GST_WARNING("corner %d, %d voters, sum %d,%d, somehow has no votes\n",
486 i, cluster->n, xsum, ysum);
489 GST_DEBUG("corner %d: %d voters, %d votes, sum %d,%d, mean %d,%d\n",
490 i, cluster->n, votes, C2I(xsum), C2I(ysum), C2I(xmean), C2I(ymean));
492 mesh[i].x = xmean;
493 mesh[i].y = ymean;
494 mesh[i].status = CORNER_EXACT;
495 GST_DEBUG("found corner %d at (%3f, %3f)\n",
496 i, COORD_TO_FLOAT(xmean), COORD_TO_FLOAT(ymean));
501 static sparrow_point_t
502 median_centre(sparrow_voter_t *estimates, int n){
503 /*X and Y arevcalculated independently, which is really not right.
504 on the other hand, it probably works. */
505 int i;
506 sparrow_point_t result;
507 coord_t vals[n];
508 for (i = 0; i < n; i++){
509 vals[i] = estimates[i].x;
511 result.x = coord_median(vals, n);
513 for (i = 0; i < n; i++){
514 vals[i] = estimates[i].y;
516 result.y = coord_median(vals, n);
517 return result;
520 static const sparrow_estimator_t base_estimators[] = {
521 { 0, 1, 0, 2, 0, 3},
522 { 0, 2, 0, 4, 0, 6},
523 { 1, 0, 2, 0, 3, 0},
524 { 1, 1, 2, 2, 3, 3},
525 { 1, 2, 2, 4, 3, 6},
526 { 1, 3, 2, 6, 3, 9},
527 { 2, 0, 4, 0, 6, 0},
528 { 2, 1, 4, 2, 6, 3},
529 { 2, 2, 4, 4, 6, 6},
530 { 2, 3, 4, 6, 6, 9},
531 { 3, 1, 6, 2, 9, 3},
532 { 3, 2, 6, 4, 9, 6},
535 #define BASE_ESTIMATORS (sizeof(base_estimators) / sizeof(sparrow_estimator_t))
536 #define ESTIMATORS (BASE_ESTIMATORS * 4)
538 static inline guint
539 calculate_estimator_tables(sparrow_estimator_t *estimators){
540 guint i, j;
541 sparrow_estimator_t *e = estimators;
542 for (i = 0; i < BASE_ESTIMATORS; i++){
543 for (j = 0; j < 4; j++){
544 *e = base_estimators[i];
545 if (j & 1){
546 if (! e->x1){
547 continue;
549 e->x1 = -e->x1;
550 e->x2 = -e->x2;
551 e->x3 = -e->x3;
553 if (j & 2){
554 if (! e->y1){
555 continue;
557 e->y1 = -e->y1;
558 e->y2 = -e->y2;
559 e->y3 = -e->y3;
561 GST_DEBUG("estimator: %-d,%-d %-d,%-d %-d,%-d",
562 e->x1, e->y1, e->x2, e->y2, e->x3, e->y3);
563 e++;
566 return e - estimators;
570 /*the map made above is likely to be full of errors. Fix them, and add in
571 missing points */
572 static void
573 complete_map(GstSparrow *sparrow, sparrow_find_lines_t *fl){
574 sparrow_voter_t estimates[ESTIMATORS + 1]; /* 1 extra for trick simplifying median */
575 sparrow_estimator_t estimators[ESTIMATORS];
576 guint est_count = calculate_estimator_tables(estimators);
577 GST_DEBUG("made %d estimators", est_count);
578 guint32 *debug = NULL;
579 if (sparrow->debug){
580 debug = (guint32*)fl->debug->imageData;
581 memset(debug, 0, sparrow->in.size);
584 int x, y;
585 int width = fl->n_vlines;
586 int height = fl->n_hlines;
587 int screen_width = sparrow->in.width;
588 int screen_height = sparrow->in.height;
589 sparrow_corner_t *mesh = fl->mesh;
590 sparrow_corner_t *mesh_next = fl->mesh_next;
592 memset(estimates, 0, sizeof(estimates)); /*just for clarity in debugging */
593 int prev_settled = 0;
594 while (1){
595 memcpy(mesh_next, mesh, width * height * sizeof(sparrow_corner_t));
596 int settled = 0;
597 for (y = 0; y < height; y++){
598 for (x = 0; x < width; x++){
599 sparrow_corner_t *corner = &mesh[y * width + x];
600 if (corner->status == CORNER_SETTLED){
601 settled ++;
602 GST_DEBUG("ignoring settled corner %d, %d", x, y);
603 continue;
605 int k = 0;
606 for (guint j = 0; j < est_count; j++){
607 sparrow_estimator_t *e = &estimators[j];
608 int x3, y3, x2, y2, x1, y1;
609 y3 = y + e->y3;
610 x3 = x + e->x3;
611 if (!(y3 >= 0 && y3 < height &&
612 x3 >= 0 && x3 < width &&
613 mesh[y3 * width + x3].status != CORNER_UNUSED
615 GST_DEBUG("not using estimator %d because corners aren't used, or are off screen\n"
616 "x3 %d, y3 %d", j, x3, y3);
617 continue;
619 y2 = y + e->y2;
620 x2 = x + e->x2;
621 y1 = y + e->y1;
622 x1 = x + e->x1;
623 if (mesh[y2 * width + x2].status == CORNER_UNUSED ||
624 mesh[y1 * width + x1].status == CORNER_UNUSED){
625 GST_DEBUG("not using estimator %d because corners aren't used", j);
626 continue;
628 /*there are 3 points, and the unknown one.
629 They should all be in a line.
630 The ratio of the p3-p2:p2-p1 sould be the same as
631 p2-p1:p1:p0.
633 This really has to be done in floating point.
635 collinearity, no division, but no useful error metric
636 x[0] * (y[1]-y[2]) + x[1] * (y[2]-y[0]) + x[2] * (y[0]-y[1]) == 0
637 (at least not without further division)
639 This way:
641 cos angle = dot product / product of euclidean lengths
643 (dx12 * dx23 + dy12 * dy23) /
644 (sqrt(dx12 * dx12 + dy12 * dy12) * sqrt(dx23 * dx23 + dy23 * dy23))
646 is costly up front (sqrt), but those distances need to be
647 calculated anyway (or at least they are handy). Not much gained by
648 short-circuiting on bad collinearity, though.
650 It also handlily catches all the division by zeros in one meaningful
653 sparrow_corner_t *c1 = &mesh[y1 * width + x1];
654 sparrow_corner_t *c2 = &mesh[y2 * width + x2];
655 sparrow_corner_t *c3 = &mesh[y3 * width + x3];
657 double dx12 = c1->x - c2->x;
658 double dy12 = c1->y - c2->y;
659 double dx23 = c2->x - c3->x;
660 double dy23 = c2->y - c3->y;
661 double distance12 = sqrt(dx12 * dx12 + dy12 * dy12);
662 double distance23 = sqrt(dx23 * dx23 + dy23 * dy23);
664 double dp = dx12 * dx23 + dy12 * dy23;
666 double distances = distance12 * distance23;
668 GST_LOG("mesh points: %d,%d, %d,%d, %d,%d\n"
669 "map points: %d,%d, %d,%d, %d,%d\n"
670 "diffs: 12: %0.3f,%0.3f, 23: %0.3f,%0.3f, \n"
671 "distances: 12: %0.3f, 32: %0.3f\n",
672 x1, y1, x2, y2, x3, y3,
673 C2I(c1->x), C2I(c1->y), C2I(c2->x), C2I(c2->y), C2I(c3->x), C2I(c3->y),
674 dx12, dy12, dx23, dy23, distance12, distance23
677 if (distances == 0.0){
678 GST_INFO("at least two points out of %d,%d, %d,%d, %d,%d are the same!",
679 x1, y1, x2, y2, x3, y3);
680 continue;
682 double line_error = 1.0 - dp / distances;
683 if (line_error > MAX_NONCOLLINEARITY){
684 GST_DEBUG("Points %d,%d, %d,%d, %d,%d are not in a line: non-collinearity: %3f",
685 x1, y1, x2, y2, x3, y3, line_error);
686 continue;
688 GST_LOG("GOOD collinearity: %3f", line_error);
691 double ratio = distance12 / distance23;
692 /*so here's the estimate!*/
693 coord_t dx = dx12 * ratio;
694 coord_t dy = dy12 * ratio;
695 coord_t ex = c1->x + dx;
696 coord_t ey = c1->y + dy;
698 GST_LOG("dx, dy: %d,%d, ex, ey: %d,%d\n"
699 "dx raw: %0.3f,%0.3f, x1, x2: %0.3f,%0.3f,\n"
700 "distances: 12: %0.3f, 32: %0.3f\n"
701 "ratio: %0.3f\n",
702 C2I(dx), C2I(dy), C2I(ex), C2I(ey),
703 dx, dy, ex, ey, ratio
706 if (! coord_in_range(ey, screen_height) ||
707 ! coord_in_range(ex, screen_width)){
708 GST_DEBUG("rejecting estimate for %d, %d, due to ex, ey being %d, %d",
709 x, y, C2I(ex), C2I(ey));
710 continue;
713 GST_LOG("estimator %d,%d SUCCESSFULLY estimated that %d, %d will be %d, %d",
714 x1, x2, x, y, C2I(ex), C2I(ey));
716 estimates[k].x = ex;
717 estimates[k].y = ey;
718 if (sparrow->debug){
719 debug[coords_to_index(ex, ey, sparrow->in.width, sparrow->in.height)] = 0x00aa7700;
721 k++;
723 /*now there is an array of estimates.
724 The *_discard_cluster_outliers functions should fit here */
725 GST_INFO("got %d estimates for %d,%d", k, x, y);
726 if(! k){
727 continue;
729 coord_t guess_x;
730 coord_t guess_y;
732 #if 1
733 /*now find median values. If the number is even, add a copy of either
734 the original value, or a random element. */
735 if (! k & 1){
736 if (corner->status != CORNER_UNUSED){
737 estimates[k].x = corner->x;
738 estimates[k].y = corner->y;
740 else {
741 int r = RANDINT(sparrow, 0, r);
742 estimates[k].x = estimates[r].x;
743 estimates[k].y = estimates[r].y;
745 k++;
747 sparrow_point_t centre = median_centre(estimates, k);
748 guess_x = centre.x;
749 guess_y = centre.y;
751 #else
752 k = euclidean_discard_cluster_outliers(estimates, k);
753 if (sparrow->debug){
754 for (int j = 0; j < k; j++){
755 debug[coords_to_index(estimates[j].x, estimates[j].y,
756 sparrow->in.width, sparrow->in.height)] = 0x00ffff00;
759 GST_INFO("After discard, left with %d estimates", k);
760 /*now what? the mean? yes.*/
761 coord_t sumx = 0;
762 coord_t sumy = 0;
763 for (int j = 0; j < k; j++){
764 sumx += estimates[j].x;
765 sumy += estimates[j].y;
767 guess_x = sumx / k;
768 guess_y = sumy / k;
769 #endif
771 GST_INFO("estimating %d,%d", C2I(guess_x), C2I(guess_y));
773 if (corner->status == CORNER_EXACT){
774 if (sparrow->debug){
775 debug[coords_to_index(corner->x, corner->y,
776 sparrow->in.width, sparrow->in.height)] = 0xffff3300;
778 if ((guess_x - corner->x) * (guess_x - corner->x) +
779 (guess_y - corner->y) * (guess_y - corner->y)
780 < CORNER_EXACT_THRESHOLD){
781 guess_x = corner->x;
782 guess_y = corner->y;
783 corner->status = CORNER_SETTLED;
784 GST_INFO("using exact reading %0.3f, %0.3f", C2F(corner->x), C2F(corner->y));
786 else{
787 GST_INFO("REJECTING exact reading %0.3f,%0.3f: too far from median %0.3f,%0.3f",
788 C2F(corner->x), C2F(corner->y), C2F(corner->x), C2F(corner->y));
789 corner->status = CORNER_PROJECTED;
792 else if (k < MIN_CORNER_ESTIMATES){
793 GST_INFO("weak evidence (%d estimates) for corner %d,%d, marking it PROJECTED",
794 k, x, y);
795 corner->status = CORNER_PROJECTED;
796 if (sparrow->debug){
797 debug[coords_to_index(guess_x, guess_y,
798 sparrow->in.width, sparrow->in.height)] = 0xff0000ff;
801 else{
802 GST_DEBUG("corner %d, %d is SETTLED", x, y);
803 corner->status = CORNER_SETTLED;
804 settled ++;
805 if (sparrow->debug){
806 debug[coords_to_index(guess_x, guess_y,
807 sparrow->in.width, sparrow->in.height)] = 0xffffffff;
810 corner->x = guess_x;
811 corner->y = guess_y;
814 GST_INFO("settled %d in that round. %d left to go",
815 settled - prev_settled, width * height - settled);
816 if (settled == width * height || settled == prev_settled){
817 break;
819 prev_settled = settled;
820 sparrow_corner_t *tmp = mesh_next;
821 mesh_next = mesh;
822 mesh = tmp;
824 fl->mesh = mesh;
825 fl->mesh_next = mesh_next;
826 MAYBE_DEBUG_IPL(fl->debug);
830 static void
831 calculate_deltas(GstSparrow *sparrow, sparrow_find_lines_t *fl){
832 int i;
833 int width = fl->n_vlines;
834 int height = fl->n_hlines;
835 sparrow_corner_t *mesh = fl->mesh;
836 gint x, y;
838 //DEBUG_FIND_LINES(fl);
839 /* calculate deltas toward adjacent corners */
840 /* try to extrapolate left and up, if possible, so need to go backwards. */
841 i = width * height - 1;
842 for (y = height - 1; y >= 0; y--){
843 for (x = width - 1; x >= 0; x--, i--){
844 sparrow_corner_t *corner = &mesh[i];
845 /* calculate the delta to next corner. If this corner is on edge, delta is
846 0 and next is this.*/
847 sparrow_corner_t *right = (x == width - 1) ? corner : corner + 1;
848 sparrow_corner_t *down = (y == height - 1) ? corner : corner + width;
849 GST_DEBUG("i %d xy %d,%d width %d. in_xy %d,%d; down in_xy %d,%d; right in_xy %d,%d\n",
850 i, x, y, width, C2I(corner->x), C2I(corner->y), C2I(down->x),
851 C2I(down->y), C2I(right->x), C2I(right->y));
852 if (corner->status != CORNER_UNUSED){
853 if (right->status != CORNER_UNUSED){
854 corner->dxr = QUANTISE_DELTA(right->x - corner->x);
855 corner->dyr = QUANTISE_DELTA(right->y - corner->y);
857 if (down->status != CORNER_UNUSED){
858 corner->dxd = QUANTISE_DELTA(down->x - corner->x);
859 corner->dyd = QUANTISE_DELTA(down->y - corner->y);
864 if (sparrow->debug){
865 debug_corners_image(sparrow, fl);
870 static void
871 look_for_line(GstSparrow *sparrow, guint8 *in, sparrow_find_lines_t *fl,
872 sparrow_line_t *line){
873 guint i;
874 guint32 colour;
875 guint32 cmask = sparrow->out.colours[sparrow->colour];
876 int signal;
878 /* subtract background noise */
879 fl->input->imageData = (char *)in;
880 cvSub(fl->input, fl->threshold, fl->working, NULL);
881 guint32 *in32 = (guint32 *)fl->working->imageData;
883 for (i = 0; i < sparrow->in.pixcount; i++){
884 colour = in32[i] & cmask;
885 signal = (((colour >> fl->shift1) & COLOUR_MASK) +
886 ((colour >> fl->shift2) & COLOUR_MASK));
888 if (signal){
889 if (fl->map[i].lines[line->dir] &&
890 signal < 2 * fl->map[i].signal[line->dir]){
891 if (fl->map[i].lines[line->dir] != BAD_PIXEL &&
892 signal * 2 < fl->map[i].signal[line->dir]){
893 /*assume the pixel is on for everyone and will just confuse
894 matters. ignore it.
897 GST_DEBUG("HEY, expected point %d to be in line %d (dir %d) "
898 "and thus empty, but it is also in line %d\n"
899 "old signal %d, new signal %d, marking as BAD\n",
900 i, line->index, line->dir, fl->map[i].lines[line->dir],
901 fl->map[i].signal[line->dir], signal);
903 fl->map[i].lines[line->dir] = BAD_PIXEL;
904 fl->map[i].signal[line->dir] = 0;
907 else{
908 fl->map[i].lines[line->dir] = line->index;
909 fl->map[i].signal[line->dir] = signal;
915 static void
916 debug_map_image(GstSparrow *sparrow, sparrow_find_lines_t *fl){
917 guint32 *data = (guint32*)fl->debug->imageData;
918 memset(data, 0, sparrow->in.size);
919 for (guint i = 0; i < sparrow->in.pixcount; i++){
920 data[i] |= fl->map[i].signal[SPARROW_HORIZONTAL] << sparrow->in.gshift;
921 data[i] |= fl->map[i].signal[SPARROW_VERTICAL] << sparrow->in.rshift;
922 data[i] |= ((fl->map[i].lines[SPARROW_VERTICAL] == BAD_PIXEL) ||
923 (fl->map[i].lines[SPARROW_HORIZONTAL] == BAD_PIXEL)) ? 255 << sparrow->in.bshift : 0;
925 MAYBE_DEBUG_IPL(fl->debug);
928 /* draw the line (in sparrow->colour) */
929 static inline void
930 draw_line(GstSparrow * sparrow, sparrow_line_t *line, guint8 *out){
931 guint32 *p = (guint32 *)out;
932 guint32 colour = sparrow->out.colours[sparrow->colour];
933 int i;
934 if (line->dir == SPARROW_HORIZONTAL){
935 p += line->offset * sparrow->out.width;
936 for (i = 0; i < sparrow->out.width; i++){
937 p[i] = colour;
940 else {
941 guint32 *p = (guint32 *)out;
942 p += line->offset;
943 for(i = 0; i < sparrow->out.height; i++){
944 *p = colour;
945 p += sparrow->out.width;
950 static void
951 jump_state(GstSparrow *sparrow, sparrow_find_lines_t *fl, edges_state_t state){
952 if (state == EDGES_NEXT_STATE){
953 fl->state++;
955 else {
956 fl->state = state;
958 switch (fl->state){
959 case EDGES_FIND_NOISE:
960 sparrow->countdown = MAX(sparrow->lag, 1) + SAFETY_LAG + CAMERA_ADJUST_TIME;
961 break;
962 case EDGES_FIND_LINES:
963 sparrow->countdown = MAX(sparrow->lag, 1) + SAFETY_LAG;
964 break;
965 case EDGES_FIND_CORNERS:
966 sparrow->countdown = 7;
967 break;
968 case EDGES_WAIT_FOR_PLAY:
969 global_number_of_edge_finders--;
970 sparrow->countdown = 300;
971 break;
972 default:
973 GST_DEBUG("jumped to non-existent state %d\n", fl->state);
974 break;
978 /* show each line for 2 frames, then wait sparrow->lag frames, leaving line on
979 until last one.
981 static inline void
982 draw_lines(GstSparrow *sparrow, sparrow_find_lines_t *fl, guint8 *in, guint8 *out)
984 sparrow_line_t *line = fl->shuffled_lines[fl->current];
985 sparrow->countdown--;
986 memset(out, 0, sparrow->out.size);
987 if (sparrow->countdown){
988 draw_line(sparrow, line, out);
990 else{
991 /*show nothing, look for result */
992 look_for_line(sparrow, in, fl, line);
993 if (sparrow->debug){
994 debug_map_image(sparrow, fl);
996 fl->current++;
997 if (fl->current == fl->n_lines){
998 if (sparrow->serial){
999 g_static_mutex_unlock(&serial_mutex);
1001 jump_state(sparrow, fl, EDGES_NEXT_STATE);
1003 else{
1004 sparrow->countdown = MAX(sparrow->lag, 1) + SAFETY_LAG;
1009 #define LINE_THRESHOLD 32
1011 static inline void
1012 find_threshold(GstSparrow *sparrow, sparrow_find_lines_t *fl, guint8 *in, guint8 *out)
1014 memset(out, 0, sparrow->out.size);
1015 /*XXX should average/median over a range of frames */
1016 if (sparrow->countdown == 0){
1017 memcpy(fl->threshold->imageData, in, sparrow->in.size);
1018 /*add a constant, and smooth */
1019 cvAddS(fl->threshold, cvScalarAll(LINE_THRESHOLD), fl->working, NULL);
1020 cvSmooth(fl->working, fl->threshold, CV_GAUSSIAN, 3, 0, 0, 0);
1021 //cvSmooth(fl->working, fl->threshold, CV_MEDIAN, 3, 0, 0, 0);
1022 jump_state(sparrow, fl, EDGES_NEXT_STATE);
1024 sparrow->countdown--;
1027 /*match up lines and find corners */
1028 static inline int
1029 find_corners(GstSparrow *sparrow, sparrow_find_lines_t *fl)
1031 sparrow->countdown--;
1032 switch(sparrow->countdown){
1033 case 4:
1034 make_clusters(sparrow, fl);
1035 break;
1036 case 3:
1037 make_corners(sparrow, fl);
1038 break;
1039 case 2:
1040 complete_map(sparrow, fl);
1041 break;
1042 case 1:
1043 calculate_deltas(sparrow, fl);
1044 break;
1045 case 0:
1046 corners_to_full_lut(sparrow, fl);
1047 jump_state(sparrow, fl, EDGES_NEXT_STATE);
1048 break;
1049 default:
1050 GST_DEBUG("how did sparrow->countdown get to be %d?", sparrow->countdown);
1051 sparrow->countdown = 5;
1053 return sparrow->countdown;
1056 /*use a dirty shared variable*/
1057 static gboolean
1058 wait_for_play(GstSparrow *sparrow, sparrow_find_lines_t *fl){
1059 if (global_number_of_edge_finders == 0 ||
1060 sparrow->countdown == 0){
1061 return TRUE;
1063 sparrow->countdown--;
1064 return FALSE;
1068 static inline void
1069 wait_for_lines_lock(GstSparrow *sparrow, sparrow_find_lines_t *fl, guint8 *out){
1070 memset(out, 0, sparrow->out.size);
1071 if (! sparrow->serial){
1072 jump_state(sparrow, fl, EDGES_NEXT_STATE);
1074 else if (g_static_mutex_trylock(&serial_mutex)){
1075 jump_state(sparrow, fl, EDGES_NEXT_STATE);
1079 INVISIBLE sparrow_state
1080 mode_find_edges(GstSparrow *sparrow, GstBuffer *inbuf, GstBuffer *outbuf){
1081 guint8 *in = GST_BUFFER_DATA(inbuf);
1082 guint8 *out = GST_BUFFER_DATA(outbuf);
1083 sparrow_find_lines_t *fl = (sparrow_find_lines_t *)sparrow->helper_struct;
1084 switch (fl->state){
1085 case EDGES_FIND_NOISE:
1086 find_threshold(sparrow, fl, in, out);
1087 break;
1088 case EDGES_WAIT_FOR_LINES_LOCK:
1089 wait_for_lines_lock(sparrow, fl, out);
1090 break;
1091 case EDGES_FIND_LINES:
1092 draw_lines(sparrow, fl, in, out);
1093 break;
1094 case EDGES_FIND_CORNERS:
1095 memset(out, 0, sparrow->out.size);
1096 find_corners(sparrow, fl);
1097 break;
1098 case EDGES_WAIT_FOR_PLAY:
1099 memset(out, 0, sparrow->out.size);
1100 if (wait_for_play(sparrow, fl)){
1101 return SPARROW_NEXT_STATE;
1103 break;
1104 default:
1105 GST_WARNING("strange state in mode_find_edges: %d", fl->state);
1106 memset(out, 0, sparrow->out.size);
1108 return SPARROW_STATUS_QUO;
1111 INVISIBLE void
1112 finalise_find_edges(GstSparrow *sparrow){
1113 sparrow_find_lines_t *fl = (sparrow_find_lines_t *)sparrow->helper_struct;
1114 //DEBUG_FIND_LINES(fl);
1115 if (sparrow->save && *(sparrow->save)){
1116 GST_DEBUG("about to save to %s\n", sparrow->save);
1117 dump_edges_info(sparrow, fl, sparrow->save);
1119 if (sparrow->debug){
1120 cvReleaseImage(&fl->debug);
1122 free(fl->h_lines);
1123 free(fl->shuffled_lines);
1124 free(fl->map);
1125 free(fl->mesh_mem);
1126 free(fl->clusters);
1127 free(fl->dither);
1128 cvReleaseImage(&fl->threshold);
1129 cvReleaseImage(&fl->working);
1130 cvReleaseImageHeader(&fl->input);
1131 free(fl);
1132 GST_DEBUG("freed everything\n");
1133 sparrow->helper_struct = NULL;
1136 static void
1137 setup_colour_shifts(GstSparrow *sparrow, sparrow_find_lines_t *fl){
1138 /*COLOUR_QUANT reduces the signal a little bit more, avoiding overflow
1139 later */
1140 switch (sparrow->colour){
1141 case SPARROW_WHITE:
1142 case SPARROW_GREEN:
1143 fl->shift1 = sparrow->in.gshift + COLOUR_QUANT;
1144 fl->shift2 = sparrow->in.gshift + COLOUR_QUANT;
1145 GST_DEBUG("using green shift: %d, %d", fl->shift1, fl->shift2);
1146 break;
1147 case SPARROW_MAGENTA:
1148 fl->shift1 = sparrow->in.rshift + COLOUR_QUANT;
1149 fl->shift2 = sparrow->in.bshift + COLOUR_QUANT;
1150 GST_DEBUG("using magenta shift: %d, %d", fl->shift1, fl->shift2);
1151 break;
1155 INVISIBLE void
1156 init_find_edges(GstSparrow *sparrow){
1157 gint i;
1158 sparrow_find_lines_t *fl = zalloc_aligned_or_die(sizeof(sparrow_find_lines_t));
1159 sparrow->helper_struct = (void *)fl;
1161 gint h_lines = (sparrow->out.height + LINE_PERIOD - 1) / LINE_PERIOD;
1162 gint v_lines = (sparrow->out.width + LINE_PERIOD - 1) / LINE_PERIOD;
1163 gint n_lines_max = (h_lines + v_lines);
1164 gint n_corners = (h_lines * v_lines);
1166 /*set up dither here, rather than in the busy time */
1167 fl->dither = malloc_aligned_or_die(sparrow->out.pixcount * sizeof(double));
1168 dsfmt_fill_array_close_open(sparrow->dsfmt, fl->dither, sparrow->out.pixcount);
1170 fl->n_hlines = h_lines;
1171 fl->n_vlines = v_lines;
1173 fl->h_lines = malloc_aligned_or_die(sizeof(sparrow_line_t) * n_lines_max);
1174 fl->shuffled_lines = malloc_aligned_or_die(sizeof(sparrow_line_t *) * n_lines_max);
1175 GST_DEBUG("shuffled lines, malloced %p\n", fl->shuffled_lines);
1177 GST_DEBUG("map is going to be %d * %d \n", sizeof(sparrow_intersect_t), sparrow->in.pixcount);
1178 fl->map = zalloc_aligned_or_die(sizeof(sparrow_intersect_t) * sparrow->in.pixcount);
1179 fl->clusters = zalloc_or_die(n_corners * sizeof(sparrow_cluster_t));
1180 fl->mesh_mem = zalloc_aligned_or_die(n_corners * sizeof(sparrow_corner_t) * 2);
1181 fl->mesh = fl->mesh_mem;
1182 fl->mesh_next = fl->mesh + n_corners;
1184 sparrow_line_t *line = fl->h_lines;
1185 sparrow_line_t **sline = fl->shuffled_lines;
1186 int offset;
1188 for (i = 0, offset = H_LINE_OFFSET; offset < sparrow->out.height;
1189 i++, offset += LINE_PERIOD){
1190 line->offset = offset;
1191 line->dir = SPARROW_HORIZONTAL;
1192 line->index = i;
1193 *sline = line;
1194 line++;
1195 sline++;
1196 //GST_DEBUG("line %d h has offset %d\n", i, offset);
1199 /*now add the vertical lines */
1200 fl->v_lines = line;
1201 for (i = 0, offset = V_LINE_OFFSET; offset < sparrow->out.width;
1202 i++, offset += LINE_PERIOD){
1203 line->offset = offset;
1204 line->dir = SPARROW_VERTICAL;
1205 line->index = i;
1206 *sline = line;
1207 line++;
1208 sline++;
1209 //GST_DEBUG("line %d v has offset %d\n", i, offset);
1211 //DEBUG_FIND_LINES(fl);
1212 fl->n_lines = line - fl->h_lines;
1213 GST_DEBUG("allocated %d lines, made %d\n", n_lines_max, fl->n_lines);
1215 /*now shuffle */
1216 for (i = 0; i < fl->n_lines; i++){
1217 int j = RANDINT(sparrow, i, fl->n_lines);
1218 sparrow_line_t *tmp = fl->shuffled_lines[j];
1219 fl->shuffled_lines[j] = fl->shuffled_lines[i];
1220 fl->shuffled_lines[i] = tmp;
1223 setup_colour_shifts(sparrow, fl);
1225 /* opencv images for threshold finding */
1226 CvSize size = {sparrow->in.width, sparrow->in.height};
1227 fl->working = cvCreateImage(size, IPL_DEPTH_8U, PIXSIZE);
1228 fl->threshold = cvCreateImage(size, IPL_DEPTH_8U, PIXSIZE);
1230 /*input has no data allocated -- it uses latest frame*/
1231 fl->input = init_ipl_image(&sparrow->in, PIXSIZE);
1232 //DEBUG_FIND_LINES(fl);
1233 if (sparrow->debug){
1234 fl->debug = cvCreateImage(size, IPL_DEPTH_8U, PIXSIZE);
1237 if (sparrow->reload){
1238 if (access(sparrow->reload, R_OK)){
1239 GST_DEBUG("sparrow->reload is '%s' and it is UNREADABLE\n", sparrow->reload);
1240 exit(1);
1242 read_edges_info(sparrow, fl, sparrow->reload);
1243 memset(fl->map, 0, sizeof(sparrow_intersect_t) * sparrow->in.pixcount);
1244 //memset(fl->clusters, 0, n_corners * sizeof(sparrow_cluster_t));
1245 memset(fl->mesh, 0, n_corners * sizeof(sparrow_corner_t));
1246 jump_state(sparrow, fl, EDGES_FIND_CORNERS);
1248 else {
1249 jump_state(sparrow, fl, EDGES_FIND_NOISE);
1252 global_number_of_edge_finders++;