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 void dump_edges_info(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, const char *filename
){
32 GST_DEBUG("about to save to %s\n", filename
);
33 FILE *f
= fopen(filename
, "w");
34 sparrow_fl_condensed_t condensed
;
35 condensed
.n_vlines
= fl
->n_vlines
;
36 condensed
.n_hlines
= fl
->n_hlines
;
38 /* simply write fl, map, clusters and mesh in sequence */
39 GST_DEBUG("fl is %p, file is %p\n", fl
, f
);
40 GST_DEBUG("fl: %d x %d\n", sizeof(sparrow_find_lines_t
), 1);
41 fwrite(&condensed
, sizeof(sparrow_fl_condensed_t
), 1, f
);
42 GST_DEBUG("fl->map %d x %d\n", sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
);
43 fwrite(fl
->map
, sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
, f
);
44 GST_DEBUG("fl->clusters %d x %d\n", sizeof(sparrow_cluster_t
), fl
->n_hlines
* fl
->n_vlines
);
45 fwrite(fl
->clusters
, sizeof(sparrow_cluster_t
), fl
->n_hlines
* fl
->n_vlines
, f
);
46 GST_DEBUG("fl->mesh %d x %d\n", sizeof(sparrow_corner_t
), fl
->n_hlines
* fl
->n_vlines
);
47 fwrite(fl
->mesh
, sizeof(sparrow_corner_t
), fl
->n_hlines
* fl
->n_vlines
, f
);
48 /*and write the mask too */
49 GST_DEBUG("sparrow->screenmask\n");
50 fwrite(sparrow
->screenmask
, 1, sparrow
->in
.pixcount
, f
);
54 static void read_edges_info(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, const char *filename
){
55 FILE *f
= fopen(filename
, "r");
56 sparrow_fl_condensed_t condensed
;
57 size_t read
= fread(&condensed
, sizeof(sparrow_fl_condensed_t
), 1, f
);
58 assert(condensed
.n_hlines
== fl
->n_hlines
);
59 assert(condensed
.n_vlines
== fl
->n_vlines
);
61 guint n_corners
= fl
->n_hlines
* fl
->n_vlines
;
62 read
+= fread(fl
->map
, sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
, f
);
63 read
+= fread(fl
->clusters
, sizeof(sparrow_cluster_t
), n_corners
, f
);
64 read
+= fread(fl
->mesh
, sizeof(sparrow_corner_t
), n_corners
, f
);
65 read
+= fread(sparrow
->screenmask
, 1, sparrow
->in
.pixcount
, f
);
72 debug_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
75 #define OFFSET(x, y, w)((((y) * (w)) >> SPARROW_FIXED_POINT) + ((x) >> SPARROW_FIXED_POINT))
77 #define QUANTISE_DELTA(d)(((d) + LINE_PERIOD / 2) / LINE_PERIOD)
79 /*tolerate up to 1/8 of a pixel drift */
80 #define MAX_DRIFT (1 << (SPARROW_FIXED_POINT - 3))
83 static inline sparrow_map_path_t
*
84 possibly_new_point(sparrow_map_path_t
*p
, int dx
, int dy
){
85 if (dx
!= p
->dx
&& dy
!= p
->dy
){
95 corners_to_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
96 //DEBUG_FIND_LINES(fl);
97 sparrow_map_t
*map
= &sparrow
->map
; /*rows in sparrow->out */
98 guint8
*mask
= sparrow
->screenmask
; /*mask in sparrow->in */
99 sparrow_corner_t
*mesh
= fl
->mesh
; /*maps regular points in ->out to points in ->in */
100 int mesh_w
= fl
->n_vlines
;
101 int mesh_h
= fl
->n_hlines
;
102 int in_w
= sparrow
->in
.width
;
103 int mcy
, mmy
, mcx
; /*Mesh Corner|Modulus X|Y*/
106 sparrow_corner_t
*mesh_row
= mesh
;
108 for (y
= 0; y
< H_LINE_OFFSET
; y
++){
109 map
->rows
[y
].start
= 0;
110 map
->rows
[y
].end
= 0;
111 map
->rows
[y
].points
= NULL
;
114 sparrow_map_row_t
*row
= map
->rows
+ H_LINE_OFFSET
;
115 row
->points
= map
->point_mem
;
116 sparrow_map_path_t
*p
= row
->points
;
118 for(mcy
= 0; mcy
< mesh_h
- 1; mcy
++){ /* for each mesh row */
119 for (mmy
= 0; mmy
< LINE_PERIOD
; mmy
++){ /* for each output line */
120 int ix
, iy
; /* input x, y at mesh points, interpolated vertically */
121 int rx
, ry
; /* running x, y; approximates ix, iy */
124 sparrow_corner_t
*mesh_square
= mesh_row
;
125 GST_DEBUG("row %p, y %d, row offset %d\n", row
, y
, row
- map
->rows
);
130 for(mcx
= 0; mcx
< mesh_w
- 1; mcx
++){
131 /*for each mesh block except the last, which has no dx,dy.
132 Thus the mesh blocks are referenced in LINE_PERIOD passes.*/
133 if (mesh_square
->status
== CORNER_UNUSED
){
138 /*lordy! continue with previous deltas*/
143 /* starting point for this row in this block. */
144 iy
= mesh_square
->in_y
+ mmy
* (mesh_square
->dyd
/ 1);
145 ix
= mesh_square
->in_x
+ mmy
* (mesh_square
->dxd
/ 1);
146 /*incremental delta going left to right in this block */
147 dy
= (mesh_square
->dyr
/ 1);
148 dx
= (mesh_square
->dxr
/ 1);
151 /*index of the last point in this block
152 NB: calculating from ix, iy, which may differ slightly from rx, ry*/
154 ix
+ (LINE_PERIOD
- 1) * dx
,
155 iy
+ (LINE_PERIOD
- 1) * dy
,
157 //GST_DEBUG("lasti is %d, ix %d, iy %d\n", lasti, ix, iy);
160 /*it doesn't turn on within this block (or it is of ignorably
165 /*it does turn on. so step through and find it. This happens once
170 for (j
= 0; j
< LINE_PERIOD
; j
++){
171 if (mask
[OFFSET(rx
, ry
, in_w
)]){
177 row
->start
= mcx
* LINE_PERIOD
+ j
;
180 p
= possibly_new_point(p
, dx
, dy
);
182 p
->n
= LINE_PERIOD
- j
;
188 /*maybe rx, ry are drifting badly, in which case, we need to recalculate dx, dy*/
189 if (abs(rx
- ix
) > MAX_DRIFT
||
190 abs(ry
- iy
) > MAX_DRIFT
){
191 int y
= mcy
* LINE_PERIOD
+ mmy
;
192 int x
= mcx
* LINE_PERIOD
;
193 GST_DEBUG("output point %d %d, rx, ry %d, %d have got %d, %d away from target %d, %d."
194 " dx, dy is %d, %d\n",
195 x
, y
, rx
, ry
, rx
- ix
, ry
- iy
, ix
, iy
, dx
, dy
);
196 sparrow_corner_t
*next
= mesh_square
+ 1;
197 if(next
->status
!= CORNER_UNUSED
){
198 int niy
= next
->in_y
+ mmy
* (next
->dyd
/ 1);
199 int nix
= next
->in_x
+ mmy
* (next
->dxd
/ 1);
200 dx
= QUANTISE_DELTA(nix
- ix
);
201 dy
= QUANTISE_DELTA(niy
- iy
);
202 GST_DEBUG("new dx, dy is %d, %d\n", dx
, dy
);
205 GST_DEBUG("next corner is UNUSED. dx, dy unchanged\n");
209 /*Probably dx/dy are different, so we need a new point */
210 p
= possibly_new_point(p
, dx
, dy
);
212 /*does it end it this one? */
215 for (j
= 0; j
< LINE_PERIOD
; j
++){
216 if (! mask
[OFFSET(rx
, ry
, in_w
)]){
223 row
->end
= mcx
* LINE_PERIOD
+ j
;
224 /*this row is done! */
228 rx
+= LINE_PERIOD
* dx
;
229 ry
+= LINE_PERIOD
* dy
;
237 /*blank lines for the last few */
238 for (y
= sparrow
->out
.height
- H_LINE_OFFSET
; y
< sparrow
->out
.height
; y
++){
239 map
->rows
[y
].start
= 0;
240 map
->rows
[y
].end
= 0;
241 map
->rows
[y
].points
= NULL
;
244 debug_lut(sparrow
, fl
);
248 corners_to_full_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
249 DEBUG_FIND_LINES(fl
);
250 sparrow_corner_t
*mesh
= fl
->mesh
; /*maps regular points in ->out to points in ->in */
251 sparrow_map_lut_t
*map_lut
= sparrow
->map_lut
;
252 int mesh_w
= fl
->n_vlines
;
253 int mesh_h
= fl
->n_hlines
;
254 int mcy
, mmy
, mcx
, mmx
; /*Mesh Corner|Modulus X|Y*/
255 int y
= H_LINE_OFFSET
;
256 sparrow_corner_t
*mesh_row
= mesh
;
257 int max_x
= (sparrow
->in
.width
<< SPARROW_MAP_LUT_SHIFT
) - 1;
258 int max_y
= (sparrow
->in
.height
<< SPARROW_MAP_LUT_SHIFT
) - 1;
260 for(mcy
= 0; mcy
< mesh_h
- 1; mcy
++){
261 for (mmy
= 0; mmy
< LINE_PERIOD
; mmy
++, y
++){
262 sparrow_corner_t
*mesh_square
= mesh_row
;
263 int i
= y
* sparrow
->out
.width
+ V_LINE_OFFSET
;
264 for(mcx
= 0; mcx
< mesh_w
- 1; mcx
++){
265 int iy
= mesh_square
->in_y
+ mmy
* mesh_square
->dyd
;
266 int ix
= mesh_square
->in_x
+ mmy
* mesh_square
->dxd
;
267 for (mmx
= 0; mmx
< LINE_PERIOD
; mmx
++, i
++){
268 int ixx
= CLAMP(ix
>> SPARROW_FP_2_LUT
, 0, max_x
);
269 int iyy
= CLAMP(iy
>> SPARROW_FP_2_LUT
, 0, max_y
);
270 if(sparrow
->screenmask
[(iyy
>> SPARROW_MAP_LUT_SHIFT
) * sparrow
->in
.width
271 + (ixx
>> SPARROW_MAP_LUT_SHIFT
)]){
275 ix
+= mesh_square
->dxr
;
276 iy
+= mesh_square
->dyr
;
283 sparrow
->map_lut
= map_lut
;
286 #define INTXY(x)((x) / (1 << SPARROW_FIXED_POINT))
287 #define FLOATXY(x)(((double)(x)) / (1 << SPARROW_FIXED_POINT))
290 clamp_intxy(int x
, const int max
){
293 if (x
>= max
<< SPARROW_FIXED_POINT
)
295 return x
/ (1 << SPARROW_FIXED_POINT
);
299 debug_corners_image(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
300 sparrow_corner_t
*mesh
= fl
->mesh
;
301 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
302 guint w
= fl
->debug
->width
;
303 guint h
= fl
->debug
->height
;
304 memset(data
, 0, sparrow
->in
.size
);
305 guint32 colours
[4] = {0xff0000ff, 0x00ff0000, 0x0000ff00, 0xcccccccc};
306 for (int i
= 0; i
< fl
->n_vlines
* fl
->n_hlines
; i
++){
307 sparrow_corner_t
*c
= &mesh
[i
];
310 GST_DEBUG("i %d status %d x: %f, y: %f dxr %f dyr %f dxd %f dyd %f\n"
311 "int x, y %d,%d (raw %d,%d) data %p\n",
312 i
, c
->status
, FLOATXY(x
), FLOATXY(y
),
313 FLOATXY(c
->dxr
), FLOATXY(c
->dyr
), FLOATXY(c
->dxd
), FLOATXY(c
->dyd
),
314 INTXY(x
), INTXY(y
), x
, y
, data
);
319 for (int j
= 1; j
< LINE_PERIOD
; j
+= 2){
324 data
[clamp_intxy(tyr
, h
) * w
+ clamp_intxy(txr
, w
)] = 0x000088;
325 data
[clamp_intxy(tyd
, h
) * w
+ clamp_intxy(txd
, w
)] = 0x663300;
327 data
[clamp_intxy(y
, h
) * w
+ clamp_intxy(x
, w
)] = colours
[c
->status
];
329 MAYBE_DEBUG_IPL(fl
->debug
);
334 debug_clusters(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
335 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
336 memset(data
, 0, sparrow
->in
.size
);
337 int width
= fl
->n_vlines
;
338 int height
= fl
->n_hlines
;
339 sparrow_cluster_t
*clusters
= fl
->clusters
;
342 guint32 colours
[4] = {0xff0000ff, 0x0000ff00, 0x00ff0000,
344 for (i
= 0; i
< width
* height
; i
++){
345 colour
= colours
[i
% 5];
346 sparrow_voter_t
*v
= clusters
[i
].voters
;
347 for (j
= 0; j
< clusters
[i
].n
; j
++){
348 data
[v
[j
].y
* sparrow
->in
.width
+
349 v
[j
].x
] = (colour
* (v
[j
].signal
/ 2)) / 256;
352 MAYBE_DEBUG_IPL(fl
->debug
);
356 #define SIGNAL_QUANT 1
358 /*maximum number of pixels in a cluster */
359 #define CLUSTER_SIZE 8
364 make_clusters(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
365 sparrow_cluster_t
*clusters
= fl
->clusters
;
367 /*special case: spurious values collect up at 0,0 */
368 fl
->map
[0].signal
[SPARROW_VERTICAL
] = 0;
369 fl
->map
[0].signal
[SPARROW_HORIZONTAL
] = 0;
370 /*each point in fl->map is in a vertical line, a horizontal line, both, or
371 neither. Only the "both" case matters. */
372 for (y
= 0; y
< sparrow
->in
.height
; y
++){
373 for (x
= 0; x
< sparrow
->in
.width
; x
++){
374 sparrow_intersect_t
*p
= &fl
->map
[y
* sparrow
->in
.width
+ x
];
375 guint vsig
= p
->signal
[SPARROW_VERTICAL
];
376 guint hsig
= p
->signal
[SPARROW_HORIZONTAL
];
377 /*remembering that 0 is valid as a line number, but not as a signal */
378 if (! (vsig
&& hsig
)){
381 /*This one is lobbying for the position of a corner.*/
382 int vline
= p
->lines
[SPARROW_VERTICAL
];
383 int hline
= p
->lines
[SPARROW_HORIZONTAL
];
384 if (vline
== BAD_PIXEL
|| hline
== BAD_PIXEL
){
385 GST_DEBUG("ignoring bad pixel %d, %d\n", x
, y
);
388 sparrow_cluster_t
*cluster
= &clusters
[hline
* fl
->n_vlines
+ vline
];
389 sparrow_voter_t
*voters
= cluster
->voters
;
391 guint signal
= (vsig
* hsig
) / SIGNAL_QUANT
;
392 GST_DEBUG("signal at %p (%d, %d): %dv %dh, product %u, lines: %dv %dh\n"
393 "cluster is %p, n is %d\n", p
, x
, y
,
394 vsig
, hsig
, signal
, vline
, hline
, cluster
, n
);
396 GST_WARNING("signal at %p (%d, %d) is %d following quantisation!\n",
400 if (n
< CLUSTER_SIZE
){
403 voters
[n
].signal
= signal
;
407 /*duplicate x, y, signal, so they aren't mucked up */
411 /*replaced one ends up here */
415 for (int j
= 0; j
< CLUSTER_SIZE
; j
++){
416 if (voters
[j
].signal
< ts
){
417 ts2
= voters
[j
].signal
;
420 voters
[j
].signal
= ts
;
428 GST_DEBUG("more than %d pixels at cluster for corner %d, %d."
429 "Dropped %u for %u\n",
430 CLUSTER_SIZE
, vline
, hline
, ts2
, signal
);
435 debug_clusters(sparrow
, fl
);
441 drop_cluster_voter(sparrow_cluster_t
*cluster
, int n
)
444 for (int i
= n
; i
< cluster
->n
- 1; i
++){
445 cluster
->voters
[i
] = cluster
->voters
[i
+ 1];
451 static inline int sort_median(int *a
, guint n
)
454 /*stupid sort, but n is very small*/
455 for (i
= 0; i
< n
; i
++){
456 for (j
= i
+ 1; j
< n
; j
++){
464 guint middle
= n
/ 2;
465 int answer
= a
[middle
];
468 answer
+= a
[middle
- 1];
474 #define EUCLIDEAN_D2(ax, ay, bx, by)((ax - bx) * (ax - bx) + (ay - by) * (ay - by))
475 #define NEIGHBOURLY_THRESHOLD (LINE_PERIOD * 4)
478 neighbourly_discard_cluster_outliers(GstSparrow
*sparrow
, sparrow_cluster_t
*cluster
,
479 sparrow_corner_t
*neighbour
)
481 /* assuming the output mesh entirely fits in the input window (which is
482 required for sparrow to work) the neighbours should be at most
483 LINE_PERIOD * input resolution / output resolution apart. But set the
484 threshold higher, just in case. */
485 const int threshold
= NEIGHBOURLY_THRESHOLD
* sparrow
->in
.height
/ sparrow
->out
.height
;
487 int neighbour_d
[CLUSTER_SIZE
];
489 for (i
= 0; i
< cluster
->n
; i
++){
490 int d
= EUCLIDEAN_D2(neighbour
->in_x
, neighbour
->in_y
,
491 cluster
->voters
[i
].x
, cluster
->voters
[i
].y
);
492 int pass
= d
> threshold
;
493 neighbour_d
[i
] = pass
;
495 GST_DEBUG("failing point %d, distance sq %d, threshold %d\n", i
, d
, threshold
);
498 for (i
= 0; i
< cluster
->n
; i
++){
499 if (! neighbour_d
[i
]){
500 drop_cluster_voter(cluster
, i
);
507 median_discard_cluster_outliers(sparrow_cluster_t
*cluster
)
509 int xvals
[CLUSTER_SIZE
];
510 int yvals
[CLUSTER_SIZE
];
512 for (i
= 0; i
< cluster
->n
; i
++){
513 /*XXX could sort here*/
514 xvals
[i
] = cluster
->voters
[i
].x
;
515 yvals
[i
] = cluster
->voters
[i
].y
;
517 const int xmed
= sort_median(xvals
, cluster
->n
);
518 const int ymed
= sort_median(yvals
, cluster
->n
);
520 for (i
= 0; i
< cluster
->n
; i
++){
521 int dx
= cluster
->voters
[i
].x
- xmed
;
522 int dy
= cluster
->voters
[i
].y
- ymed
;
523 if (dx
* dx
+ dy
* dy
> OUTLIER_THRESHOLD
){
524 drop_cluster_voter(cluster
, i
);
531 make_corners(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
532 //DEBUG_FIND_LINES(fl);
533 int width
= fl
->n_vlines
;
534 int height
= fl
->n_hlines
;
535 sparrow_cluster_t
*clusters
= fl
->clusters
;
536 sparrow_corner_t
*mesh
= fl
->mesh
;
540 for (y
= 0; y
< height
; y
++){
541 for (x
= 0; x
< width
; x
++, i
++){
542 sparrow_cluster_t
*cluster
= clusters
+ i
;
543 if (cluster
->n
== 0){
547 /*the good points should all be adjacent; distant ones are spurious, so
550 This fails if all the cluster are way off. Obviously it would be good
551 to include information about the grid in the decision, but that is not
552 there yet. (needs iteration, really).
554 Here's a slight attempt:*/
556 sparrow_corner_t
*neighbour
;
558 neighbour
= &mesh
[i
- 1];
559 neighbourly_discard_cluster_outliers(sparrow
, cluster
, neighbour
);
562 neighbour
= &mesh
[i
- width
];
563 neighbourly_discard_cluster_outliers(sparrow
, cluster
, neighbour
);
566 median_discard_cluster_outliers(cluster
);
568 /* now find a weighted average position */
569 /*64 bit to avoid overflow -- should probably just use floating point
578 for (j
= 0; j
< cluster
->n
; j
++){
579 votes
+= cluster
->voters
[j
].signal
;
580 ysum
+= cluster
->voters
[j
].y
* cluster
->voters
[j
].signal
;
581 xsum
+= cluster
->voters
[j
].x
* cluster
->voters
[j
].signal
;
584 xmean
= (xsum
<< SPARROW_FIXED_POINT
) / votes
;
585 ymean
= (ysum
<< SPARROW_FIXED_POINT
) / votes
;
588 GST_WARNING("corner %d, %d voters, sum %d,%d, somehow has no votes\n",
589 i
, cluster
->n
, xsum
, ysum
);
592 GST_DEBUG("corner %d: %d voters, %d votes, sum %d,%d, mean %d,%d\n",
593 i
, cluster
->n
, votes
, xsum
, ysum
, xmean
, ymean
);
595 mesh
[i
].in_x
= xmean
;
596 mesh
[i
].in_y
= ymean
;
597 mesh
[i
].status
= CORNER_EXACT
;
598 GST_DEBUG("found corner %d at (%3f, %3f)\n",
599 i
, FLOATXY(xmean
), FLOATXY(ymean
));
606 make_map(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
608 int width
= fl
->n_vlines
;
609 int height
= fl
->n_hlines
;
610 sparrow_corner_t
*mesh
= fl
->mesh
;
613 DEBUG_FIND_LINES(fl
);
614 /* calculate deltas toward adjacent corners */
615 /* try to extrapolate left and up, if possible, so need to go backwards. */
616 i
= width
* height
- 1;
617 for (y
= height
- 1; y
>= 0; y
--){
618 for (x
= width
- 1; x
>= 0; x
--, i
--){
619 sparrow_corner_t
*corner
= &mesh
[i
];
620 /* calculate the delta to next corner. If this corner is on edge, delta is
621 0 and next is this.*/
622 sparrow_corner_t
*right
= (x
== width
- 1) ? corner
: corner
+ 1;
623 sparrow_corner_t
*down
= (y
== height
- 1) ? corner
: corner
+ width
;
624 GST_DEBUG("i %d xy %d,%d width %d. in_xy %d,%d; down in_xy %d,%d; right in_xy %d,%d\n",
625 i
, x
, y
, width
, corner
->in_x
, corner
->in_y
, down
->in_x
,
626 down
->in_y
, right
->in_x
, right
->in_y
);
627 if (corner
->status
!= CORNER_UNUSED
){
628 corner
->dxr
= (right
->in_x
- corner
->in_x
);
629 corner
->dyr
= (right
->in_y
- corner
->in_y
);
630 corner
->dxd
= (down
->in_x
- corner
->in_x
);
631 corner
->dyd
= (down
->in_y
- corner
->in_y
);
634 /*copy from both right and down, if they both exist. */
640 } dividends
= {0, 0, 0, 0};
646 if (right
!= corner
){
647 if (right
->dxr
|| right
->dyr
){
648 dividends
.dxr
+= right
->dxr
;
649 dividends
.dyr
+= right
->dyr
;
652 if (right
->dxd
|| right
->dyd
){
653 dividends
.dxd
+= right
->dxd
;
654 dividends
.dyd
+= right
->dyd
;
659 if (down
->dxr
|| down
->dyr
){
660 dividends
.dxr
+= down
->dxr
;
661 dividends
.dyr
+= down
->dyr
;
664 if (down
->dxd
|| down
->dyd
){
665 dividends
.dxd
+= down
->dxd
;
666 dividends
.dyd
+= down
->dyd
;
670 corner
->dxr
= divisors
.r
? dividends
.dxr
/ divisors
.r
: 0;
671 corner
->dyr
= divisors
.r
? dividends
.dyr
/ divisors
.r
: 0;
672 corner
->dxd
= divisors
.d
? dividends
.dxd
/ divisors
.d
: 0;
673 corner
->dyd
= divisors
.d
? dividends
.dyd
/ divisors
.d
: 0;
675 /*now extrapolate position, preferably from both left and right */
676 if (right
== corner
){
677 if (down
!= corner
){ /*use down only */
678 corner
->in_x
= down
->in_x
- corner
->dxd
;
679 corner
->in_y
= down
->in_y
- corner
->dyd
;
682 GST_DEBUG("can't reconstruct corner %d, %d: no useable neighbours\n", x
, y
);
683 /*it would be easy enough to look further, but hopefully of no
687 else if (down
== corner
){ /*use right only */
688 corner
->in_x
= right
->in_x
- corner
->dxr
;
689 corner
->in_y
= right
->in_y
- corner
->dyr
;
691 else { /* use both */
692 corner
->in_x
= right
->in_x
- corner
->dxr
;
693 corner
->in_y
= right
->in_y
- corner
->dyr
;
694 corner
->in_x
+= down
->in_x
- corner
->dxd
;
695 corner
->in_y
+= down
->in_y
- corner
->dyd
;
699 corner
->status
= CORNER_PROJECTED
;
703 /*now quantise delta values. It would be wrong to do it earlier, when they
704 are being used to calculate whole mesh jumps, but from now they are
705 primarily going to used for pixel (mesh / LINE_PERIOD) jumps. To do this in
706 corners_to_lut puts a whole lot of division in a tight loop.*/
707 for (i
= 0; i
< width
* height
; i
++){
708 sparrow_corner_t
*corner
= &mesh
[i
];
709 corner
->dxr
= QUANTISE_DELTA(corner
->dxr
);
710 corner
->dyr
= QUANTISE_DELTA(corner
->dyr
);
711 corner
->dxd
= QUANTISE_DELTA(corner
->dxd
);
712 corner
->dyd
= QUANTISE_DELTA(corner
->dyd
);
714 DEBUG_FIND_LINES(fl
);
716 debug_corners_image(sparrow
, fl
);
723 look_for_line(GstSparrow
*sparrow
, guint8
*in
, sparrow_find_lines_t
*fl
,
724 sparrow_line_t
*line
){
727 guint32 cmask
= sparrow
->out
.colours
[sparrow
->colour
];
730 /* subtract background noise */
731 fl
->input
->imageData
= (char *)in
;
732 cvSub(fl
->input
, fl
->threshold
, fl
->working
, NULL
);
733 guint32
*in32
= (guint32
*)fl
->working
->imageData
;
735 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
736 colour
= in32
[i
] & cmask
;
737 signal
= (((colour
>> fl
->shift1
) +
738 (colour
>> fl
->shift2
))) & 0xff;
740 if (fl
->map
[i
].lines
[line
->dir
]){
741 /*assume the pixel is on for everyone and will just confuse
743 XXX maybe threshold, so if signal is hugely bigger in one, don't ban it.
745 if (fl
->map
[i
].lines
[line
->dir
] != BAD_PIXEL
){
746 GST_DEBUG("HEY, expected point %d to be in line %d (dir %d) "
747 "and thus empty, but it is also in line %d\n"
748 "old signal %d, new signal %d, marking as BAD\n",
749 i
, line
->index
, line
->dir
, fl
->map
[i
].lines
[line
->dir
],
750 fl
->map
[i
].signal
[line
->dir
], signal
);
751 fl
->map
[i
].lines
[line
->dir
] = BAD_PIXEL
;
752 fl
->map
[i
].signal
[line
->dir
] = 0;
756 fl
->map
[i
].lines
[line
->dir
] = line
->index
;
757 fl
->map
[i
].signal
[line
->dir
] = signal
;
764 debug_map_image(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
765 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
766 memset(data
, 0, sparrow
->in
.size
);
767 for (guint i
= 0; i
< sparrow
->in
.pixcount
; i
++){
768 data
[i
] |= fl
->map
[i
].signal
[SPARROW_HORIZONTAL
] << sparrow
->in
.gshift
;
769 data
[i
] |= fl
->map
[i
].signal
[SPARROW_VERTICAL
] << sparrow
->in
.rshift
;
770 data
[i
] |= ((fl
->map
[i
].lines
[SPARROW_VERTICAL
] == BAD_PIXEL
) ||
771 (fl
->map
[i
].lines
[SPARROW_HORIZONTAL
] == BAD_PIXEL
)) ? 255 << sparrow
->in
.bshift
: 0;
773 MAYBE_DEBUG_IPL(fl
->debug
);
776 /* draw the line (in sparrow->colour) */
778 draw_line(GstSparrow
* sparrow
, sparrow_line_t
*line
, guint8
*out
){
779 guint32
*p
= (guint32
*)out
;
780 guint32 colour
= sparrow
->out
.colours
[sparrow
->colour
];
782 if (line
->dir
== SPARROW_HORIZONTAL
){
783 p
+= line
->offset
* sparrow
->out
.width
;
784 for (i
= 0; i
< sparrow
->out
.width
; i
++){
789 guint32
*p
= (guint32
*)out
;
791 for(i
= 0; i
< sparrow
->out
.height
; i
++){
793 p
+= sparrow
->out
.width
;
799 jump_state(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, edges_state_t state
){
800 if (state
== EDGES_NEXT_STATE
){
807 case EDGES_FIND_NOISE
:
808 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
810 case EDGES_FIND_LINES
:
811 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
813 case EDGES_FIND_CORNERS
:
814 sparrow
->countdown
= 4;
817 GST_DEBUG("jumped to non-existent state %d\n", fl
->state
);
822 /* show each line for 2 frames, then wait sparrow->lag frames, leaving line on
826 draw_lines(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, guint8
*in
, guint8
*out
)
828 sparrow_line_t
*line
= fl
->shuffled_lines
[fl
->current
];
829 sparrow
->countdown
--;
830 memset(out
, 0, sparrow
->out
.size
);
831 if (sparrow
->countdown
){
832 draw_line(sparrow
, line
, out
);
835 /*show nothing, look for result */
836 look_for_line(sparrow
, in
, fl
, line
);
838 debug_map_image(sparrow
, fl
);
841 if (fl
->current
== fl
->n_lines
){
842 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
845 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
850 #define LINE_THRESHOLD 32
853 find_threshold(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, guint8
*in
, guint8
*out
)
855 memset(out
, 0, sparrow
->out
.size
);
856 /*XXX should average/median over a range of frames */
857 if (sparrow
->countdown
== 0){
858 memcpy(fl
->threshold
->imageData
, in
, sparrow
->in
.size
);
859 /*add a constant, and smooth */
860 cvAddS(fl
->threshold
, cvScalarAll(LINE_THRESHOLD
), fl
->working
, NULL
);
861 cvSmooth(fl
->working
, fl
->threshold
, CV_GAUSSIAN
, 3, 0, 0, 0);
862 //cvSmooth(fl->working, fl->threshold, CV_MEDIAN, 3, 0, 0, 0);
863 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
865 sparrow
->countdown
--;
868 /*match up lines and find corners */
870 find_corners(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
)
872 sparrow
->countdown
--;
873 switch(sparrow
->countdown
){
875 make_clusters(sparrow
, fl
);
878 make_corners(sparrow
, fl
);
881 make_map(sparrow
, fl
);
885 corners_to_full_lut(sparrow
, fl
);
887 corners_to_lut(sparrow
, fl
);
889 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
892 GST_DEBUG("how did sparrow->countdown get to be %d?", sparrow
->countdown
);
893 sparrow
->countdown
= 4;
895 return sparrow
->countdown
;
899 wait_for_play(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
900 switch(sparrow
->countdown
){
902 /*just starting! set to wait time*/
903 sparrow
->countdown
= 100;
909 sparrow
->countdown
--;
913 INVISIBLE sparrow_state
914 mode_find_edges(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
915 sparrow_find_lines_t
*fl
= (sparrow_find_lines_t
*)sparrow
->helper_struct
;
917 case EDGES_FIND_NOISE
:
918 find_threshold(sparrow
, fl
, in
, out
);
920 case EDGES_FIND_LINES
:
921 draw_lines(sparrow
, fl
, in
, out
);
923 case EDGES_FIND_CORNERS
:
924 memset(out
, 0, sparrow
->out
.size
);
925 if (find_corners(sparrow
, fl
))
927 case EDGES_WAIT_FOR_PLAY
:
928 memset(out
, 0, sparrow
->out
.size
);
929 if (wait_for_play(sparrow
, fl
)){
930 return SPARROW_NEXT_STATE
;
933 case EDGES_NEXT_STATE
:
934 break; /*shush gcc */
936 return SPARROW_STATUS_QUO
;
940 finalise_find_edges(GstSparrow
*sparrow
){
941 sparrow_find_lines_t
*fl
= (sparrow_find_lines_t
*)sparrow
->helper_struct
;
942 //DEBUG_FIND_LINES(fl);
943 if (sparrow
->save
&& *(sparrow
->save
)){
944 GST_DEBUG("about to save to %s\n", sparrow
->save
);
945 dump_edges_info(sparrow
, fl
, sparrow
->save
);
948 cvReleaseImage(&fl
->debug
);
951 free(fl
->shuffled_lines
);
955 cvReleaseImage(&fl
->threshold
);
956 cvReleaseImage(&fl
->working
);
957 cvReleaseImageHeader(&fl
->input
);
959 GST_DEBUG("freed everything\n");
960 sparrow
->helper_struct
= NULL
;
963 /*reduce the signal a little bit more, avoiding overflow later */
964 #define COLOUR_QUANT 1
967 setup_colour_shifts(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
968 switch (sparrow
->colour
){
971 fl
->shift1
= sparrow
->in
.gshift
+ COLOUR_QUANT
;
972 fl
->shift2
= sparrow
->in
.gshift
+ COLOUR_QUANT
;
974 case SPARROW_MAGENTA
:
975 fl
->shift1
= sparrow
->in
.rshift
+ COLOUR_QUANT
;
976 fl
->shift2
= sparrow
->in
.bshift
+ COLOUR_QUANT
;
982 init_find_edges(GstSparrow
*sparrow
){
984 sparrow_find_lines_t
*fl
= zalloc_aligned_or_die(sizeof(sparrow_find_lines_t
));
985 sparrow
->helper_struct
= (void *)fl
;
987 gint h_lines
= (sparrow
->out
.height
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
988 gint v_lines
= (sparrow
->out
.width
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
989 gint n_lines_max
= (h_lines
+ v_lines
);
990 gint n_corners
= (h_lines
* v_lines
);
991 fl
->n_hlines
= h_lines
;
992 fl
->n_vlines
= v_lines
;
995 fl
->h_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
) * n_lines_max
);
996 fl
->shuffled_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
*) * n_lines_max
);
997 GST_DEBUG("shuffled lines, malloced %p\n", fl
->shuffled_lines
);
999 GST_DEBUG("map is going to be %d * %d \n", sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
);
1000 fl
->map
= zalloc_aligned_or_die(sizeof(sparrow_intersect_t
) * sparrow
->in
.pixcount
);
1001 fl
->clusters
= zalloc_or_die(n_corners
* sizeof(sparrow_cluster_t
));
1002 fl
->mesh
= zalloc_aligned_or_die(n_corners
* sizeof(sparrow_corner_t
));
1004 sparrow_line_t
*line
= fl
->h_lines
;
1005 sparrow_line_t
**sline
= fl
->shuffled_lines
;
1008 for (i
= 0, offset
= H_LINE_OFFSET
; offset
< sparrow
->out
.height
;
1009 i
++, offset
+= LINE_PERIOD
){
1010 line
->offset
= offset
;
1011 line
->dir
= SPARROW_HORIZONTAL
;
1016 //GST_DEBUG("line %d h has offset %d\n", i, offset);
1019 /*now add the vertical lines */
1021 for (i
= 0, offset
= V_LINE_OFFSET
; offset
< sparrow
->out
.width
;
1022 i
++, offset
+= LINE_PERIOD
){
1023 line
->offset
= offset
;
1024 line
->dir
= SPARROW_VERTICAL
;
1029 //GST_DEBUG("line %d v has offset %d\n", i, offset);
1031 //DEBUG_FIND_LINES(fl);
1032 fl
->n_lines
= line
- fl
->h_lines
;
1033 GST_DEBUG("allocated %d lines, made %d\n", n_lines_max
, fl
->n_lines
);
1036 for (i
= 0; i
< fl
->n_lines
; i
++){
1037 int j
= RANDINT(sparrow
, 0, fl
->n_lines
);
1038 sparrow_line_t
*tmp
= fl
->shuffled_lines
[j
];
1039 fl
->shuffled_lines
[j
] = fl
->shuffled_lines
[i
];
1040 fl
->shuffled_lines
[i
] = tmp
;
1043 setup_colour_shifts(sparrow
, fl
);
1045 if (sparrow
->reload
){
1046 if (access(sparrow
->reload
, R_OK
)){
1047 GST_DEBUG("sparrow>reload is '%s' and it is UNREADABLE\n", sparrow
->reload
);
1050 read_edges_info(sparrow
, fl
, sparrow
->reload
);
1051 memset(fl
->map
, 0, sizeof(sparrow_intersect_t
) * sparrow
->in
.pixcount
);
1052 //memset(fl->clusters, 0, n_corners * sizeof(sparrow_cluster_t));
1053 memset(fl
->mesh
, 0, n_corners
* sizeof(sparrow_corner_t
));
1054 jump_state(sparrow
, fl
, EDGES_FIND_CORNERS
);
1057 jump_state(sparrow
, fl
, EDGES_FIND_NOISE
);
1059 /* opencv images for threshold finding */
1060 CvSize size
= {sparrow
->in
.width
, sparrow
->in
.height
};
1061 fl
->working
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);
1062 fl
->threshold
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);
1064 /*input has no data allocated -- it uses latest frame*/
1065 fl
->input
= init_ipl_image(&sparrow
->in
, PIXSIZE
);
1067 //DEBUG_FIND_LINES(fl);
1068 if (sparrow
->debug
){
1069 fl
->debug
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);