www.pudn.com > reacTIVision-1.3.rar > fidtrack120.c


/*
  Fiducial tracking library.
  Copyright (C) 2004 Ross Bencina 

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#ifndef M_PI
#define M_PI        3.14159265358979323846
#endif

#include "fidtrack120.h"

#include 
#include 


typedef struct RegionPoint{
    Region *r;
    float x, y;
}RegionPoint;


static float squared_distance( RegionPoint* a, RegionPoint* b )
{
    float x = a->x - b->x;
    float y = a->y - b->y;
    return x*x + y*y;
}


static double calculate_angle( double dx, double dy )
{
    double result;

    if( fabs(dx) < 0.000001 )
        result = M_PI * .5;
    else
        result = fabs( atan(dy/dx) );

    if( dx < 0 )
        result = M_PI - result;

    if( dy < 0 )
        result = -result;

    if( result < 0 )
        result += 2. * M_PI;

    return result;
}


static int intersects( RegionPoint *A, RegionPoint *B, RegionPoint *C, RegionPoint *D )
{
    float a, b, c;

    float ABx = (B->x - A->x);
    float ABy = (B->y - A->y);

    float CDx = (D->x - C->x);
    float CDy = (D->y - C->y);

    a = CDx * ABy - CDy * ABx;

    if( a == 0. || a == -0. )
        return 0;

    b = (C->y - A->y) * ABx + (-C->x + A->x) * ABy;

    c = b / a;

    if (c >= 0. && c <= 1.)
        return 1;
    else
        return 0;
}

/* given a sequence of points [begin, end), find the three which are
    closest to a straight line.

    the algorithm used is as follows: permute through all combinations of
    3 point sequences. for each 3 point sequence form a triangle. the set
    of points which are considered to be on a straight line are those where
    the ratio of the hypotenuse to the sum of the two other sides is
    smallest. (ie the flattest triangle)
*/
static void find_straightest_line_of_3( RegionPoint *seq_begin, RegionPoint *seq_end,
    RegionPoint **line_start, RegionPoint **line_middle, RegionPoint **line_end )
{
    RegionPoint *i, *j, *k;
    float best_distance = 0x7FFFFFFF;
    float n;

    for( i = seq_begin; i != seq_end; ++i ){

        for( j = seq_begin; j != seq_end; ++j ){

            if( j != i ){
                for( k = seq_begin; k != seq_end; ++k ){

                    if( k != i && k != j ){

                        assert( i != j );
                        assert( j != k );
                        assert( i != k );

                        n = (float)(squared_distance( i, j ) + squared_distance( j, k ))
                                / (float)squared_distance( i, k );

                        if( n < best_distance ){
                            best_distance = n;
                            *line_start = i;
                            *line_middle = j;
                            *line_end = k;
                        }
                    }
                }
            }
        }
    }
}

/*
    length may not exceed 16
*/
static int has_all_symbols_once( int *sequence, int length )
{
    int i;
    int symbol;
    char symbols_present[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    assert( length < 16 );

    for( i=0; i < length; ++i ){
	symbol = sequence[i];
	if( symbol < 0 || symbol >= length )
		return 0; /* symbol value is out of range */
	else if( symbols_present[ symbol ] )
		return 0; /* symbol is duplicate */
	else
		symbols_present[ symbol ] = 1;
    }

    return 1;
}

/*
    see header file for a description of this function
*/
int fiducial120_id_from_symbol_sequence( int *sequence, int length )
{
    int id = 0;
    int factorial[8] = { 1, 1, 2, 6, 24, 120, 720, 5040 };
    int mapping[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };
    int i, j;

    assert( sequence[0] == 0 );
    assert( length <= 8 );

    /* ignore the first slot, which should be zero */
    sequence = sequence + 1;
    length = length - 1;

    for( i=0; i < length; ++i ){
        id += mapping[ (sequence[i]-1) ] * factorial[(length-1) - i];
        for( j = (sequence[i]-1) + 1; j < length; ++j ){
            mapping[j]--;
        }
    }

    return id;
}


static int extract_region_points( RegionPoint *dest, int max_count,
        Region *container, int level )
{
    int result = 0;
    int i;
    int j = 0;

    for( i=0; i < container->adjacent_region_count; ++i ){
        Region *r = container->adjacent_regions[i];
        if( r->level == level ){
            dest[j].r = r;
            /* compute container region centers (center of aligned bounding box) */
            dest[j].x = (float)(r->left + r->right) / 2.0f;
            dest[j].y = (float)(r->top + r->bottom) / 2.0f;
            ++result;
            assert( result <= max_count );
            ++j;
        }
    }

    return result;
}

#define SWAP( T, a, b ) { T temp___xxuusds = a; a = b; b = temp___xxuusds; }

static void compute_location_angle_and_id_for_6_symbol_fiducial( Fiducial120 *f, Region *r )
{
    RegionPoint points[6];
    RegionPoint points2[6];
    RegionPoint *i;
    RegionPoint *start1, *middle1, *end1, *start2, *middle2, *end2;
    int jj;

    assert( r->adjacent_region_count == 7 );

    /* load container region ptrs into points array */

    jj = extract_region_points( points, 6, r, LEAF_CONTAINER_LEVEL );

    /* expect 6 container regions */
	assert( jj == 6 );

    /* find first line */
    find_straightest_line_of_3( &points[0], &points[6], &start1, &middle1, &end1 );

    /* remove first line from points list */
    {
        jj = 0;
        for( i = &points[0]; i != &points[6]; ++i ){
            if( i != start1 && i != middle1 && i != end1 )
                points2[jj++] = *i;
        }

        assert( jj == 3 );
    }

    /* find second line */
    find_straightest_line_of_3( &points2[0], &points2[3], &start2, &middle2, &end2 );


    /* reorder points so that start1 contains the 1 symbol (adjacent region count == 2) */

#define SYMBOL_1_ADJACENT_REGION_COUNT (2)

    if( start1->r->adjacent_region_count == SYMBOL_1_ADJACENT_REGION_COUNT ){

        /* nothing */

    }else if( end1->r->adjacent_region_count == SYMBOL_1_ADJACENT_REGION_COUNT ){

        SWAP( RegionPoint*, start1, end1 );

    }else{

        SWAP( RegionPoint*, start1, start2 );
        SWAP( RegionPoint*, middle1, middle2 );
        SWAP( RegionPoint*, end1, end2 );

        if( start1->r->adjacent_region_count == SYMBOL_1_ADJACENT_REGION_COUNT ){

            /* nothing */

        }else if( end1->r->adjacent_region_count == SYMBOL_1_ADJACENT_REGION_COUNT ){

            SWAP( RegionPoint*, start1, end1 );
        }
    }

    /* make start2 the point adjacent to end1 so that the codes can be read
        in clockwise order */    
    if( intersects( start1, end2, start2, end1 ) )
        SWAP( RegionPoint*, start2, end2 );

#if 0
    /* simple fiducial center calculation using only the middle point of each line */
    f->x = (middle1->x + middle2->x) / 2.0f;
    f->y = (middle1->y + middle2->y) / 2.0f;
#else
    /* center calculation using all points */
    f->x = ((start1->x + middle1->x + end1->x) + (start2->x + middle2->x + end2->x)) / 6.0f;
    f->y = ((start1->y + middle1->y + end1->y) + (start2->y + middle2->y + end2->y)) / 6.0f;
#endif


#if 0
    /* simple angle calculation using only the line start1->end1 */
    {
        double dx = end1->x - start1->x;
        double dy = -(end1->y - start1->y);
        f->angle = (float)calculate_angle( dx, dy );
    }
#else
    /* angle calculation by averaging most available angles */
    {
        /* vectors parallel to longer side of the fiducial */

        double dx = end1->x - start1->x;
        double dy = -(end1->y - start1->y);

        dx += middle1->x - start1->x;
        dy += -(middle1->y - start1->y);

        dx += end1->x - middle1->x;
        dy += -(end1->y - middle1->y);

        dx += start2->x - end2->x;
        dy += -(start2->y - end2->y);

        dx += middle2->x - end2->x;
        dy += -(middle2->y - end2->y);

        dx += start2->x - middle2->x;
        dy += -(start2->y - middle2->y);

        /* vectors parallel to the shorter side of the fiducial, rotated 90 degrees */

        dx += start1->y - end2->y;
        dy += start1->x - end2->x;

        dx += middle1->y - middle2->y;
        dy += middle1->x - middle2->x;

        dx += end1->y - start2->y;
        dy += end1->x - start2->x;

        dx /= 9.;
        dy /= 9.;

        f->angle = (float)calculate_angle( dx, dy );
    }
#endif

    /* compute fiducial id */

    assert( MAX_FIDUCIAL120_SYMBOLS >= 6 );

    /* convert adjacent_region_count to 0 based symbol id by subtracting
	2 -- 1 for the root container, and 1 because there is always one
	leaf to encode a symbol (eg it is 1 based)
    */

    f->symbol_sequence[0] = start1->r->adjacent_region_count - 2;
    f->symbol_sequence[1] = middle1->r->adjacent_region_count - 2;
    f->symbol_sequence[2] = end1->r->adjacent_region_count - 2;
    f->symbol_sequence[3] = start2->r->adjacent_region_count - 2;
    f->symbol_sequence[4] = middle2->r->adjacent_region_count - 2;
    f->symbol_sequence[5] = end2->r->adjacent_region_count - 2;
    f->sequence_length = 6;

    if( f->symbol_sequence[0] == 0 && has_all_symbols_once( f->symbol_sequence, f->sequence_length ) )        
        f->id = fiducial120_id_from_symbol_sequence( f->symbol_sequence, f->sequence_length );
    else
        f->id = INVALID_FIDUCIAL120_ID;
}

/* -------------------------------------------------------------------------- */

int find_fiducials120( Fiducial120 *fiducials, int count,
        PartialSegmentTopology *pst, Segmenter *segments )
{
    int i = 0;
    Region *next;

    initialize_segment_topology( pst, segments, 6 );

    next = pst->root_regions_head.next;
    while( next != &pst->root_regions_head ){

        compute_location_angle_and_id_for_6_symbol_fiducial( &fiducials[i], next );

        next = next->next;
        ++i;
        if( i >= count )
            return i;
    }

    return i;
}