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
* PIXSIZE
;
50 guint8
*line
= out
+ shape
->y
* stride
+ shape
->x
* PIXSIZE
;
51 for(y
= 0; y
< (guint
)shape
->h
; y
++){
52 memset(line
, 255, shape
->w
* PIXSIZE
);
57 static void draw_shapes(GstSparrow
*sparrow
, guint8
*out
){
59 sparrow_shape_t
*shape
;
60 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
61 shape
= sparrow
->shapes
+ i
;
62 switch (shape
->shape
){
64 goto done
; /* an empty one ends the list */
66 vertical_line(sparrow
, out
, shape
->x
);
69 horizontal_line(sparrow
, out
, shape
->x
);
72 rectangle(sparrow
, out
, shape
);
75 memset(out
, 255, sparrow
->out
.size
);
84 init_one_square(GstSparrow
*sparrow
, sparrow_shape_t
* shape
){
85 shape
->shape
= RECTANGLE
;
86 shape
->w
= CALIBRATE_SELF_SIZE
;
87 shape
->h
= CALIBRATE_SELF_SIZE
;
88 shape
->x
= RANDINT(sparrow
, sparrow
->out
.width
/ 8,
89 sparrow
->out
.width
* 7 / 8 - shape
->w
);
90 shape
->y
= RANDINT(sparrow
, sparrow
->out
.height
/ 8,
91 sparrow
->out
.height
* 7 / 8 - shape
->h
);
94 static void calibrate_init_squares(GstSparrow
*sparrow
){
96 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
97 init_one_square(sparrow
, &(sparrow
->shapes
[i
]));
101 /*fake other projection */
102 static void add_random_signal(GstSparrow
*sparrow
, guint8
*out
){
104 static sparrow_shape_t shapes
[MAX_CALIBRATE_SHAPES
];
105 static int been_here
= 0;
106 static int countdown
= 0;
109 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
110 init_one_square(sparrow
, &shapes
[i
]);
116 countdown
= on
? RANDINT(sparrow
, CALIBRATE_ON_MIN_T
, CALIBRATE_ON_MAX_T
) :
117 RANDINT(sparrow
, CALIBRATE_ON_MIN_T
, CALIBRATE_ON_MAX_T
);
120 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
121 rectangle(sparrow
, out
, &shapes
[i
]);
129 calibrate_init_lines(GstSparrow
*sparrow
){
131 sparrow_shape_t
* shape
= sparrow
->shapes
;
132 for (i
= 0; i
< MAX_CALIBRATE_SHAPES
; i
++){
137 shape
[i
].shape
= NO_SHAPE
;
139 /* shape[0] will be set to vertical or horizontal in due course */
146 record_calibration(GstSparrow
*sparrow
, gint32 offset
, int signal
){
147 //signal = (signal != 0);
148 sparrow
->lag_table
[offset
].record
<<= 1;
149 sparrow
->lag_table
[offset
].record
|= signal
;
153 colour_coded_pixel(guint32
* pixel
, guint32 lag
, guint32 shift
){
155 if (shift
< 9 * CCP_SCALE
){
158 *pixel
= (guint32
)-1;
162 guint32 c
= lag_false_colour
[lag
];
163 guint32 mask
= (1 << (8 - shift
)) - 1;
165 mask
|= (mask
<< 16); //XXX LUT would be quicker
175 int64_to_binary_string(char *s
, guint64 n
){
176 /* s should be a *65* byte array */
178 for (i
= 0; i
< 64; i
++){
179 s
[i
] = (n
& (1ULL << (63 - i
))) ? '*' : '.';
186 /*return 1 if a reasonably likely lag has been found */
189 find_lag(GstSparrow
*sparrow
){
192 guint32
*frame
= (guint32
*)sparrow
->debug_frame
;
194 memset(frame
, 0, sparrow
->in
.size
);
196 guint64 target_pattern
= sparrow
->lag_record
;
197 guint32 overall_best
= (guint32
)-1;
198 guint32 overall_lag
= 0;
199 char pattern_debug
[65];
200 int votes
[MAX_CALIBRATION_LAG
] = {0};
202 GST_DEBUG("pattern: %s %llx\n", int64_to_binary_string(pattern_debug
, target_pattern
),
205 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
206 guint64 record
= sparrow
->lag_table
[i
].record
;
207 if (record
== 0 || ~record
== 0){
208 /*ignore this one! it'll never usefully match. */
209 //frame[i] = 0xffffffff;
213 guint64 mask
= ((guint64
)-1) >> MAX_CALIBRATION_LAG
;
214 guint32 best
= hamming_distance64(record
, target_pattern
, mask
);
217 for (j
= 1; j
< MAX_CALIBRATION_LAG
; j
++){
218 /*latest frame is least significant bit
219 >> pushes into future,
221 record is presumed to be a few frames past
222 relative to main record, so we push it back.
226 guint32 d
= hamming_distance64(record
, target_pattern
, mask
);
233 colour_coded_pixel(&frame
[i
], lag
, best
);
236 if (best
<= CALIBRATE_MAX_VOTE_ERROR
){
237 votes
[lag
] += 1 >> (CALIBRATE_MAX_VOTE_ERROR
- best
);
240 if (best
< overall_best
){
243 char pattern_debug2
[65];
244 guint64 r
= sparrow
->lag_table
[i
].record
;
245 GST_DEBUG("Best now: lag %u! error %u pixel %u\n"
247 "pattern: %s %llx\n",
248 overall_lag
, overall_best
, i
,
249 int64_to_binary_string(pattern_debug
, r
), r
,
250 int64_to_binary_string(pattern_debug2
, target_pattern
), target_pattern
256 debug_frame(sparrow
, sparrow
->debug_frame
, sparrow
->in
.width
, sparrow
->in
.height
);
259 /*calculate votes winner, as a check for winner-takes-all */
261 int popular_votes
= -1;
262 for (i
= 0; i
< MAX_CALIBRATION_LAG
; i
++){
263 if(votes
[i
] > popular_votes
){
264 popular_votes
= votes
[i
];
268 GST_DEBUG("%d votes for %d\n", votes
[i
], i
);
271 /*votes and best have to agree, and best has to be low */
272 if (overall_best
<= CALIBRATE_MAX_BEST_ERROR
&&
273 overall_lag
== popular_lag
){
274 sparrow
->lag
= overall_lag
;
282 abs_diff(GstSparrow
*sparrow
, guint8
*a
, guint8
*b
, guint8
*target
){
283 sparrow
->in_ipl
[0]->imageData
= (char*) a
;
284 sparrow
->in_ipl
[1]->imageData
= (char*) b
;
285 sparrow
->in_ipl
[2]->imageData
= (char*) target
;
286 cvAbsDiff(sparrow
->in_ipl
[0], sparrow
->in_ipl
[1], sparrow
->in_ipl
[2]);
290 threshold(GstSparrow
*sparrow
, guint8
*frame
, guint8
*target
, guint threshold
){
291 sparrow
->in_ipl
[0]->imageData
= (char*) frame
;
292 sparrow
->in_ipl
[1]->imageData
= (char*) target
;
293 //cvAbsDiff(sparrow->in_ipl[0], sparrow->in_ipl[1], sparrow->in_ipl[2]);
294 cvCmpS(sparrow
->in_ipl
[0], (double)threshold
, sparrow
->in_ipl
[1], CV_CMP_GT
);
299 reset_find_self(GstSparrow
*sparrow
, gint first
){
301 calibrate_init_squares(sparrow
);
302 sparrow
->countdown
= CALIBRATE_INITIAL_WAIT
;
305 sparrow
->countdown
= CALIBRATE_RETRY_WAIT
;
309 /*compare the frame to the new one. regions of change should indicate the
313 calibrate_find_self(GstSparrow
*sparrow
, guint8
*in
){
314 //GST_DEBUG("finding square\n");
316 if(sparrow
->prev_frame
){
317 //debug_frame(sparrow, sparrow->in_frame, sparrow->in.width, sparrow->in.height);
319 guint32
*frame
= (guint32
*)in
;
320 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
321 int signal
= (((frame
[i
] >> sparrow
->in
.gshift
) & 255) > CALIBRATE_SIGNAL_THRESHOLD
);
322 record_calibration(sparrow
, i
, signal
);
324 if (sparrow
->countdown
== 0){
325 res
= find_lag(sparrow
);
327 GST_DEBUG("lag is set at %u! after %u cycles\n", sparrow
->lag
, sparrow
->frame_count
);
330 reset_find_self(sparrow
, 0);
334 sparrow
->countdown
--;
339 static gboolean
cycle_pattern(GstSparrow
*sparrow
){
340 gboolean on
= sparrow
->calibrate
.on
;
341 if (sparrow
->calibrate
.wait
== 0){
344 sparrow
->calibrate
.wait
= RANDINT(sparrow
, CALIBRATE_ON_MIN_T
, CALIBRATE_ON_MAX_T
);
347 sparrow
->calibrate
.wait
= RANDINT(sparrow
, CALIBRATE_OFF_MIN_T
, CALIBRATE_OFF_MAX_T
);
349 sparrow
->calibrate
.on
= on
;
350 sparrow
->calibrate
.transitions
++;
352 sparrow
->calibrate
.wait
--;
353 sparrow
->lag_record
= (sparrow
->lag_record
<< 1) | on
;
354 //GST_DEBUG("lag record %llx, on %i\n", sparrow->lag_record, on);
359 see_grid(GstSparrow
*sparrow
, guint8
*in
){
363 find_grid(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
364 see_grid(sparrow
, in
);
365 int on
= cycle_pattern(sparrow
);
366 memset(out
, 0, sparrow
->out
.size
);
368 draw_shapes(sparrow
, out
);
373 see_edges(GstSparrow
*sparrow
, guint8
*in
){
374 /* there is a big flash of white. or not. look for the largest area of
377 perhaps, threshold at one or two levels. or more. if they agree they are
380 or floodfill (and fill in)
382 canny edge detection:
383 http://opencv.willowgarage.com/documentation/c/feature_detection.html#canny
387 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
389 guint32 signal
= sparrow
->work_frame
[i
* PIXSIZE
+ 2];
390 if (signal
> CALIBRATE_WAIT_SIGNAL_THRESHOLD
){
391 sparrow
->countdown
= WAIT_COUNTDOWN
;
399 INVISIBLE sparrow_state
400 mode_find_edges(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
401 see_edges(sparrow
, in
);
402 int on
= cycle_pattern(sparrow
);
404 memset(out
, 255, sparrow
->out
.size
);
407 memset(out
, 0, sparrow
->out
.size
);
409 return SPARROW_STATUS_QUO
;
413 init_find_edges(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
414 //reset_pattern(GstSparrow *sparrow);
417 INVISIBLE sparrow_state
418 mode_find_self(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
419 if(calibrate_find_self(sparrow
, in
)){
420 return SPARROW_WAIT_FOR_GRID
;
422 gboolean on
= cycle_pattern(sparrow
);
423 memset(out
, 0, sparrow
->out
.size
);
425 draw_shapes(sparrow
, out
);
427 #if FAKE_OTHER_PROJECTION
428 add_random_signal(sparrow
, out
);
430 return SPARROW_STATUS_QUO
;
433 /* wait for the other projector to stop changing: sufficient to look for no
434 significant changes for as long as the longest pattern interval */
437 wait_for_blank(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
439 abs_diff(sparrow
, in
, sparrow
->prev_frame
, sparrow
->work_frame
);
440 for (i
= 0; i
< sparrow
->in
.pixcount
; i
++){
441 guint32 signal
= sparrow
->work_frame
[i
* PIXSIZE
+ 2]; //possibly R, G, or B, but never A
442 if (signal
> CALIBRATE_WAIT_SIGNAL_THRESHOLD
){
443 sparrow
->countdown
= WAIT_COUNTDOWN
;
447 memset(out
, 0, sparrow
->out
.size
);
448 sparrow
->countdown
--;
449 return (sparrow
->countdown
== 0);
453 INVISIBLE sparrow_state
454 mode_wait_for_grid(GstSparrow
*sparrow
, guint8
*in
, guint8
*out
){
455 if (wait_for_blank(sparrow
, in
, out
)){
456 return SPARROW_FIND_EDGES
;
458 return SPARROW_STATUS_QUO
;
461 INVISIBLE
calibrate_init_grid(GstSparrow
*sparrow
){}