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"
35 /*3 pixels manhatten distance makes you an outlier */
36 #define OUTLIER_THRESHOLD 3 << (SPARROW_FIXED_POINT)
37 #define OUTLIER_PENALTY 8
43 #define SPARROW_MAP_LUT_SHIFT 1
44 #define SPARROW_FP_2_LUT (SPARROW_FIXED_POINT - SPARROW_MAP_LUT_SHIFT)
46 #define OFFSET(x, y, w)((((y) * (w)) >> SPARROW_FIXED_POINT) + ((x) >> SPARROW_FIXED_POINT))
48 static void corners_to_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
49 sparrow_map_t
*map
= &sparrow
->map
; /*rows in sparrow->out */
50 guint8
*mask
= sparrow
->screenmask
; /*mask in sparrow->in */
51 sparrow_corner_t
*mesh
= fl
->mesh
; /*maps regular points in ->out to points in ->in */
53 size_t point_memsize
= (sizeof(sparrow_map_point_t
) * sparrow
->out
.pixcount
/ LINE_PERIOD
) + 1;
54 size_t row_memsize
= sizeof(sparrow_map_row_t
) * sparrow
->out
.height
+ 1;
55 map
->point_mem
= malloc_aligned_or_die(point_memsize
);
56 map
->rows
= zalloc_aligned_or_die(row_memsize
);
58 int mesh_w
= fl
->n_vlines
;
59 int mesh_h
= fl
->n_hlines
;
60 int in_w
= sparrow
->in
.width
;
61 int mcy
, mmy
, mcx
; /*Mesh Corner|Modulus X|Y*/
65 sparrow_map_row_t
*row
= map
->rows
;
66 sparrow_map_point_t
*p
= map
->point_mem
;
67 sparrow_corner_t
*mesh_row
= mesh
;
68 for(mcy
= 0; mcy
< mesh_h
; mcy
++){
69 for (mmy
= 0; mmy
< LINE_PERIOD
; mmy
++){
70 sparrow_corner_t
*mesh_square
= mesh_row
;
74 for(mcx
= 0; mcx
< mesh_w
; mcx
++){
75 if (mesh_square
->used
){
76 int iy
= mesh_square
->in_y
+ mmy
* mesh_square
->dyv
;
77 int ix
= mesh_square
->in_x
+ mmy
* mesh_square
->dxv
;
78 int ii
= OFFSET(ix
, iy
, in_w
);
79 int ii_end
= OFFSET(ix
+ (LINE_PERIOD
- 1) * mesh_square
->dxh
,
80 iy
+ (LINE_PERIOD
- 1) * mesh_square
->dyh
, in_w
);
81 int start_on
= mask
[ii
];
82 int end_on
= mask
[ii_end
];
83 if(start_on
&& end_on
){
84 /*add the point, maybe switch on */
85 if (row
->start
== row
->end
){/* if both are 0 */
86 row
->start
= mcx
* LINE_PERIOD
;
90 p
->dx
= mesh_square
->dxh
;
91 p
->dy
= mesh_square
->dyh
;
95 /*add the point, switch off somewhere in the middle*/
96 for (x
= 1; x
< LINE_PERIOD
; x
++){
97 iy
+= mesh_square
->dyh
;
98 ix
+= mesh_square
->dxh
;
99 ii
= OFFSET(ix
, iy
, in_w
);
101 /*point is not in the same column with the others,
102 but sparrow knows this because the row->start says so */
103 row
->start
= mcx
+ x
;
106 p
->dx
= mesh_square
->dxh
;
107 p
->dy
= mesh_square
->dyh
;
114 /* add some, switch off */
115 for (x
= 1; x
< LINE_PERIOD
; x
++){
116 iy
+= mesh_square
->dyh
;
117 ix
+= mesh_square
->dxh
;
118 ii
= OFFSET(ix
, iy
, in_w
);
127 start > end: this is first off pixel.
128 start == end: row hasn't started (both 0)
129 start < end: both are set -- row is done
131 if (row
->start
> row
->end
){
132 row
->end
= mcx
* LINE_PERIOD
;
134 else if (row
->start
< row
->end
){
148 corners_to_full_lut(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
149 sparrow_corner_t
*mesh
= fl
->mesh
; /*maps regular points in ->out to points in ->in */
150 size_t lutsize
= sizeof(sparrow_map_lut_t
) * sparrow
->out
.pixcount
;
151 sparrow_map_lut_t
*map_lut
= malloc_aligned_or_die(lutsize
);
153 int mesh_w
= fl
->n_vlines
;
154 int mesh_h
= fl
->n_hlines
;
155 int mcy
, mmy
, mcx
, mmx
; /*Mesh Corner|Modulus X|Y*/
157 sparrow_corner_t
*mesh_row
= mesh
;
158 for(mcy
= 0; mcy
< mesh_h
; mcy
++){
159 for (mmy
= 0; mmy
< LINE_PERIOD
; mmy
++){
160 sparrow_corner_t
*mesh_square
= mesh_row
;
161 for(mcx
= 0; mcx
< mesh_w
; mcx
++){
162 int iy
= mesh_square
->in_y
+ mmy
* mesh_square
->dyv
;
163 int ix
= mesh_square
->in_x
+ mmy
* mesh_square
->dxv
;
164 for (mmx
= 0; mmx
< LINE_PERIOD
; mmx
++, i
++){
165 map_lut
[i
].x
= ix
>> SPARROW_FP_2_LUT
;
166 map_lut
[i
].y
= iy
>> SPARROW_FP_2_LUT
;
167 ix
+= mesh_square
->dxh
;
168 iy
+= mesh_square
->dyh
;
175 sparrow
->map_lut
= map_lut
;
183 find_corners(GstSparrow
*sparrow
, guint8
*in
, sparrow_find_lines_t
*fl
){
185 int width
= fl
->n_vlines
;
186 int height
= fl
->n_hlines
;
188 sparrow_cluster_t
*clusters
= malloc_or_die(height
* width
* sizeof(sparrow_cluster_t
));
189 sparrow_corner_t
*corners
= zalloc_aligned_or_die(height
* width
* sizeof(sparrow_corner_t
));
192 for (y
= 0; y
< sparrow
->in
.height
; y
++){
193 for (x
= 0; x
< sparrow
->in
.width
; x
++){
194 sparrow_intersect_t
*p
= &fl
->map
[y
* sparrow
->in
.width
+ x
];
195 /*remembering that 0 is valid as a line no, but not as a signal */
196 if (! p
->signal
[SPARROW_HORIZONTAL
] ||
197 ! p
->signal
[SPARROW_VERTICAL
]){
200 /*This one is lobbying for the position of a corner.*/
202 /*XXX what to do in the case that there is no intersection? often cases
203 this will happen in the dark bits and be OK. But if it happens in the
205 /*linearise the xy coordinates*/
206 int vline
= p
->lines
[SPARROW_VERTICAL
];
207 int hline
= p
->lines
[SPARROW_HORIZONTAL
];
208 sparrow_cluster_t
*cluster
= &clusters
[vline
* height
+ hline
];
212 cluster
->voters
[n
].x
= x
<< SPARROW_FIXED_POINT
;
213 cluster
->voters
[n
].y
= y
<< SPARROW_FIXED_POINT
;
214 cluster
->voters
[n
].signal
= (SIG_WEIGHT
+ p
->signal
[SPARROW_HORIZONTAL
]) *
215 (SIG_WEIGHT
+ p
->signal
[SPARROW_VERTICAL
]);
217 /*these next two could of course be computed from the offset */
220 GST_DEBUG("more than 8 pixels at cluster for corner %d, %d\n",
222 /*if this message ever turns up, replace the weakest signals or add
229 for (y
= 0; y
< height
; y
++){
230 for (x
= 0; x
< width
; x
++, i
++){
232 1. centre of gravity (x,y, weighted average)
233 2. discard outliers? look for connectedness? but if 2 are outliers?
235 sparrow_cluster_t
*cluster
= clusters
+ i
;
236 if (cluster
->n
== 0){
247 for (j
= 0; j
< cluster
->n
; j
++){
248 votes
+= cluster
->voters
[j
].signal
;
249 ysum
+= cluster
->voters
[j
].y
* cluster
->voters
[j
].signal
;
250 xsum
+= cluster
->voters
[j
].x
* cluster
->voters
[j
].signal
;
253 /* don't diminish signal altogether. The previous iteration's means
257 xmean
= xsum
/ votes
;
258 ymean
= ysum
/ votes
;
262 for (j
= 0; j
< cluster
->n
; j
++){
263 int xdiff
= abs(cluster
->voters
[j
].x
- xmean
);
264 int ydiff
= abs(cluster
->voters
[j
].y
- ymean
);
265 devsum
+= xdiff
+ ydiff
;
266 if (xdiff
+ ydiff
> worst
){
267 worst
= xdiff
+ ydiff
;
271 /*a bad outlier has significantly greater than average deviation
272 (but how much is bad? median deviation would be more useful)*/
273 if (worst
> 3 * devsum
/ cluster
->n
){
274 /* reduce the worst ones weight. it is a silly aberration. */
275 cluster
->voters
[worstn
].signal
/= OUTLIER_PENALTY
;
276 GST_DEBUG("dropping outlier at %s,%s (mean %s,%s)\n",
277 cluster
->voters
[worstn
].x
, cluster
->voters
[worstn
].y
, xmean
, ymean
);
282 corners
[i
].out_x
= x
* LINE_PERIOD
;
283 corners
[i
].out_y
= y
* LINE_PERIOD
;
284 corners
[i
].in_x
= xmean
;
285 corners
[i
].in_y
= ymean
;
286 corners
[i
].used
= TRUE
;
287 double div
= (double)(1 << SPARROW_FIXED_POINT
); /*for printf only*/
288 GST_DEBUG("found corner %d (%d,%d) at (%3f, %3f)\n",
289 i
, corners
[i
].out_x
, corners
[i
].out_y
,
290 xmean
/ div
, ymean
/ div
);
295 /* calculate deltas toward adjacent corners */
296 /* try to extrapolate left and up, if possible, so need to go backwards. */
297 for (y
= height
- 2; y
>= 0; y
--){
298 for (x
= width
- 2; x
>= 0; x
--){
300 if (corners
[i
].used
){
301 corners
[i
].dxh
= (corners
[i
+ 1].in_x
- corners
[i
].in_x
) / LINE_PERIOD
;
302 corners
[i
].dyh
= (corners
[i
+ 1].in_y
- corners
[i
].in_y
) / LINE_PERIOD
;
303 corners
[i
].dxv
= (corners
[i
+ width
].in_x
- corners
[i
].in_x
) / LINE_PERIOD
;
304 corners
[i
].dyv
= (corners
[i
+ width
].in_y
- corners
[i
].in_y
) / LINE_PERIOD
;
307 /*prefer copy from left unless it is itself reconstructed,
308 (for no great reason)
309 A mixed copy would be possible and better */
310 if(corners
[i
+ 1].used
&&
311 (corners
[i
+ 1].used
< corners
[i
+ width
].used
)){
312 corners
[i
].dxh
= corners
[i
+ 1].dxh
;
313 corners
[i
].dyh
= corners
[i
+ 1].dyh
;
314 corners
[i
].dxv
= corners
[i
+ 1].dxv
;
315 corners
[i
].dyv
= corners
[i
+ 1].dyv
;
316 corners
[i
].in_x
= corners
[i
+ 1].in_x
- corners
[i
+ 1].dxh
* LINE_PERIOD
;
317 corners
[i
].in_y
= corners
[i
+ 1].in_y
- corners
[i
+ 1].dyh
* LINE_PERIOD
;
318 corners
[i
].out_x
= corners
[i
+ 1].out_x
- 1;
319 corners
[i
].out_y
= corners
[i
+ 1].out_y
;
320 corners
[i
].used
= corners
[i
+ 1].used
+ 1;
322 else if(corners
[i
+ width
].used
){
323 corners
[i
].dxh
= corners
[i
+ width
].dxh
;
324 corners
[i
].dyh
= corners
[i
+ width
].dyh
;
325 corners
[i
].dxv
= corners
[i
+ width
].dxv
;
326 corners
[i
].dyv
= corners
[i
+ width
].dyv
;
327 corners
[i
].in_x
= corners
[i
+ width
].in_x
- corners
[i
+ width
].dxv
* LINE_PERIOD
;
328 corners
[i
].in_y
= corners
[i
+ width
].in_y
- corners
[i
+ width
].dyv
* LINE_PERIOD
;
329 corners
[i
].out_x
= corners
[i
+ width
].out_x
;
330 corners
[i
].out_y
= corners
[i
+ width
].out_y
- 1;
331 corners
[i
].used
= corners
[i
+ width
].used
+ 1;
341 /* With no line drawn (in our colour) look at the background noise. Any real
342 signal has to be stringer than this.
344 XXX looking for simple maximum -- maybe heap or histogram might be better,
345 so as to be less susceptible to wierd outliers (e.g., bad pixels). */
347 look_for_threshold(GstSparrow
*sparrow
, guint8
*in
, sparrow_find_lines_t
*fl
){
351 guint32
*in32
= (guint32
*)in
;
353 for (i
= 0; i
< (int)sparrow
->in
.size
; i
++){
354 colour
= in32
[i
] & sparrow
->colour
;
355 signal
= ((colour
>> fl
->shift1
) +
356 (colour
>> fl
->shift2
)) & 0x1ff;
357 if (signal
> highest
){
361 fl
->threshold
= highest
+ 10;
362 GST_DEBUG("found maximum noise of %s, using threshold %s\n", highest
, fl
->threshold
);
367 look_for_line(GstSparrow
*sparrow
, guint8
*in
, sparrow_find_lines_t
*fl
,
368 sparrow_line_t
*line
){
372 guint32
*in32
= (guint32
*)in
;
373 for (i
= 0; i
< sparrow
->in
.size
; i
++){
374 colour
= in32
[i
] & sparrow
->colour
;
375 signal
= ((colour
>> fl
->shift1
) +
376 (colour
>> fl
->shift2
)) & 0x1ff;
377 if (signal
> fl
->threshold
){
378 if (fl
->map
[i
].lines
[line
->dir
]){
379 GST_DEBUG("HEY, expected point %d to be in line %d (dir %d)"
380 "and thus empty, but it is also in line %d\n",
381 i
, line
->index
, line
->dir
, fl
->map
[i
].lines
[line
->dir
]);
383 fl
->map
[i
].lines
[line
->dir
] = line
->index
;
384 fl
->map
[i
].signal
[line
->dir
] = signal
;
390 /* draw the line (in sparrow->colour) */
392 draw_line(GstSparrow
* sparrow
, sparrow_line_t
*line
, guint8
*out
){
393 guint32
*p
= (guint32
*)out
;
395 if (line
->dir
== SPARROW_HORIZONTAL
){
396 p
+= line
->offset
* sparrow
->out
.width
;
397 for (i
= 0; i
< sparrow
->out
.width
; i
++){
398 p
[i
] = sparrow
->colour
;
402 guint32
*p
= (guint32
*)out
;
404 for(i
= 0; i
< sparrow
->out
.height
; i
++){
405 *p
= sparrow
->colour
;
406 p
+= sparrow
->out
.width
;
411 /* show each line for 2 frames, then wait sparrow->lag frames, leaving line on
415 INVISIBLE sparrow_state
416 mode_find_edges(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
417 sparrow_find_lines_t
*fl
= (sparrow_find_lines_t
*)&sparrow
->helper_struct
;
418 sparrow_line_t
*line
= fl
->shuffled_lines
[fl
->current
];
419 sparrow
->countdown
--;
420 memset(out
, 0, sparrow
->out
.size
);
421 if (sparrow
->countdown
){
422 /* show the line except on the first round, when we find a threshold*/
424 draw_line(sparrow
, line
, out
);
428 /*show nothing, look for result */
430 if (fl
->current
== fl
->n_lines
){
433 look_for_line(sparrow
, in
, fl
, line
);
437 look_for_threshold(sparrow
, in
, fl
);
439 sparrow
->countdown
= sparrow
->lag
+ 2;
441 return SPARROW_STATUS_QUO
;
443 /*match up lines and find corners */
444 find_corners(sparrow
, in
, fl
);
445 corners_to_lut(sparrow
, fl
);
447 /*free stuff!, including fl, and reset pointer to NULL*/
449 return SPARROW_NEXT_STATE
;
454 setup_colour_shifts(GstSparrow
*sparrow
, sparrow_find_lines_t
*fl
){
455 switch (sparrow
->colour
){
458 fl
->shift1
= sparrow
->in
.gshift
;
459 fl
->shift2
= sparrow
->in
.gshift
;
461 case SPARROW_MAGENTA
:
462 fl
->shift1
= sparrow
->in
.rshift
;
463 fl
->shift2
= sparrow
->in
.bshift
;
469 init_find_edges(GstSparrow
*sparrow
){
470 gint32 w
= sparrow
->out
.width
;
471 gint32 h
= sparrow
->out
.height
;
473 sparrow_find_lines_t
*fl
= zalloc_aligned_or_die(sizeof(sparrow_find_lines_t
));
474 sparrow
->helper_struct
= (void *)fl
;
476 gint h_lines
= (h
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
477 gint v_lines
= (w
+ LINE_PERIOD
- 1) / LINE_PERIOD
;
478 gint n_lines
= (h_lines
+ v_lines
);
479 fl
->n_hlines
= h_lines
;
480 fl
->n_vlines
= v_lines
;
481 fl
->n_lines
= n_lines
;
483 fl
->h_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
) * n_lines
);
484 fl
->shuffled_lines
= malloc_aligned_or_die(sizeof(sparrow_line_t
*) * n_lines
);
485 fl
->map
= zalloc_aligned_or_die(sizeof(sparrow_intersect_t
) * sparrow
->in
.pixcount
);
487 sparrow_line_t
*line
= fl
->h_lines
;
488 sparrow_line_t
**sline
= fl
->shuffled_lines
;
489 int offset
= LINE_PERIOD
/ 2;
491 for (i
= 0, offset
= LINE_PERIOD
/ 2; offset
< h
;
492 i
++, offset
+= LINE_PERIOD
){
493 line
->offset
= offset
;
494 line
->dir
= SPARROW_HORIZONTAL
;
501 /*now add the vertical lines */
503 for (i
= 0, offset
= LINE_PERIOD
/ 2; offset
< w
;
504 i
++, offset
+= LINE_PERIOD
){
505 line
->offset
= offset
;
506 line
->dir
= SPARROW_VERTICAL
;
513 GST_DEBUG("allocated %s lines, used %s\n", n_lines
, line
- fl
->h_lines
);
515 /*now shuffle (triangluar, to no particular advantage) */
516 for (i
= 0; i
< n_lines
- 1; i
++){
517 int j
= RANDINT(sparrow
, i
+ 1, n_lines
);
518 sparrow_line_t
*tmp
= fl
->shuffled_lines
[j
];
519 fl
->shuffled_lines
[j
] = fl
->shuffled_lines
[i
];
520 fl
->shuffled_lines
[i
] = tmp
;
523 setup_colour_shifts(sparrow
, fl
);
524 sparrow
->countdown
= sparrow
->lag
+ 2;
526 fl
->mesh
= malloc_aligned_or_die(sizeof(sparrow_corner_t
) * h_lines
* v_lines
);