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"
34 /* for discarding outliers */
35 #define OUTLIER_FIXED_POINT 4
36 #define OUTLIER_RADIUS 7
37 #define OUTLIER_THRESHOLD ((OUTLIER_RADIUS * OUTLIER_RADIUS) << (OUTLIER_FIXED_POINT * 2))
39 #define SPARROW_MAP_LUT_SHIFT 1
40 #define SPARROW_FP_2_LUT (SPARROW_FIXED_POINT - SPARROW_MAP_LUT_SHIFT)
44 #define FL_DUMPFILE "/tmp/edges.dump"
46 static void dump_edges_info(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, const char *filename
){
47 GST_DEBUG("about to save to %s\n", filename
);
48 FILE *f
= fopen(filename
, "w");
49 /* simply write fl, map, clusters and mesh in sequence */
50 GST_DEBUG("fl is %p, file is %p\n", fl
, f
);
51 GST_DEBUG("fl: %d x %d\n", sizeof(sparrow_find_lines_t
), 1);
52 fwrite(fl
, sizeof(sparrow_find_lines_t
), 1, f
);
53 GST_DEBUG("fl->map %d x %d\n", sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
);
54 fwrite(fl
->map
, sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
, f
);
55 GST_DEBUG("fl->clusters %d x %d\n", sizeof(sparrow_cluster_t
), fl
->n_hlines
* fl
->n_vlines
);
56 fwrite(fl
->clusters
, sizeof(sparrow_cluster_t
), fl
->n_hlines
* fl
->n_vlines
, f
);
57 GST_DEBUG("fl->mesh %d x %d\n", sizeof(sparrow_corner_t
), fl
->n_hlines
* fl
->n_vlines
);
58 fwrite(fl
->mesh
, sizeof(sparrow_corner_t
), fl
->n_hlines
* fl
->n_vlines
, f
);
62 static void read_edges_info(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, const char *filename
){
63 FILE *f
= fopen(filename
, "r");
64 sparrow_find_lines_t fl2
;
65 size_t read
= fread(&fl2
, sizeof(sparrow_find_lines_t
), 1, f
);
66 assert(fl2
.n_hlines
== fl
->n_hlines
);
67 assert(fl2
.n_vlines
== fl
->n_vlines
);
69 guint n_corners
= fl
->n_hlines
* fl
->n_vlines
;
70 read
+= fread(fl
->map
, sizeof(sparrow_intersect_t
), sparrow
->in
.pixcount
, f
);
71 read
+= fread(fl
->clusters
, sizeof(sparrow_cluster_t
), n_corners
, f
);
72 read
+= fread(fl
->mesh
, sizeof(sparrow_corner_t
), n_corners
, f
);
77 static inline int sort_median(int *a
, guint n
)
80 /*stupid sort, but n is very small*/
81 for (i
= 0; i
< n
; i
++){
82 for (j
= i
+ 1; j
< n
; j
++){
91 int answer
= a
[middle
];
94 answer
+= a
[middle
- 1];
101 debug_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
104 #define OFFSET(x, y, w)((((y) * (w)) >> SPARROW_FIXED_POINT) + ((x) >> SPARROW_FIXED_POINT))
106 static void corners_to_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
107 //DEBUG_FIND_LINES(fl);
108 sparrow_map_t
*map
= &sparrow
->map
; /*rows in sparrow->out */
109 guint8
*mask
= sparrow
->screenmask
; /*mask in sparrow->in */
110 sparrow_corner_t
*mesh
= fl
->mesh
; /*maps regular points in ->out to points in ->in */
112 int mesh_w
= fl
->n_vlines
;
113 int mesh_h
= fl
->n_hlines
;
114 int in_w
= sparrow
->in
.width
;
115 int mcy
, mmy
, mcx
; /*Mesh Corner|Modulus X|Y*/
118 sparrow_map_row_t
*row
= map
->rows
;
119 sparrow_map_point_t
*p
= map
->point_mem
;
120 sparrow_corner_t
*mesh_row
= mesh
;
121 for(mcy
= 0; mcy
< mesh_h
; mcy
++){
122 for (mmy
= 0; mmy
< LINE_PERIOD
; mmy
++){
123 sparrow_corner_t
*mesh_square
= mesh_row
;
127 for(mcx
= 0; mcx
< mesh_w
; mcx
++){
128 if (mesh_square
->status
!= CORNER_UNUSED
){
129 int iy
= mesh_square
->in_y
+ mmy
* mesh_square
->dyd
;
130 int ix
= mesh_square
->in_x
+ mmy
* mesh_square
->dxd
;
131 int ii
= OFFSET(ix
, iy
, in_w
);
132 int ii_end
= OFFSET(ix
+ (LINE_PERIOD
- 1) * mesh_square
->dxr
,
133 iy
+ (LINE_PERIOD
- 1) * mesh_square
->dyr
, in_w
);
134 int start_on
= mask
[ii
];
135 int end_on
= mask
[ii_end
];
136 GST_DEBUG("start %d end %d row s %d e %d", start_on
, end_on
, row
->start
, row
->end
);
137 if(start_on
&& end_on
){
138 /*add the point, maybe switch on */
139 if (row
->start
== row
->end
){/* if both are 0 */
140 row
->start
= mcx
* LINE_PERIOD
;
144 p
->dx
= mesh_square
->dxr
;
145 p
->dy
= mesh_square
->dyr
;
149 /*add the point, switch off somewhere in the middle*/
150 for (x
= 1; x
< LINE_PERIOD
; x
++){
151 iy
+= mesh_square
->dyr
;
152 ix
+= mesh_square
->dxr
;
153 ii
= OFFSET(ix
, iy
, in_w
);
155 /*point is not in the same column with the others,
156 but sparrow knows this because the row->start says so */
157 row
->start
= mcx
+ x
;
160 p
->dx
= mesh_square
->dxr
;
161 p
->dy
= mesh_square
->dyr
;
168 /* add some, switch off */
169 for (x
= 1; x
< LINE_PERIOD
; x
++){
170 iy
+= mesh_square
->dyr
;
171 ix
+= mesh_square
->dxr
;
172 ii
= OFFSET(ix
, iy
, in_w
);
181 start > end: this is first off pixel.
182 start == end: row hasn't started (both 0)
183 start < end: both are set -- row is done
185 if (row
->start
> row
->end
){
186 row
->end
= mcx
* LINE_PERIOD
;
188 else if (row
->start
< row
->end
){
199 debug_lut(sparrow
, fl
);
203 corners_to_full_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
204 //DEBUG_FIND_LINES(fl);
205 sparrow_corner_t
*mesh
= fl
->mesh
; /*maps regular points in ->out to points in ->in */
206 sparrow_map_lut_t
*map_lut
= sparrow
->map_lut
;
207 int mesh_w
= fl
->n_vlines
;
208 int mesh_h
= fl
->n_hlines
;
209 int mcy
, mmy
, mcx
, mmx
; /*Mesh Corner|Modulus X|Y*/
211 sparrow_corner_t
*mesh_row
= mesh
;
212 for(mcy
= 0; mcy
< mesh_h
; mcy
++){
213 for (mmy
= 0; mmy
< LINE_PERIOD
; mmy
++){
214 sparrow_corner_t
*mesh_square
= mesh_row
;
215 for(mcx
= 0; mcx
< mesh_w
; mcx
++){
216 int iy
= mesh_square
->in_y
+ mmy
* mesh_square
->dyd
;
217 int ix
= mesh_square
->in_x
+ mmy
* mesh_square
->dxd
;
218 for (mmx
= 0; mmx
< LINE_PERIOD
; mmx
++, i
++){
219 map_lut
[i
].x
= ix
>> SPARROW_FP_2_LUT
;
220 map_lut
[i
].y
= iy
>> SPARROW_FP_2_LUT
;
221 ix
+= mesh_square
->dxr
;
222 iy
+= mesh_square
->dyr
;
229 sparrow
->map_lut
= map_lut
;
232 #define DIV ((double)(1 << SPARROW_FIXED_POINT))
233 #define INTXY(x)((x) / (1 << SPARROW_FIXED_POINT))
234 #define FLOATXY(x)(((double)(x)) / (1 << SPARROW_FIXED_POINT))
236 debug_corners_image(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
237 sparrow_corner_t
*mesh
= fl
->mesh
;
238 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
239 guint w
= fl
->debug
->width
;
240 memset(data
, 0, sparrow
->in
.size
);
241 guint32 colours
[4] = {0xff0000ff, 0x00ff0000, 0x0000ff00, 0xcccccccc};
242 for (int i
= 0; i
< fl
->n_vlines
* fl
->n_hlines
; i
++){
243 sparrow_corner_t
*c
= &mesh
[i
];
246 GST_DEBUG("i %d status %d x: %f, y: %f dxr %f dyr %f dxd %f dyd %f\n"
247 "int x, y %d,%d (raw %d,%d) data %p\n",
248 i
, c
->status
, FLOATXY(x
), FLOATXY(y
),
249 FLOATXY(c
->dxr
), FLOATXY(c
->dyr
), FLOATXY(c
->dxd
), FLOATXY(c
->dyd
),
250 INTXY(x
), INTXY(y
), x
, y
, data
);
255 for (int j
= 1; j
< LINE_PERIOD
; j
++){
260 data
[INTXY(tyr
) * w
+ INTXY(txr
)] = 0x000088;
261 data
[INTXY(tyd
) * w
+ INTXY(txd
)] = 0x663300;
264 #define LP8 (LINE_PERIOD / 8)
265 #define LP4 (LINE_PERIOD / 4)
266 #define LP2 (LINE_PERIOD / 2)
267 data
[INTXY(y
+ c
->dyr
* LP8
) * w
+ INTXY(x
+ c
->dxr
* LP8
)] = 0xbbbbbbbb;
268 data
[INTXY(y
+ c
->dyr
* LP4
) * w
+ INTXY(x
+ c
->dxr
* LP4
)] = 0xaaaaaaaa;
269 data
[INTXY(y
+ c
->dyr
* LP2
) * w
+ INTXY(x
+ c
->dxr
* LP2
)] = 0x99999999;
270 data
[INTXY(y
+ c
->dyd
* LP8
) * w
+ INTXY(x
+ c
->dxd
* LP8
)] = 0xbb6666bb;
271 data
[INTXY(y
+ c
->dyd
* LP4
) * w
+ INTXY(x
+ c
->dxd
* LP4
)] = 0xaa5555aa;
272 data
[INTXY(y
+ c
->dyd
* LP2
) * w
+ INTXY(x
+ c
->dxd
* LP2
)] = 0x99444499;
274 data
[INTXY(y
) * w
+ INTXY(x
)] = colours
[c
->status
];
276 MAYBE_DEBUG_IPL(fl
->debug
);
281 debug_clusters(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
282 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
283 memset(data
, 0, sparrow
->in
.size
);
284 int width
= fl
->n_vlines
;
285 int height
= fl
->n_hlines
;
286 sparrow_cluster_t
*clusters
= fl
->clusters
;
289 guint32 colours
[4] = {0xff0000ff, 0x0000ff00, 0x00ff0000,
291 for (i
= 0; i
< width
* height
; i
++){
292 colour
= colours
[i
% 5];
293 sparrow_voter_t
*v
= clusters
[i
].voters
;
294 for (j
= 0; j
< clusters
[i
].n
; j
++){
295 data
[(v
[j
].y
>> SPARROW_FIXED_POINT
) * sparrow
->in
.width
+
296 (v
[j
].x
>> SPARROW_FIXED_POINT
)] = (colour
* (v
[j
].signal
/ 2)) / 256;
299 MAYBE_DEBUG_IPL(fl
->debug
);
302 /*signal product is close to 18 bits. reduce to 4 */
303 #define SIGNAL_QUANT (1 << 14)
305 /*maximum number of pixels in a cluster */
306 #define CLUSTER_SIZE 8
311 make_clusters(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
312 sparrow_cluster_t
*clusters
= fl
->clusters
;
314 /*each point in fl->map is in a vertical line, a horizontal line, both, or
315 neither. Only the "both" case matters. */
316 for (y
= 0; y
< sparrow
->in
.height
; y
++){
317 for (x
= 0; x
< sparrow
->in
.width
; x
++){
318 sparrow_intersect_t
*p
= &fl
->map
[y
* sparrow
->in
.width
+ x
];
319 guint vsig
= p
->signal
[SPARROW_VERTICAL
];
320 guint hsig
= p
->signal
[SPARROW_HORIZONTAL
];
321 /*remembering that 0 is valid as a line number, but not as a signal */
322 if (! (vsig
&& hsig
)){
325 /*This one is lobbying for the position of a corner.*/
326 int vline
= p
->lines
[SPARROW_VERTICAL
];
327 int hline
= p
->lines
[SPARROW_HORIZONTAL
];
329 sparrow_cluster_t
*cluster
= &clusters
[hline
* fl
->n_vlines
+ vline
];
330 sparrow_voter_t
*voters
= cluster
->voters
;
332 guint signal
= (vsig
* hsig
) / SIGNAL_QUANT
;
333 GST_DEBUG("signal at %p (%d, %d): %dv %dh, product %u, lines: %dv %dh\n"
334 "cluster is %p, n is %d\n", p
, x
, y
,
335 vsig
, hsig
, signal
, vline
, hline
, cluster
, n
);
337 GST_WARNING("signal at %p (%d, %d) is %d following quantisation!\n",
341 if (n
< CLUSTER_SIZE
){
344 voters
[n
].signal
= signal
;
348 /*duplicate x, y, signal, so they aren't mucked up */
352 /*replaced one ends up here */
356 for (int j
= 0; j
< CLUSTER_SIZE
; j
++){
357 if (voters
[j
].signal
< ts
){
358 ts2
= voters
[j
].signal
;
361 voters
[j
].signal
= ts
;
369 GST_DEBUG("more than %d pixels at cluster for corner %d, %d."
370 "Dropped %u for %u\n",
371 CLUSTER_SIZE
, vline
, hline
, ts2
, signal
);
376 debug_clusters(sparrow
, fl
);
380 /* look for connected group. if there is more than one connected group,
384 drop_cluster_voter(sparrow_cluster_t
*cluster
, int n
)
387 for (int i
= n
; i
< cluster
->n
- 1; i
++){
388 cluster
->voters
[i
] = cluster
->voters
[i
+ 1];
395 median_discard_cluster_outliers(sparrow_cluster_t
*cluster
)
397 int xvals
[CLUSTER_SIZE
];
398 int yvals
[CLUSTER_SIZE
];
400 for (i
= 0; i
< cluster
->n
; i
++){
401 /*XXX could sort here*/
402 xvals
[i
] = cluster
->voters
[i
].x
;
403 yvals
[i
] = cluster
->voters
[i
].y
;
405 const int xmed
= sort_median(xvals
, cluster
->n
);
406 const int ymed
= sort_median(yvals
, cluster
->n
);
408 for (i
= 0; i
< cluster
->n
; i
++){
409 int dx
= cluster
->voters
[i
].x
- xmed
;
410 int dy
= cluster
->voters
[i
].y
- ymed
;
411 if (dx
*dx
+ dy
* dy
> OUTLIER_THRESHOLD
){
412 drop_cluster_voter(cluster
, i
);
419 make_corners(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
420 //DEBUG_FIND_LINES(fl);
421 int width
= fl
->n_vlines
;
422 int height
= fl
->n_hlines
;
423 sparrow_cluster_t
*clusters
= fl
->clusters
;
424 sparrow_corner_t
*mesh
= fl
->mesh
;
428 for (y
= 0; y
< height
; y
++){
429 for (x
= 0; x
< width
; x
++, i
++){
430 sparrow_cluster_t
*cluster
= clusters
+ i
;
431 if (cluster
->n
== 0){
435 /*the good points should all be adjacent; distant ones are spurious, so
437 median_discard_cluster_outliers(cluster
);
439 /* now find a weighted average position */
447 for (j
= 0; j
< cluster
->n
; j
++){
448 votes
+= cluster
->voters
[j
].signal
;
449 ysum
+= cluster
->voters
[j
].y
* cluster
->voters
[j
].signal
;
450 xsum
+= cluster
->voters
[j
].x
* cluster
->voters
[j
].signal
;
453 xmean
= (xsum
<< SPARROW_FIXED_POINT
) / votes
;
454 ymean
= (ysum
<< SPARROW_FIXED_POINT
) / votes
;
457 GST_WARNING("corner %d, %d voters, sum %d,%d, somehow has no votes\n",
458 i
, cluster
->n
, xsum
, ysum
);
461 GST_DEBUG("corner %d: %d voters, %d votes, sum %d,%d, mean %d,%d\n",
462 i
, cluster
->n
, votes
, xsum
, ysum
, xmean
, ymean
);
464 mesh
[i
].in_x
= xmean
;
465 mesh
[i
].in_y
= ymean
;
466 mesh
[i
].status
= CORNER_EXACT
;
467 GST_DEBUG("found corner %d at (%3f, %3f)\n",
468 i
, FLOATXY(xmean
), FLOATXY(ymean
));
473 #define LINE_PERIOD_TMP 1
476 make_map(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
478 int width
= fl
->n_vlines
;
479 int height
= fl
->n_hlines
;
480 sparrow_corner_t
*mesh
= fl
->mesh
;
483 //DEBUG_FIND_LINES(fl);
484 /* calculate deltas toward adjacent corners */
485 /* try to extrapolate left and up, if possible, so need to go backwards. */
486 i
= width
* height
- 1;
487 for (y
= height
- 1; y
>= 0; y
--){
488 for (x
= width
- 1; x
>= 0; x
--, i
--){
489 sparrow_corner_t
*corner
= &mesh
[i
];
490 /* calculate the delta to next corner. If this corner is on edge, delta is
491 0 and next is this.*/
492 sparrow_corner_t
*right
= (x
>= width
- 1) ? corner
: corner
+ 1;
493 sparrow_corner_t
*down
= (y
>= height
- 1) ? corner
: corner
+ width
;
494 GST_DEBUG("i %d xy %d,%d width %d. in_xy %d,%d; down in_xy %d,%d; right in_xy %d,%d\n",
495 i
, x
, y
, width
, corner
->in_x
, corner
->in_y
, down
->in_x
,
496 down
->in_y
, right
->in_x
, right
->in_y
);
497 if (corner
->status
!= CORNER_UNUSED
){
498 corner
->dxr
= (right
->in_x
- corner
->in_x
) / LINE_PERIOD_TMP
;
499 corner
->dyr
= (right
->in_y
- corner
->in_y
) / LINE_PERIOD_TMP
;
500 corner
->dxd
= (down
->in_x
- corner
->in_x
) / LINE_PERIOD_TMP
;
501 corner
->dyd
= (down
->in_y
- corner
->in_y
) / LINE_PERIOD_TMP
;
504 /*copy from both right and down, if they both exist. */
510 } dividends
= {0, 0, 0, 0};
516 if (right
!= corner
){
517 if (right
->dxr
|| right
->dyr
){
518 dividends
.dxr
+= right
->dxr
;
519 dividends
.dyr
+= right
->dyr
;
522 if (right
->dxd
|| right
->dyd
){
523 dividends
.dxd
+= right
->dxd
;
524 dividends
.dyd
+= right
->dyd
;
529 if (down
->dxr
|| down
->dyr
){
530 dividends
.dxr
+= down
->dxr
;
531 dividends
.dyr
+= down
->dyr
;
534 if (down
->dxd
|| down
->dyd
){
535 dividends
.dxd
+= down
->dxd
;
536 dividends
.dyd
+= down
->dyd
;
540 corner
->dxr
= divisors
.r
? dividends
.dxr
/ divisors
.r
: 0;
541 corner
->dyr
= divisors
.r
? dividends
.dyr
/ divisors
.r
: 0;
542 corner
->dxd
= divisors
.d
? dividends
.dxd
/ divisors
.d
: 0;
543 corner
->dyd
= divisors
.d
? dividends
.dyd
/ divisors
.d
: 0;
545 /*now extrapolate position, preferably from both left and right */
546 if (right
== corner
){
547 if (down
!= corner
){ /*use down only */
548 corner
->in_x
= down
->in_x
- corner
->dxd
* LINE_PERIOD_TMP
;
549 corner
->in_y
= down
->in_y
- corner
->dyd
* LINE_PERIOD_TMP
;
552 GST_DEBUG("can't reconstruct corner %d, %d: no useable neighbours\n", x
, y
);
553 /*it would be easy enough to look further, but hopefully of no
557 else if (down
== corner
){ /*use right only */
558 corner
->in_x
= right
->in_x
- corner
->dxr
* LINE_PERIOD_TMP
;
559 corner
->in_y
= right
->in_y
- corner
->dyr
* LINE_PERIOD_TMP
;
561 else { /* use both */
562 corner
->in_x
= right
->in_x
- corner
->dxr
* LINE_PERIOD_TMP
;
563 corner
->in_y
= right
->in_y
- corner
->dyr
* LINE_PERIOD_TMP
;
564 corner
->in_x
+= down
->in_x
- corner
->dxd
* LINE_PERIOD_TMP
;
565 corner
->in_y
+= down
->in_y
- corner
->dyd
* LINE_PERIOD_TMP
;
569 corner
->status
= CORNER_PROJECTED
;
574 DEBUG_FIND_LINES(fl
);
575 debug_corners_image(sparrow
, fl
);
582 look_for_line(GstSparrow
*sparrow
, guint8
*in
, sparrow_find_lines_t
*fl
,
583 sparrow_line_t
*line
){
586 guint32 cmask
= sparrow
->out
.colours
[sparrow
->colour
];
589 /* subtract background noise */
590 fl
->input
->imageData
= (char *)in
;
591 cvSub(fl
->input
, fl
->threshold
, fl
->working
, NULL
);
592 guint32
*in32
= (guint32
*)fl
->working
->imageData
;
594 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
595 colour
= in32
[i
] & cmask
;
596 signal
= ((colour
>> fl
->shift1
) +
597 (colour
>> fl
->shift2
)) & 0x1ff;
599 if (fl
->map
[i
].lines
[line
->dir
]){
600 GST_DEBUG("HEY, expected point %d to be in line %d (dir %d)"
601 "and thus empty, but it is also in line %d\n",
602 "old signal %d, new signal %d, ignoring weakest\n",
603 i
, line
->index
, line
->dir
, fl
->map
[i
].lines
[line
->dir
],
604 fl
->map
[i
].signal
[line
->dir
], signal
);
605 if (signal
< fl
->map
[i
].signal
[line
->dir
]){
609 fl
->map
[i
].lines
[line
->dir
] = line
->index
;
610 fl
->map
[i
].signal
[line
->dir
] = signal
;
616 debug_map_image(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
617 guint32
*data
= (guint32
*)fl
->debug
->imageData
;
618 memset(data
, 0, sparrow
->in
.size
);
619 for (guint i
= 0; i
< sparrow
->in
.pixcount
; i
++){
620 data
[i
] |= (fl
->map
[i
].signal
[SPARROW_HORIZONTAL
] >> 1) << sparrow
->in
.gshift
;
621 data
[i
] |= (fl
->map
[i
].signal
[SPARROW_VERTICAL
] >> 1) << sparrow
->in
.rshift
;
623 MAYBE_DEBUG_IPL(fl
->debug
);
626 /* draw the line (in sparrow->colour) */
628 draw_line(GstSparrow
* sparrow
, sparrow_line_t
*line
, guint8
*out
){
629 guint32
*p
= (guint32
*)out
;
630 guint32 colour
= sparrow
->out
.colours
[sparrow
->colour
];
632 if (line
->dir
== SPARROW_HORIZONTAL
){
633 p
+= line
->offset
* sparrow
->out
.width
;
634 for (i
= 0; i
< sparrow
->out
.width
; i
++){
639 guint32
*p
= (guint32
*)out
;
641 for(i
= 0; i
< sparrow
->out
.height
; i
++){
643 p
+= sparrow
->out
.width
;
649 jump_state(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, edges_state_t state
){
650 if (state
== EDGES_NEXT_STATE
){
657 case EDGES_FIND_NOISE
:
658 sparrow
->countdown
= sparrow
->lag
+ 2;
660 case EDGES_FIND_LINES
:
661 sparrow
->countdown
= sparrow
->lag
+ 2;
663 case EDGES_FIND_CORNERS
:
664 sparrow
->countdown
= 4;
666 GST_DEBUG("jumped to state %d\n", fl
->state
);
671 /* show each line for 2 frames, then wait sparrow->lag frames, leaving line on
675 draw_lines(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, guint8
*in
, guint8
*out
)
677 sparrow_line_t
*line
= fl
->shuffled_lines
[fl
->current
];
678 sparrow
->countdown
--;
679 memset(out
, 0, sparrow
->out
.size
);
680 if (sparrow
->countdown
){
681 GST_DEBUG("current %d line %p\n", fl
->current
, line
);
682 draw_line(sparrow
, line
, out
);
685 /*show nothing, look for result */
686 look_for_line(sparrow
, in
, fl
, line
);
688 debug_map_image(sparrow
, fl
);
691 if (fl
->current
== fl
->n_lines
){
692 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
695 sparrow
->countdown
= sparrow
->lag
+ 2;
700 #define LINE_THRESHOLD 32
703 find_threshold(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
, guint8
*in
, guint8
*out
)
705 memset(out
, 0, sparrow
->out
.size
);
706 /*XXX should average/median over a range of frames */
707 if (sparrow
->countdown
== 0){
708 memcpy(fl
->threshold
->imageData
, in
, sparrow
->in
.size
);
709 /*add a constant, and smooth */
710 cvAddS(fl
->threshold
, cvScalarAll(LINE_THRESHOLD
), fl
->working
, NULL
);
711 cvSmooth(tmp
, fl
->threshold_im
, CV_GAUSSIAN
, 3, 0, 0, 0);
712 //cvSmooth(fl->working, fl->threshold, CV_MEDIAN, 3, 0, 0, 0);
713 jump_state(sparrow
, fl
, EDGES_NEXT_STATE
);
715 sparrow
->countdown
--;
718 /*match up lines and find corners */
720 find_corners(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
)
722 sparrow
->countdown
--;
723 switch(sparrow
->countdown
){
725 make_clusters(sparrow
, fl
);
728 make_corners(sparrow
, fl
);
731 make_map(sparrow
, fl
);
734 corners_to_lut(sparrow
, fl
);
737 GST_DEBUG("how did sparrow->countdown get to be %d?", sparrow
->countdown
);
738 sparrow
->countdown
= 4;
740 return sparrow
->countdown
;
744 INVISIBLE sparrow_state
745 mode_find_edges(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
746 sparrow_find_lines_t
*fl
= (sparrow_find_lines_t
*)sparrow
->helper_struct
;
748 case EDGES_FIND_NOISE
:
749 find_threshold(sparrow
, fl
, in
, out
);
751 case EDGES_FIND_LINES
:
752 draw_lines(sparrow
, fl
, in
, out
);
754 case EDGES_FIND_CORNERS
:
755 if (find_corners(sparrow
, fl
))
757 return SPARROW_NEXT_STATE
;
758 case EDGES_NEXT_STATE
:
759 break; /*shush gcc */
761 return SPARROW_STATUS_QUO
;
766 finalise_find_edges(GstSparrow
*sparrow
){
767 sparrow_find_lines_t
*fl
= (sparrow_find_lines_t
*)sparrow
->helper_struct
;
768 //DEBUG_FIND_LINES(fl);
769 if (sparrow
->save
&& *(sparrow
->save
)){
770 GST_DEBUG("about to save to %s\n", sparrow
->save
);
771 dump_edges_info(sparrow
, fl
, sparrow
->save
);
774 cvReleaseImage(&fl
->debug
);
777 free(fl
->shuffled_lines
);
781 cvReleaseImage(&fl
->threshold
);
782 cvReleaseImage(&fl
->working
);
783 cvReleaseImageHeader(&fl
->input
);
785 sparrow
->helper_struct
= NULL
;
790 setup_colour_shifts(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
791 switch (sparrow
->colour
){
794 fl
->shift1
= sparrow
->in
.gshift
;
795 fl
->shift2
= sparrow
->in
.gshift
;
797 case SPARROW_MAGENTA
:
798 fl
->shift1
= sparrow
->in
.rshift
;
799 fl
->shift2
= sparrow
->in
.bshift
;
806 init_find_edges(GstSparrow
*sparrow
){
807 gint32 w
= sparrow
->out
.width
;
808 gint32 h
= sparrow
->out
.height
;
810 sparrow_find_lines_t
*fl
= zalloc_aligned_or_die(sizeof(sparrow_find_lines_t
));
811 sparrow
->helper_struct
= (void *)fl
;
813 gint h_lines
= (h
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
814 gint v_lines
= (w
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
815 gint n_lines
= (h_lines
+ v_lines
);
816 gint n_corners
= (h_lines
* v_lines
);
817 fl
->n_hlines
= h_lines
;
818 fl
->n_vlines
= v_lines
;
819 fl
->n_lines
= n_lines
;
821 fl
->h_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
) * n_lines
);
822 fl
->shuffled_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
*) * n_lines
);
823 GST_DEBUG("shuffled lines, malloced %p\n", fl
->shuffled_lines
);
825 fl
->map
= zalloc_aligned_or_die(sizeof(sparrow_intersect_t
) * sparrow
->in
.pixcount
);
826 fl
->clusters
= zalloc_or_die(n_corners
* sizeof(sparrow_cluster_t
));
827 fl
->mesh
= zalloc_aligned_or_die(n_corners
* sizeof(sparrow_corner_t
));
829 sparrow_line_t
*line
= fl
->h_lines
;
830 sparrow_line_t
**sline
= fl
->shuffled_lines
;
831 int offset
= LINE_PERIOD
/ 2;
833 for (i
= 0, offset
= LINE_PERIOD
/ 2; offset
< h
;
834 i
++, offset
+= LINE_PERIOD
){
835 line
->offset
= offset
;
836 line
->dir
= SPARROW_HORIZONTAL
;
843 /*now add the vertical lines */
845 for (i
= 0, offset
= LINE_PERIOD
/ 2; offset
< w
;
846 i
++, offset
+= LINE_PERIOD
){
847 line
->offset
= offset
;
848 line
->dir
= SPARROW_VERTICAL
;
854 //DEBUG_FIND_LINES(fl);
856 GST_DEBUG("allocated %d lines, status %d\n", n_lines
, line
- fl
->h_lines
);
859 for (i
= 0; i
< n_lines
- 1; i
++){
860 int j
= RANDINT(sparrow
, 0, n_lines
);
861 sparrow_line_t
*tmp
= fl
->shuffled_lines
[j
];
862 fl
->shuffled_lines
[j
] = fl
->shuffled_lines
[i
];
863 fl
->shuffled_lines
[i
] = tmp
;
866 setup_colour_shifts(sparrow
, fl
);
868 if (sparrow
->reload
&& *(sparrow
->reload
)){
869 GST_DEBUG("sparrow>reload is %s\n", sparrow
->reload
);
870 read_edges_info(sparrow
, fl
, sparrow
->reload
);
871 memset(fl
->map
, 0, sizeof(sparrow_intersect_t
) * sparrow
->in
.pixcount
);
872 //memset(fl->clusters, 0, n_corners * sizeof(sparrow_cluster_t));
873 memset(fl
->mesh
, 0, n_corners
* sizeof(sparrow_corner_t
));
874 jump_state(sparrow
, fl
, EDGES_FIND_CORNERS
);
878 jump_state(sparrow
, fl
, EDGES_FIND_NOISE
);
880 /* opencv images for threshold finding */
881 CvSize size
= {sparrow
->in
.width
, sparrow
->in
.height
};
882 fl
->working
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);
883 fl
->threshold
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);
885 /*input has no data allocated -- it uses latest frame*/
886 fl
->input
= init_ipl_image(&sparrow
->in
, PIXSIZE
);
888 //DEBUG_FIND_LINES(fl);
890 fl
->debug
= cvCreateImage(size
, IPL_DEPTH_8U
, PIXSIZE
);