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
->out
.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
->out
.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 for(mcy
= 0; mcy
< mesh_h
- 1; mcy
++){
258 for (mmy
= 0; mmy
< LINE_PERIOD
; mmy
++, y
++){
259 sparrow_corner_t
*mesh_square
= mesh_row
;
260 int i
= y
* sparrow
->out
.width
+ V_LINE_OFFSET
;
261 for(mcx
= 0; mcx
< mesh_w
- 1; mcx
++){
262 int iy
= mesh_square
->in_y
+ mmy
* mesh_square
->dyd
;
263 int ix
= mesh_square
->in_x
+ mmy
* mesh_square
->dxd
;
264 for (mmx
= 0; mmx
< LINE_PERIOD
; mmx
++, i
++){
265 map_lut
[i
].x
= MAX(ix
>> SPARROW_FP_2_LUT
, 0);
266 map_lut
[i
].y
= MAX(iy
>> SPARROW_FP_2_LUT
, 0);
267 //GST_DEBUG("ix, iy %d, %d x, y %d, %d i: %d, y%d\n", ix, iy,
268 // map_lut[i].x, map_lut[i].y, i, y);
270 ix
+= mesh_square
->dxr
;
271 iy
+= mesh_square
->dyr
;
278 sparrow
->map_lut
= map_lut
;
281 #define INTXY(x)((x) / (1 << SPARROW_FIXED_POINT))
282 #define FLOATXY(x)(((double)(x)) / (1 << SPARROW_FIXED_POINT))
284 debug_corners_image(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
285 sparrow_corner_t
*mesh
= fl
->mesh
;
286 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
287 guint w
= fl
->debug
->width
;
288 memset(data
, 0, sparrow
->in
.size
);
289 guint32 colours
[4] = {0xff0000ff, 0x00ff0000, 0x0000ff00, 0xcccccccc};
290 for (int i
= 0; i
< fl
->n_vlines
* fl
->n_hlines
; i
++){
291 sparrow_corner_t
*c
= &mesh
[i
];
294 GST_DEBUG("i %d status %d x: %f, y: %f dxr %f dyr %f dxd %f dyd %f\n"
295 "int x, y %d,%d (raw %d,%d) data %p\n",
296 i
, c
->status
, FLOATXY(x
), FLOATXY(y
),
297 FLOATXY(c
->dxr
), FLOATXY(c
->dyr
), FLOATXY(c
->dxd
), FLOATXY(c
->dyd
),
298 INTXY(x
), INTXY(y
), x
, y
, data
);
303 for (int j
= 1; j
< LINE_PERIOD
; j
+= 2){
308 data
[INTXY(tyr
) * w
+ INTXY(txr
)] = 0x000088;
309 data
[INTXY(tyd
) * w
+ INTXY(txd
)] = 0x663300;
311 data
[INTXY(y
) * w
+ INTXY(x
)] = colours
[c
->status
];
313 MAYBE_DEBUG_IPL(fl
->debug
);
318 debug_clusters(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
319 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
320 memset(data
, 0, sparrow
->in
.size
);
321 int width
= fl
->n_vlines
;
322 int height
= fl
->n_hlines
;
323 sparrow_cluster_t
*clusters
= fl
->clusters
;
326 guint32 colours
[4] = {0xff0000ff, 0x0000ff00, 0x00ff0000,
328 for (i
= 0; i
< width
* height
; i
++){
329 colour
= colours
[i
% 5];
330 sparrow_voter_t
*v
= clusters
[i
].voters
;
331 for (j
= 0; j
< clusters
[i
].n
; j
++){
332 data
[v
[j
].y
* sparrow
->in
.width
+
333 v
[j
].x
] = (colour
* (v
[j
].signal
/ 2)) / 256;
336 MAYBE_DEBUG_IPL(fl
->debug
);
340 #define SIGNAL_QUANT 1
342 /*maximum number of pixels in a cluster */
343 #define CLUSTER_SIZE 8
348 make_clusters(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
349 sparrow_cluster_t
*clusters
= fl
->clusters
;
351 /*special case: spurious values collect up at 0,0 */
352 fl
->map
[0].signal
[SPARROW_VERTICAL
] = 0;
353 fl
->map
[0].signal
[SPARROW_HORIZONTAL
] = 0;
354 /*each point in fl->map is in a vertical line, a horizontal line, both, or
355 neither. Only the "both" case matters. */
356 for (y
= 0; y
< sparrow
->in
.height
; y
++){
357 for (x
= 0; x
< sparrow
->in
.width
; x
++){
358 sparrow_intersect_t
*p
= &fl
->map
[y
* sparrow
->in
.width
+ x
];
359 guint vsig
= p
->signal
[SPARROW_VERTICAL
];
360 guint hsig
= p
->signal
[SPARROW_HORIZONTAL
];
361 /*remembering that 0 is valid as a line number, but not as a signal */
362 if (! (vsig
&& hsig
)){
365 /*This one is lobbying for the position of a corner.*/
366 int vline
= p
->lines
[SPARROW_VERTICAL
];
367 int hline
= p
->lines
[SPARROW_HORIZONTAL
];
368 if (vline
== BAD_PIXEL
|| hline
== BAD_PIXEL
){
369 GST_DEBUG("ignoring bad pixel %d, %d\n", x
, y
);
372 sparrow_cluster_t
*cluster
= &clusters
[hline
* fl
->n_vlines
+ vline
];
373 sparrow_voter_t
*voters
= cluster
->voters
;
375 guint signal
= (vsig
* hsig
) / SIGNAL_QUANT
;
376 GST_DEBUG("signal at %p (%d, %d): %dv %dh, product %u, lines: %dv %dh\n"
377 "cluster is %p, n is %d\n", p
, x
, y
,
378 vsig
, hsig
, signal
, vline
, hline
, cluster
, n
);
380 GST_WARNING("signal at %p (%d, %d) is %d following quantisation!\n",
384 if (n
< CLUSTER_SIZE
){
387 voters
[n
].signal
= signal
;
391 /*duplicate x, y, signal, so they aren't mucked up */
395 /*replaced one ends up here */
399 for (int j
= 0; j
< CLUSTER_SIZE
; j
++){
400 if (voters
[j
].signal
< ts
){
401 ts2
= voters
[j
].signal
;
404 voters
[j
].signal
= ts
;
412 GST_DEBUG("more than %d pixels at cluster for corner %d, %d."
413 "Dropped %u for %u\n",
414 CLUSTER_SIZE
, vline
, hline
, ts2
, signal
);
419 debug_clusters(sparrow
, fl
);
425 drop_cluster_voter(sparrow_cluster_t
*cluster
, int n
)
428 for (int i
= n
; i
< cluster
->n
- 1; i
++){
429 cluster
->voters
[i
] = cluster
->voters
[i
+ 1];
435 static inline int sort_median(int *a
, guint n
)
438 /*stupid sort, but n is very small*/
439 for (i
= 0; i
< n
; i
++){
440 for (j
= i
+ 1; j
< n
; j
++){
448 guint middle
= n
/ 2;
449 int answer
= a
[middle
];
452 answer
+= a
[middle
- 1];
458 #define EUCLIDEAN_D2(ax, ay, bx, by)((ax - bx) * (ax - bx) + (ay - by) * (ay - by))
459 #define NEIGHBOURLY_THRESHOLD (LINE_PERIOD * 4)
462 neighbourly_discard_cluster_outliers(GstSparrow
*sparrow
, sparrow_cluster_t
*cluster
,
463 sparrow_corner_t
*neighbour
)
465 /* assuming the output mesh entirely fits in the input window (which is
466 required for sparrow to work) the neighbours should be at most
467 LINE_PERIOD * input resolution / output resolution apart. But set the
468 threshold higher, just in case. */
469 const int threshold
= NEIGHBOURLY_THRESHOLD
* sparrow
->in
.height
/ sparrow
->out
.height
;
471 int neighbour_d
[CLUSTER_SIZE
];
473 for (i
= 0; i
< cluster
->n
; i
++){
474 int d
= EUCLIDEAN_D2(neighbour
->in_x
, neighbour
->in_y
,
475 cluster
->voters
[i
].x
, cluster
->voters
[i
].y
);
476 int pass
= d
> threshold
;
477 neighbour_d
[i
] = pass
;
479 GST_DEBUG("failing point %d, distance sq %d, threshold %d\n", i
, d
, threshold
);
482 for (i
= 0; i
< cluster
->n
; i
++){
483 if (! neighbour_d
[i
]){
484 drop_cluster_voter(cluster
, i
);
491 median_discard_cluster_outliers(sparrow_cluster_t
*cluster
)
493 int xvals
[CLUSTER_SIZE
];
494 int yvals
[CLUSTER_SIZE
];
496 for (i
= 0; i
< cluster
->n
; i
++){
497 /*XXX could sort here*/
498 xvals
[i
] = cluster
->voters
[i
].x
;
499 yvals
[i
] = cluster
->voters
[i
].y
;
501 const int xmed
= sort_median(xvals
, cluster
->n
);
502 const int ymed
= sort_median(yvals
, cluster
->n
);
504 for (i
= 0; i
< cluster
->n
; i
++){
505 int dx
= cluster
->voters
[i
].x
- xmed
;
506 int dy
= cluster
->voters
[i
].y
- ymed
;
507 if (dx
* dx
+ dy
* dy
> OUTLIER_THRESHOLD
){
508 drop_cluster_voter(cluster
, i
);
515 make_corners(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
516 //DEBUG_FIND_LINES(fl);
517 int width
= fl
->n_vlines
;
518 int height
= fl
->n_hlines
;
519 sparrow_cluster_t
*clusters
= fl
->clusters
;
520 sparrow_corner_t
*mesh
= fl
->mesh
;
524 for (y
= 0; y
< height
; y
++){
525 for (x
= 0; x
< width
; x
++, i
++){
526 sparrow_cluster_t
*cluster
= clusters
+ i
;
527 if (cluster
->n
== 0){
531 /*the good points should all be adjacent; distant ones are spurious, so
534 This fails if all the cluster are way off. Obviously it would be good
535 to include information about the grid in the decision, but that is not
536 there yet. (needs iteration, really).
538 Here's a slight attempt:*/
540 sparrow_corner_t
*neighbour
;
542 neighbour
= &mesh
[i
- 1];
543 neighbourly_discard_cluster_outliers(sparrow
, cluster
, neighbour
);
546 neighbour
= &mesh
[i
- width
];
547 neighbourly_discard_cluster_outliers(sparrow
, cluster
, neighbour
);
550 median_discard_cluster_outliers(cluster
);
552 /* now find a weighted average position */
553 /*64 bit to avoid overflow -- should probably just use floating point
562 for (j
= 0; j
< cluster
->n
; j
++){
563 votes
+= cluster
->voters
[j
].signal
;
564 ysum
+= cluster
->voters
[j
].y
* cluster
->voters
[j
].signal
;
565 xsum
+= cluster
->voters
[j
].x
* cluster
->voters
[j
].signal
;
568 xmean
= (xsum
<< SPARROW_FIXED_POINT
) / votes
;
569 ymean
= (ysum
<< SPARROW_FIXED_POINT
) / votes
;
572 GST_WARNING("corner %d, %d voters, sum %d,%d, somehow has no votes\n",
573 i
, cluster
->n
, xsum
, ysum
);
576 GST_DEBUG("corner %d: %d voters, %d votes, sum %d,%d, mean %d,%d\n",
577 i
, cluster
->n
, votes
, xsum
, ysum
, xmean
, ymean
);
579 mesh
[i
].in_x
= xmean
;
580 mesh
[i
].in_y
= ymean
;
581 mesh
[i
].status
= CORNER_EXACT
;
582 GST_DEBUG("found corner %d at (%3f, %3f)\n",
583 i
, FLOATXY(xmean
), FLOATXY(ymean
));
590 make_map(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
592 int width
= fl
->n_vlines
;
593 int height
= fl
->n_hlines
;
594 sparrow_corner_t
*mesh
= fl
->mesh
;
597 DEBUG_FIND_LINES(fl
);
598 /* calculate deltas toward adjacent corners */
599 /* try to extrapolate left and up, if possible, so need to go backwards. */
600 i
= width
* height
- 1;
601 for (y
= height
- 1; y
>= 0; y
--){
602 for (x
= width
- 1; x
>= 0; x
--, i
--){
603 sparrow_corner_t
*corner
= &mesh
[i
];
604 /* calculate the delta to next corner. If this corner is on edge, delta is
605 0 and next is this.*/
606 sparrow_corner_t
*right
= (x
== width
- 1) ? corner
: corner
+ 1;
607 sparrow_corner_t
*down
= (y
== height
- 1) ? corner
: corner
+ width
;
608 GST_DEBUG("i %d xy %d,%d width %d. in_xy %d,%d; down in_xy %d,%d; right in_xy %d,%d\n",
609 i
, x
, y
, width
, corner
->in_x
, corner
->in_y
, down
->in_x
,
610 down
->in_y
, right
->in_x
, right
->in_y
);
611 if (corner
->status
!= CORNER_UNUSED
){
612 corner
->dxr
= (right
->in_x
- corner
->in_x
);
613 corner
->dyr
= (right
->in_y
- corner
->in_y
);
614 corner
->dxd
= (down
->in_x
- corner
->in_x
);
615 corner
->dyd
= (down
->in_y
- corner
->in_y
);
618 /*copy from both right and down, if they both exist. */
624 } dividends
= {0, 0, 0, 0};
630 if (right
!= corner
){
631 if (right
->dxr
|| right
->dyr
){
632 dividends
.dxr
+= right
->dxr
;
633 dividends
.dyr
+= right
->dyr
;
636 if (right
->dxd
|| right
->dyd
){
637 dividends
.dxd
+= right
->dxd
;
638 dividends
.dyd
+= right
->dyd
;
643 if (down
->dxr
|| down
->dyr
){
644 dividends
.dxr
+= down
->dxr
;
645 dividends
.dyr
+= down
->dyr
;
648 if (down
->dxd
|| down
->dyd
){
649 dividends
.dxd
+= down
->dxd
;
650 dividends
.dyd
+= down
->dyd
;
654 corner
->dxr
= divisors
.r
? dividends
.dxr
/ divisors
.r
: 0;
655 corner
->dyr
= divisors
.r
? dividends
.dyr
/ divisors
.r
: 0;
656 corner
->dxd
= divisors
.d
? dividends
.dxd
/ divisors
.d
: 0;
657 corner
->dyd
= divisors
.d
? dividends
.dyd
/ divisors
.d
: 0;
659 /*now extrapolate position, preferably from both left and right */
660 if (right
== corner
){
661 if (down
!= corner
){ /*use down only */
662 corner
->in_x
= down
->in_x
- corner
->dxd
;
663 corner
->in_y
= down
->in_y
- corner
->dyd
;
666 GST_DEBUG("can't reconstruct corner %d, %d: no useable neighbours\n", x
, y
);
667 /*it would be easy enough to look further, but hopefully of no
671 else if (down
== corner
){ /*use right only */
672 corner
->in_x
= right
->in_x
- corner
->dxr
;
673 corner
->in_y
= right
->in_y
- corner
->dyr
;
675 else { /* use both */
676 corner
->in_x
= right
->in_x
- corner
->dxr
;
677 corner
->in_y
= right
->in_y
- corner
->dyr
;
678 corner
->in_x
+= down
->in_x
- corner
->dxd
;
679 corner
->in_y
+= down
->in_y
- corner
->dyd
;
683 corner
->status
= CORNER_PROJECTED
;
687 /*now quantise delta values. It would be wrong to do it earlier, when they
688 are being used to calculate whole mesh jumps, but from now they are
689 primarily going to used for pixel (mesh / LINE_PERIOD) jumps. To do this in
690 corners_to_lut puts a whole lot of division in a tight loop.*/
691 for (i
= 0; i
< width
* height
; i
++){
692 sparrow_corner_t
*corner
= &mesh
[i
];
693 corner
->dxr
= QUANTISE_DELTA(corner
->dxr
);
694 corner
->dyr
= QUANTISE_DELTA(corner
->dyr
);
695 corner
->dxd
= QUANTISE_DELTA(corner
->dxd
);
696 corner
->dyd
= QUANTISE_DELTA(corner
->dyd
);
698 DEBUG_FIND_LINES(fl
);
700 debug_corners_image(sparrow
, fl
);
707 look_for_line(GstSparrow
*sparrow
, guint8
*in
, sparrow_find_lines_t
*fl
,
708 sparrow_line_t
*line
){
711 guint32 cmask
= sparrow
->out
.colours
[sparrow
->colour
];
714 /* subtract background noise */
715 fl
->input
->imageData
= (char *)in
;
716 cvSub(fl
->input
, fl
->threshold
, fl
->working
, NULL
);
717 guint32
*in32
= (guint32
*)fl
->working
->imageData
;
719 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
720 colour
= in32
[i
] & cmask
;
721 signal
= (((colour
>> fl
->shift1
) +
722 (colour
>> fl
->shift2
))) & 0xff;
724 if (fl
->map
[i
].lines
[line
->dir
]){
725 /*assume the pixel is on for everyone and will just confuse
727 XXX maybe threshold, so if signal is hugely bigger in one, don't ban it.
729 if (fl
->map
[i
].lines
[line
->dir
] != BAD_PIXEL
){
730 GST_DEBUG("HEY, expected point %d to be in line %d (dir %d) "
731 "and thus empty, but it is also in line %d\n"
732 "old signal %d, new signal %d, marking as BAD\n",
733 i
, line
->index
, line
->dir
, fl
->map
[i
].lines
[line
->dir
],
734 fl
->map
[i
].signal
[line
->dir
], signal
);
735 fl
->map
[i
].lines
[line
->dir
] = BAD_PIXEL
;
736 fl
->map
[i
].signal
[line
->dir
] = 0;
740 fl
->map
[i
].lines
[line
->dir
] = line
->index
;
741 fl
->map
[i
].signal
[line
->dir
] = signal
;
748 debug_map_image(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
749 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
750 memset(data
, 0, sparrow
->in
.size
);
751 for (guint i
= 0; i
< sparrow
->in
.pixcount
; i
++){
752 data
[i
] |= fl
->map
[i
].signal
[SPARROW_HORIZONTAL
] << sparrow
->in
.gshift
;
753 data
[i
] |= fl
->map
[i
].signal
[SPARROW_VERTICAL
] << sparrow
->in
.rshift
;
754 data
[i
] |= ((fl
->map
[i
].lines
[SPARROW_VERTICAL
] == BAD_PIXEL
) ||
755 (fl
->map
[i
].lines
[SPARROW_HORIZONTAL
] == BAD_PIXEL
)) ? 255 << sparrow
->in
.bshift
: 0;
757 MAYBE_DEBUG_IPL(fl
->debug
);
760 /* draw the line (in sparrow->colour) */
762 draw_line(GstSparrow
* sparrow
, sparrow_line_t
*line
, guint8
*out
){
763 guint32
*p
= (guint32
*)out
;
764 guint32 colour
= sparrow
->out
.colours
[sparrow
->colour
];
766 if (line
->dir
== SPARROW_HORIZONTAL
){
767 p
+= line
->offset
* sparrow
->out
.width
;
768 for (i
= 0; i
< sparrow
->out
.width
; i
++){
773 guint32
*p
= (guint32
*)out
;
775 for(i
= 0; i
< sparrow
->out
.height
; i
++){
777 p
+= sparrow
->out
.width
;
783 jump_state(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, edges_state_t state
){
784 if (state
== EDGES_NEXT_STATE
){
791 case EDGES_FIND_NOISE
:
792 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
794 case EDGES_FIND_LINES
:
795 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
797 case EDGES_FIND_CORNERS
:
798 sparrow
->countdown
= 4;
801 GST_DEBUG("jumped to non-existent state %d\n", fl
->state
);
806 /* show each line for 2 frames, then wait sparrow->lag frames, leaving line on
810 draw_lines(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, guint8
*in
, guint8
*out
)
812 sparrow_line_t
*line
= fl
->shuffled_lines
[fl
->current
];
813 sparrow
->countdown
--;
814 memset(out
, 0, sparrow
->out
.size
);
815 if (sparrow
->countdown
){
816 draw_line(sparrow
, line
, out
);
819 /*show nothing, look for result */
820 look_for_line(sparrow
, in
, fl
, line
);
822 debug_map_image(sparrow
, fl
);
825 if (fl
->current
== fl
->n_lines
){
826 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
829 sparrow
->countdown
= MAX(sparrow
->lag
, 1) + SAFETY_LAG
;
834 #define LINE_THRESHOLD 32
837 find_threshold(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, guint8
*in
, guint8
*out
)
839 memset(out
, 0, sparrow
->out
.size
);
840 /*XXX should average/median over a range of frames */
841 if (sparrow
->countdown
== 0){
842 memcpy(fl
->threshold
->imageData
, in
, sparrow
->in
.size
);
843 /*add a constant, and smooth */
844 cvAddS(fl
->threshold
, cvScalarAll(LINE_THRESHOLD
), fl
->working
, NULL
);
845 cvSmooth(fl
->working
, fl
->threshold
, CV_GAUSSIAN
, 3, 0, 0, 0);
846 //cvSmooth(fl->working, fl->threshold, CV_MEDIAN, 3, 0, 0, 0);
847 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
849 sparrow
->countdown
--;
852 /*match up lines and find corners */
854 find_corners(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
)
856 sparrow
->countdown
--;
857 switch(sparrow
->countdown
){
859 make_clusters(sparrow
, fl
);
862 make_corners(sparrow
, fl
);
865 make_map(sparrow
, fl
);
869 corners_to_full_lut(sparrow
, fl
);
871 corners_to_lut(sparrow
, fl
);
875 GST_DEBUG("how did sparrow->countdown get to be %d?", sparrow
->countdown
);
876 sparrow
->countdown
= 4;
878 return sparrow
->countdown
;
882 INVISIBLE sparrow_state
883 mode_find_edges(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
884 sparrow_find_lines_t
*fl
= (sparrow_find_lines_t
*)sparrow
->helper_struct
;
886 case EDGES_FIND_NOISE
:
887 find_threshold(sparrow
, fl
, in
, out
);
889 case EDGES_FIND_LINES
:
890 draw_lines(sparrow
, fl
, in
, out
);
892 case EDGES_FIND_CORNERS
:
893 if (find_corners(sparrow
, fl
))
895 return SPARROW_NEXT_STATE
;
896 case EDGES_NEXT_STATE
:
897 break; /*shush gcc */
899 return SPARROW_STATUS_QUO
;
903 finalise_find_edges(GstSparrow
*sparrow
){
904 sparrow_find_lines_t
*fl
= (sparrow_find_lines_t
*)sparrow
->helper_struct
;
905 //DEBUG_FIND_LINES(fl);
906 if (sparrow
->save
&& *(sparrow
->save
)){
907 GST_DEBUG("about to save to %s\n", sparrow
->save
);
908 dump_edges_info(sparrow
, fl
, sparrow
->save
);
911 cvReleaseImage(&fl
->debug
);
914 free(fl
->shuffled_lines
);
918 cvReleaseImage(&fl
->threshold
);
919 cvReleaseImage(&fl
->working
);
920 cvReleaseImageHeader(&fl
->input
);
922 GST_DEBUG("freed everything\n");
923 sparrow
->helper_struct
= NULL
;
926 /*reduce the signal a little bit more, avoiding overflow later */
927 #define COLOUR_QUANT 1
930 setup_colour_shifts(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
931 switch (sparrow
->colour
){
934 fl
->shift1
= sparrow
->in
.gshift
+ COLOUR_QUANT
;
935 fl
->shift2
= sparrow
->in
.gshift
+ COLOUR_QUANT
;
937 case SPARROW_MAGENTA
:
938 fl
->shift1
= sparrow
->in
.rshift
+ COLOUR_QUANT
;
939 fl
->shift2
= sparrow
->in
.bshift
+ COLOUR_QUANT
;
945 init_find_edges(GstSparrow
*sparrow
){
947 sparrow_find_lines_t
*fl
= zalloc_aligned_or_die(sizeof(sparrow_find_lines_t
));
948 sparrow
->helper_struct
= (void *)fl
;
950 gint h_lines
= (sparrow
->out
.height
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
951 gint v_lines
= (sparrow
->out
.width
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
952 gint n_lines_max
= (h_lines
+ v_lines
);
953 gint n_corners
= (h_lines
* v_lines
);
954 fl
->n_hlines
= h_lines
;
955 fl
->n_vlines
= v_lines
;
958 fl
->h_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
) * n_lines_max
);
959 fl
->shuffled_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
*) * n_lines_max
);
960 GST_DEBUG("shuffled lines, malloced %p\n", fl
->shuffled_lines
);
962 GST_DEBUG("map is going to be %d * %d \n", sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
);
963 fl
->map
= zalloc_aligned_or_die(sizeof(sparrow_intersect_t
) * sparrow
->in
.pixcount
);
964 fl
->clusters
= zalloc_or_die(n_corners
* sizeof(sparrow_cluster_t
));
965 fl
->mesh
= zalloc_aligned_or_die(n_corners
* sizeof(sparrow_corner_t
));
967 sparrow_line_t
*line
= fl
->h_lines
;
968 sparrow_line_t
**sline
= fl
->shuffled_lines
;
971 for (i
= 0, offset
= H_LINE_OFFSET
; offset
< sparrow
->out
.height
;
972 i
++, offset
+= LINE_PERIOD
){
973 line
->offset
= offset
;
974 line
->dir
= SPARROW_HORIZONTAL
;
979 //GST_DEBUG("line %d h has offset %d\n", i, offset);
982 /*now add the vertical lines */
984 for (i
= 0, offset
= V_LINE_OFFSET
; offset
< sparrow
->out
.width
;
985 i
++, offset
+= LINE_PERIOD
){
986 line
->offset
= offset
;
987 line
->dir
= SPARROW_VERTICAL
;
992 //GST_DEBUG("line %d v has offset %d\n", i, offset);
994 //DEBUG_FIND_LINES(fl);
995 fl
->n_lines
= line
- fl
->h_lines
;
996 GST_DEBUG("allocated %d lines, made %d\n", n_lines_max
, fl
->n_lines
);
999 for (i
= 0; i
< fl
->n_lines
; i
++){
1000 int j
= RANDINT(sparrow
, 0, fl
->n_lines
);
1001 sparrow_line_t
*tmp
= fl
->shuffled_lines
[j
];
1002 fl
->shuffled_lines
[j
] = fl
->shuffled_lines
[i
];
1003 fl
->shuffled_lines
[i
] = tmp
;
1006 setup_colour_shifts(sparrow
, fl
);
1008 if (sparrow
->reload
){
1009 if (access(sparrow
->reload
, R_OK
)){
1010 GST_DEBUG("sparrow>reload is '%s' and it is UNREADABLE\n", sparrow
->reload
);
1013 read_edges_info(sparrow
, fl
, sparrow
->reload
);
1014 memset(fl
->map
, 0, sizeof(sparrow_intersect_t
) * sparrow
->in
.pixcount
);
1015 //memset(fl->clusters, 0, n_corners * sizeof(sparrow_cluster_t));
1016 memset(fl
->mesh
, 0, n_corners
* sizeof(sparrow_corner_t
));
1017 jump_state(sparrow
, fl
, EDGES_FIND_CORNERS
);
1020 jump_state(sparrow
, fl
, EDGES_FIND_NOISE
);
1022 /* opencv images for threshold finding */
1023 CvSize size
= {sparrow
->in
.width
, sparrow
->in
.height
};
1024 fl
->working
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);
1025 fl
->threshold
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);
1027 /*input has no data allocated -- it uses latest frame*/
1028 fl
->input
= init_ipl_image(&sparrow
->in
, PIXSIZE
);
1030 //DEBUG_FIND_LINES(fl);
1031 if (sparrow
->debug
){
1032 fl
->debug
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);