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.
20 #include "gstsparrow.h"
29 horizontal_line(GstSparrow
*sparrow
, guint8
*out
, guint32 y
){
30 guint stride
= sparrow
->out
.width
* PIXSIZE
;
31 guint8
*line
= out
+ y
* stride
;
32 memset(line
, 255, stride
);
36 vertical_line(GstSparrow
*sparrow
, guint8
*out
, guint32 x
){
38 guint32
*p
= (guint32
*)out
;
40 for(y
= 0; y
< (guint
)(sparrow
->out
.height
); y
++){
42 p
+= sparrow
->out
.width
;
47 rectangle(GstSparrow
*sparrow
, guint8
*out
, sparrow_shape_t
*shape
){
49 guint stride
= sparrow
->out
.width
;
50 guint32
*line
= ((guint32
*)out
) + shape
->y
* stride
+ shape
->x
;
51 for (x
= 0; x
< shape
->w
; x
++){
52 line
[x
] = sparrow
->calibrate
.outcolour
;
54 guint32
*line2
= line
+ stride
;
55 for(y
= 1; y
< shape
->h
; y
++){
56 memcpy(line2
, line
, shape
->w
* PIXSIZE
);
61 static void draw_shapes(GstSparrow
*sparrow
, guint8
*out
){
63 sparrow_shape_t
*shape
;
64 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
65 shape
= sparrow
->shapes
+ i
;
66 switch (shape
->shape
){
68 goto done
; /* an empty one ends the list */
70 vertical_line(sparrow
, out
, shape
->x
);
73 horizontal_line(sparrow
, out
, shape
->x
);
76 rectangle(sparrow
, out
, shape
);
79 memset(out
, 255, sparrow
->out
.size
);
88 init_one_square(GstSparrow
*sparrow
, sparrow_shape_t
* shape
){
89 shape
->shape
= RECTANGLE
;
90 shape
->w
= CALIBRATE_SELF_SIZE
;
91 shape
->h
= CALIBRATE_SELF_SIZE
;
92 shape
->x
= RANDINT(sparrow
, sparrow
->out
.width
/ 8,
93 sparrow
->out
.width
* 7 / 8 - shape
->w
);
94 shape
->y
= RANDINT(sparrow
, sparrow
->out
.height
/ 8,
95 sparrow
->out
.height
* 7 / 8 - shape
->h
);
99 /*fake other projection */
100 static void add_random_signal(GstSparrow
*sparrow
, guint8
*out
){
102 static sparrow_shape_t shapes
[MAX_CALIBRATE_SHAPES
];
103 static int been_here
= 0;
104 static int countdown
= 0;
107 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
108 init_one_square(sparrow
, &shapes
[i
]);
114 countdown
= on
? RANDINT(sparrow
, CALIBRATE_ON_MIN_T
, CALIBRATE_ON_MAX_T
) :
115 RANDINT(sparrow
, CALIBRATE_ON_MIN_T
, CALIBRATE_ON_MAX_T
);
118 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
119 rectangle(sparrow
, out
, &shapes
[i
]);
125 static gboolean
cycle_pattern(GstSparrow
*sparrow
){
126 gboolean on
= sparrow
->calibrate
.on
;
127 if (sparrow
->calibrate
.wait
== 0){
130 sparrow
->calibrate
.wait
= RANDINT(sparrow
, CALIBRATE_ON_MIN_T
, CALIBRATE_ON_MAX_T
);
133 sparrow
->calibrate
.wait
= RANDINT(sparrow
, CALIBRATE_OFF_MIN_T
, CALIBRATE_OFF_MAX_T
);
135 sparrow
->calibrate
.on
= on
;
136 sparrow
->calibrate
.transitions
++;
138 sparrow
->calibrate
.wait
--;
139 sparrow
->lag_record
= (sparrow
->lag_record
<< 1) | on
;
140 //GST_DEBUG("lag record %llx, on %i\n", sparrow->lag_record, on);
147 init_lines(GstSparrow
*sparrow
){
149 sparrow_shape_t
* shape
= sparrow
->shapes
;
150 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
155 shape
[i
].shape
= NO_SHAPE
;
157 /* shape[0] will be set to vertical or horizontal in due course */
161 colour_coded_pixel(guint32
* pixel
, guint32 lag
, guint32 shift
){
163 if (shift
< 9 * CCP_SCALE
){
166 *pixel
= (guint32
)-1;
170 guint32 c
= lag_false_colour
[lag
];
171 guint32 mask
= (1 << (8 - shift
)) - 1;
173 mask
|= (mask
<< 16); //XXX LUT would be quicker
183 int64_to_binary_string(char *s
, guint64 n
){
184 /* s should be a *65* byte array */
186 for (i
= 0; i
< 64; i
++){
187 s
[i
] = (n
& (1ULL << (63 - i
))) ? '*' : '.';
194 /*return 1 if a reasonably likely lag has been found */
197 find_lag(GstSparrow
*sparrow
){
200 guint32
*frame
= (guint32
*)sparrow
->debug_frame
;
202 memset(frame
, 0, sparrow
->in
.size
);
204 guint64 target_pattern
= sparrow
->lag_record
;
205 guint32 overall_best
= (guint32
)-1;
206 guint32 overall_lag
= 0;
207 char pattern_debug
[65];
208 int votes
[MAX_CALIBRATION_LAG
] = {0};
210 GST_DEBUG("pattern: %s %llx\n", int64_to_binary_string(pattern_debug
, target_pattern
),
213 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
214 guint64 record
= sparrow
->lag_table
[i
].record
;
215 if (record
== 0 || ~record
== 0){
216 /*ignore this one! it'll never usefully match. */
217 //frame[i] = 0xffffffff;
221 guint64 mask
= ((guint64
)-1) >> MAX_CALIBRATION_LAG
;
222 guint32 best
= hamming_distance64(record
, target_pattern
, mask
);
225 for (j
= 1; j
< MAX_CALIBRATION_LAG
; j
++){
226 /*latest frame is least significant bit
227 >> pushes into future,
229 record is presumed to be a few frames past
230 relative to main record, so we push it back.
234 guint32 d
= hamming_distance64(record
, target_pattern
, mask
);
241 colour_coded_pixel(&frame
[i
], lag
, best
);
244 if (best
<= CALIBRATE_MAX_VOTE_ERROR
){
245 votes
[lag
] += 1 >> (CALIBRATE_MAX_VOTE_ERROR
- best
);
248 if (best
< overall_best
){
251 char pattern_debug2
[65];
252 guint64 r
= sparrow
->lag_table
[i
].record
;
253 GST_DEBUG("Best now: lag %u! error %u pixel %u\n"
255 "pattern: %s %llx\n",
256 overall_lag
, overall_best
, i
,
257 int64_to_binary_string(pattern_debug
, r
), r
,
258 int64_to_binary_string(pattern_debug2
, target_pattern
), target_pattern
264 debug_frame(sparrow
, sparrow
->debug_frame
, sparrow
->in
.width
, sparrow
->in
.height
);
267 /*calculate votes winner, as a check for winner-takes-all */
269 int popular_votes
= -1;
270 for (i
= 0; i
< MAX_CALIBRATION_LAG
; i
++){
271 if(votes
[i
] > popular_votes
){
272 popular_votes
= votes
[i
];
276 GST_DEBUG("%d votes for %d\n", votes
[i
], i
);
279 /*votes and best have to agree, and best has to be low */
280 if (overall_best
<= CALIBRATE_MAX_BEST_ERROR
&&
281 overall_lag
== popular_lag
){
282 sparrow
->lag
= overall_lag
;
289 record_calibration(GstSparrow
*sparrow
, gint32 offset
, int signal
){
290 //signal = (signal != 0);
291 sparrow
->lag_table
[offset
].record
<<= 1;
292 sparrow
->lag_table
[offset
].record
|= signal
;
296 INVISIBLE sparrow_state
297 mode_find_self(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
298 int ret
= SPARROW_STATUS_QUO
;
300 guint32
*frame
= (guint32
*)in
;
301 /* record the current signal */
302 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
303 int signal
= (((frame
[i
] >> sparrow
->in
.gshift
) & 255) > CALIBRATE_SIGNAL_THRESHOLD
);
304 record_calibration(sparrow
, i
, signal
);
306 if (sparrow
->countdown
== 0){
307 /* analyse the signal */
308 int r
= find_lag(sparrow
);
310 GST_DEBUG("lag is set at %u! after %u cycles\n", sparrow
->lag
, sparrow
->frame_count
);
311 ret
= SPARROW_NEXT_STATE
;
314 init_find_self(sparrow
);
318 memset(out
, 0, sparrow
->out
.size
);
319 gboolean on
= cycle_pattern(sparrow
);
321 draw_shapes(sparrow
, out
);
323 #if FAKE_OTHER_PROJECTION
324 add_random_signal(sparrow
, out
);
326 sparrow
->countdown
--;
332 abs_diff(GstSparrow
*sparrow
, guint8
*a
, guint8
*b
, guint8
*target
){
333 sparrow
->in_ipl
[0]->imageData
= (char*) a
;
334 sparrow
->in_ipl
[1]->imageData
= (char*) b
;
335 sparrow
->in_ipl
[2]->imageData
= (char*) target
;
336 cvAbsDiff(sparrow
->in_ipl
[0], sparrow
->in_ipl
[1], sparrow
->in_ipl
[2]);
343 see_grid(GstSparrow
*sparrow
, guint8
*in
){
347 find_grid(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
348 see_grid(sparrow
, in
);
349 int on
= cycle_pattern(sparrow
);
350 memset(out
, 0, sparrow
->out
.size
);
352 draw_shapes(sparrow
, out
);
357 see_screen(GstSparrow
*sparrow
, guint8
*in
){
358 /* there is a big flash of white. or not. look for the largest area of
361 perhaps, threshold at one or two levels. or more. if they agree they are
364 or floodfill (and fill in)
366 canny edge detection:
367 http://opencv.willowgarage.com/documentation/c/feature_detection.html#canny
371 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
373 guint32 signal
= sparrow
->work_frame
[i
* PIXSIZE
+ 2];
374 if (signal
> CALIBRATE_WAIT_SIGNAL_THRESHOLD
){
375 sparrow
->countdown
= WAIT_COUNTDOWN
;
381 INVISIBLE sparrow_state
382 mode_find_edges(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
383 return SPARROW_STATUS_QUO
;
386 INVISIBLE sparrow_state
387 mode_find_screen(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
388 see_screen(sparrow
, in
);
389 int on
= cycle_pattern(sparrow
);
391 memset(out
, 255, sparrow
->out
.size
);
394 memset(out
, 0, sparrow
->out
.size
);
396 return SPARROW_STATUS_QUO
;
399 /* wait for the other projector to stop changing, for as many frames as are
400 set in sparrow->countdown. When sparrow->countdown reaches 0, return 1.
402 If sparrow->countdown is already 0, do nothing.
404 If something happens, reset sparrow->countdown to <blanktime>.
407 wait_for_blank(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
, int blanktime
){
408 if (sparrow
->countdown
){
410 abs_diff(sparrow
, in
, sparrow
->prev_frame
, sparrow
->work_frame
);
411 //Use the green channel
412 for (i
= (sparrow
->in
.gshift
>> 8);
413 i
< sparrow
->in
.pixcount
* PIXSIZE
;
415 guint8 signal
= sparrow
->work_frame
[i
];
416 if (signal
> CALIBRATE_WAIT_SIGNAL_THRESHOLD
){
417 sparrow
->countdown
= blanktime
;
421 memset(out
, 0, sparrow
->out
.size
);
422 sparrow
->countdown
--;
424 return (sparrow
->countdown
== 0);
429 new_calibration_colour(GstSparrow
*sparrow
){
430 int c
= RANDINT(sparrow
, 1, 3);
431 sparrow
->calibrate
.incolour
= sparrow
->in
.colours
[c
];
432 sparrow
->calibrate
.outcolour
= sparrow
->out
.colours
[c
];
433 //sparrow->calibrate.wait == 0;
436 /* Choose between green or magenta, randomly and iteratively, until the
437 other one chooses something else. But first wait for blank? */
439 INVISIBLE sparrow_state
440 mode_pick_colour(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
441 if(wait_for_blank(sparrow
, in
, out
, WAIT_COUNTDOWN
)){
442 new_calibration_colour(sparrow
);
443 sparrow
->calibrate
.wait
= sparrow
->lag
+ 2;
445 sparrow
->calibrate
.wait
--;
446 return SPARROW_STATUS_QUO
;
450 INVISIBLE sparrow_state
451 mode_wait_for_grid(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
452 if (wait_for_blank(sparrow
, in
, out
, WAIT_COUNTDOWN
)){
453 return SPARROW_NEXT_STATE
;
455 return SPARROW_STATUS_QUO
;
461 init_wait_for_grid(GstSparrow
*sparrow
){}
464 init_find_grid(GstSparrow
*sparrow
){}
467 init_find_self(GstSparrow
*sparrow
){
468 sparrow
->calibrate
.incolour
= sparrow
->in
.colours
[SPARROW_WHITE
];
469 sparrow
->calibrate
.outcolour
= sparrow
->out
.colours
[SPARROW_WHITE
];
470 if (! sparrow
->n_shapes
){
472 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
473 init_one_square(sparrow
, &(sparrow
->shapes
[i
]));
475 sparrow
->n_shapes
= MAX_CALIBRATE_SHAPES
;
476 sparrow
->countdown
= CALIBRATE_INITIAL_WAIT
;
479 sparrow
->countdown
= CALIBRATE_RETRY_WAIT
;
484 init_pick_colour(GstSparrow
*sparrow
)
486 sparrow
->countdown
= WAIT_COUNTDOWN
;
490 init_find_screen(GstSparrow
*sparrow
){
491 //reset_pattern(GstSparrow *sparrow);
495 init_find_edges(GstSparrow
*sparrow
){}