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.
21 #include "gstsparrow.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
);
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
);
74 debug_map_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
75 guint32
*map_lut
= sparrow
->map_lut
;
77 debug_frame(sparrow
, (guint8
*)map_lut
, sparrow
->out
.width
, sparrow
->out
.height
, PIXSIZE
);
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))
88 coord_to_int_clamp(coord_t x
, const int max_plus_one
){
91 if (x
>= max_plus_one
- 1.5)
92 return max_plus_one
- 1;
93 return (int)(x
+ 0.5);
97 coord_to_int_clamp_dither(sparrow_find_lines_t
*fl
, coord_t x
,
98 const int max_plus_one
, const int i
){
102 if (x
>= max_plus_one
)
103 return max_plus_one
- 1;
109 coord_in_range(coord_t x
, const int max_plus_one
){
110 return x
>= 0 && (x
+ 0.5 < max_plus_one
);
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))
120 coord_to_int_clamp(coord_t x
, const int max_plus_one
){
123 x
>>= SPARROW_FIXED_POINT
;
124 if (x
>= max_plus_one
)
125 return max_plus_one
- 1;
130 coord_in_range(coord_t x
, const int max_plus_one
){
131 return x
>= 0 && (x
< max_plus_one
<< SPARROW_FIXED_POINT
);
136 //these ones are common
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
);
144 #define C2I COORD_TO_INT
145 #define C2F COORD_TO_FLOAT
147 /********************************************/
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
]){
174 ix
+= mesh_square
->dxr
;
175 iy
+= mesh_square
->dyr
;
182 sparrow
->map_lut
= map_lut
;
183 debug_map_lut(sparrow
, fl
);
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
];
202 for (int j
= 1; j
< LINE_PERIOD
; j
+= 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
);
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
;
227 guint32 colours
[4] = {0xff0000ff, 0x0000ff00, 0x00ff0000,
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 */
249 make_clusters(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
250 sparrow_cluster_t
*clusters
= fl
->clusters
;
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
)){
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
);
273 sparrow_cluster_t
*cluster
= &clusters
[hline
* fl
->n_vlines
+ vline
];
274 sparrow_voter_t
*voters
= cluster
->voters
;
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
);
281 GST_WARNING("signal at %p (%d, %d) is %d following quantisation!\n",
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
;
292 /*duplicate x, y, signal, so they aren't mucked up */
296 /*replaced one ends up here */
300 for (int j
= 0; j
< CLUSTER_SIZE
; j
++){
301 if (voters
[j
].signal
< ts
){
302 ts2
= voters
[j
].signal
;
305 voters
[j
].signal
= ts
;
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
);
320 debug_clusters(sparrow
, fl
);
326 drop_cluster_voter(sparrow_voter_t
*voters
, int n
, int k
)
331 for (i
= k
; i
< n
; i
++){
332 voters
[i
] = voters
[i
+ 1];
338 static inline int sort_median(coord_t
*a
, guint n
)
341 /*stupid sort, but n is very small*/
342 for (i
= 0; i
< n
; i
++){
343 for (j
= i
+ 1; j
< n
; j
++){
351 guint middle
= n
/ 2;
352 coord_t answer
= a
[middle
];
355 answer
+= a
[middle
- 1];
361 #define EUCLIDEAN_D2(ax, ay, bx, by)((ax - bx) * (ax - bx) + (ay - by) * (ay - by))
362 #define EUCLIDEAN_THRESHOLD 7
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
);
373 for (i
= 0; i
< n
; i
++){
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
);
384 coord_t worst_d
, threshold
;
386 threshold
= EUCLIDEAN_THRESHOLD
* n
;
389 for (i
= 0; i
< n
; i
++){
390 if (dsums
[i
] > worst_d
){
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
);
406 GST_DEBUG("worst %d, was only %d, threshold %d\n",
407 worst_i
, C2I(worst_d
), C2I(threshold
));
415 median_discard_cluster_outliers(sparrow_voter_t
*voters
, int n
)
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
);
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
;
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){
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
);
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
);
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
468 coord_sum_t xsum
, ysum
;
469 coord_t xmean
, ymean
;
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
;
481 xmean
= xsum
/ votes
;
482 ymean
= ysum
/ votes
;
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
));
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. */
506 sparrow_point_t result
;
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
);
520 static const sparrow_estimator_t base_estimators
[] = {
535 #define BASE_ESTIMATORS (sizeof(base_estimators) / sizeof(sparrow_estimator_t))
536 #define ESTIMATORS (BASE_ESTIMATORS * 4)
539 calculate_estimator_tables(sparrow_estimator_t
*estimators
){
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
];
561 GST_DEBUG("estimator: %-d,%-d %-d,%-d %-d,%-d",
562 e
->x1
, e
->y1
, e
->x2
, e
->y2
, e
->x3
, e
->y3
);
566 return e
- estimators
;
570 /*the map made above is likely to be full of errors. Fix them, and add in
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
;
580 debug
= (guint32
*)fl
->debug
->imageData
;
581 memset(debug
, 0, sparrow
->in
.size
);
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;
595 memcpy(mesh_next
, mesh
, width
* height
* sizeof(sparrow_corner_t
));
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
){
602 GST_DEBUG("ignoring settled corner %d, %d", x
, y
);
606 for (guint j
= 0; j
< est_count
; j
++){
607 sparrow_estimator_t
*e
= &estimators
[j
];
608 int x3
, y3
, x2
, y2
, x1
, y1
;
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
);
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
);
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
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)
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
);
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
);
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"
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
));
713 GST_LOG("estimator %d,%d SUCCESSFULLY estimated that %d, %d will be %d, %d",
714 x1
, x2
, x
, y
, C2I(ex
), C2I(ey
));
719 debug
[coords_to_index(ex
, ey
, sparrow
->in
.width
, sparrow
->in
.height
)] = 0x00aa7700;
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
);
733 /*now find median values. If the number is even, add a copy of either
734 the original value, or a random element. */
736 if (corner
->status
!= CORNER_UNUSED
){
737 estimates
[k
].x
= corner
->x
;
738 estimates
[k
].y
= corner
->y
;
741 int r
= RANDINT(sparrow
, 0, r
);
742 estimates
[k
].x
= estimates
[r
].x
;
743 estimates
[k
].y
= estimates
[r
].y
;
747 sparrow_point_t centre
= median_centre(estimates
, k
);
752 k
= euclidean_discard_cluster_outliers(estimates
, k
);
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.*/
763 for (int j
= 0; j
< k
; j
++){
764 sumx
+= estimates
[j
].x
;
765 sumy
+= estimates
[j
].y
;
771 GST_INFO("estimating %d,%d", C2I(guess_x
), C2I(guess_y
));
773 if (corner
->status
== CORNER_EXACT
){
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
){
783 corner
->status
= CORNER_SETTLED
;
784 GST_INFO("using exact reading %0.3f, %0.3f", C2F(corner
->x
), C2F(corner
->y
));
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",
795 corner
->status
= CORNER_PROJECTED
;
797 debug
[coords_to_index(guess_x
, guess_y
,
798 sparrow
->in
.width
, sparrow
->in
.height
)] = 0xff0000ff;
802 GST_DEBUG("corner %d, %d is SETTLED", x
, y
);
803 corner
->status
= CORNER_SETTLED
;
806 debug
[coords_to_index(guess_x
, guess_y
,
807 sparrow
->in
.width
, sparrow
->in
.height
)] = 0xffffffff;
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
){
819 prev_settled
= settled
;
820 sparrow_corner_t
*tmp
= mesh_next
;
825 fl
->mesh_next
= mesh_next
;
826 MAYBE_DEBUG_IPL(fl
->debug
);
831 calculate_deltas(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
833 int width
= fl
->n_vlines
;
834 int height
= fl
->n_hlines
;
835 sparrow_corner_t
*mesh
= fl
->mesh
;
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
);
865 debug_corners_image(sparrow
, fl
);
871 look_for_line(GstSparrow
*sparrow
, guint8
*in
, sparrow_find_lines_t
*fl
,
872 sparrow_line_t
*line
){
875 guint32 cmask
= sparrow
->out
.colours
[sparrow
->colour
];
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
));
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
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;
908 fl
->map
[i
].lines
[line
->dir
] = line
->index
;
909 fl
->map
[i
].signal
[line
->dir
] = signal
;
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) */
930 draw_line(GstSparrow
* sparrow
, sparrow_line_t
*line
, guint8
*out
){
931 guint32
*p
= (guint32
*)out
;
932 guint32 colour
= sparrow
->out
.colours
[sparrow
->colour
];
934 if (line
->dir
== SPARROW_HORIZONTAL
){
935 p
+= line
->offset
* sparrow
->out
.width
;
936 for (i
= 0; i
< sparrow
->out
.width
; i
++){
941 guint32
*p
= (guint32
*)out
;
943 for(i
= 0; i
< sparrow
->out
.height
; i
++){
945 p
+= sparrow
->out
.width
;
951 jump_state(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, edges_state_t state
){
952 if (state
== EDGES_NEXT_STATE
){
959 case EDGES_FIND_NOISE
:
960 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
+ CAMERA_ADJUST_TIME
;
962 case EDGES_FIND_LINES
:
963 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
965 case EDGES_FIND_CORNERS
:
966 sparrow
->countdown
= 7;
968 case EDGES_WAIT_FOR_PLAY
:
969 global_number_of_edge_finders
--;
970 sparrow
->countdown
= 300;
973 GST_DEBUG("jumped to non-existent state %d\n", fl
->state
);
978 /* show each line for 2 frames, then wait sparrow->lag frames, leaving line on
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
);
991 /*show nothing, look for result */
992 look_for_line(sparrow
, in
, fl
, line
);
994 debug_map_image(sparrow
, fl
);
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
);
1004 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
1009 #define LINE_THRESHOLD 32
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 */
1029 find_corners(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
)
1031 sparrow
->countdown
--;
1032 switch(sparrow
->countdown
){
1034 make_clusters(sparrow
, fl
);
1037 make_corners(sparrow
, fl
);
1040 complete_map(sparrow
, fl
);
1043 calculate_deltas(sparrow
, fl
);
1046 corners_to_full_lut(sparrow
, fl
);
1047 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
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*/
1058 wait_for_play(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
1059 if (global_number_of_edge_finders
== 0 ||
1060 sparrow
->countdown
== 0){
1063 sparrow
->countdown
--;
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
;
1085 case EDGES_FIND_NOISE
:
1086 find_threshold(sparrow
, fl
, in
, out
);
1088 case EDGES_WAIT_FOR_LINES_LOCK
:
1089 wait_for_lines_lock(sparrow
, fl
, out
);
1091 case EDGES_FIND_LINES
:
1092 draw_lines(sparrow
, fl
, in
, out
);
1094 case EDGES_FIND_CORNERS
:
1095 memset(out
, 0, sparrow
->out
.size
);
1096 find_corners(sparrow
, fl
);
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
;
1105 GST_WARNING("strange state in mode_find_edges: %d", fl
->state
);
1106 memset(out
, 0, sparrow
->out
.size
);
1108 return SPARROW_STATUS_QUO
;
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
);
1123 free(fl
->shuffled_lines
);
1128 cvReleaseImage(&fl
->threshold
);
1129 cvReleaseImage(&fl
->working
);
1130 cvReleaseImageHeader(&fl
->input
);
1132 GST_DEBUG("freed everything\n");
1133 sparrow
->helper_struct
= NULL
;
1137 setup_colour_shifts(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
1138 /*COLOUR_QUANT reduces the signal a little bit more, avoiding overflow
1140 switch (sparrow
->colour
){
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
);
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
);
1156 init_find_edges(GstSparrow
*sparrow
){
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
;
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
;
1196 //GST_DEBUG("line %d h has offset %d\n", i, offset);
1199 /*now add the vertical lines */
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
;
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
);
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
);
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
);
1249 jump_state(sparrow
, fl
, EDGES_FIND_NOISE
);
1252 global_number_of_edge_finders
++;