サーチ 応用例
[サンプルコード一覧]


回転対応グレイサーチ

/*
    回転しているパターン画像のサーチを目的とした、グレイサーチのサンプルコードです.
    
    従来の結果に加え、検出された際の角度を出力します.
    また、同一座標で複数角度のパターンが検出された場合、スコアが最大のものを回答します.
*/

#include <stdio.h>
#include "fie.h"
#include "oal_aloc.h"
#include <math.h>

/*
    サンプル用回転グレイサーチ結果構造体
*/
typedef struct
{
    INT x;
    INT y;
    INT score;
    INT angle;
}SMP_RESULT_ROT;

#define DEG_TO_RAD(a) ( (a)*PI/180.0 )
#define RAD_TO_DEG(a) ( (a)*180.0/PI )
#define SAMPLE_PRINT

//結果表示用マクロ.
#ifdef SAMPLE_PRINT
#define PRINT_RESULT( result, result_num )\
    {\
        INT num;\
        puts("result");\
        for( num = 0; num < result_num; num++ )\
        {\
            printf("No.%2d : score %d, ( %d, %d ) angle:%d\n",\
            num, result[num].score, result[num].x/100, result[num].y/100, result[num].angle/10 );\
        }\
    }
#else
#define PRINT_RESULT( result, result_num )
#endif

//------------------------------------------------
// 関数プロトタイプ

INT fnSMP_gray_search_rot( 
    FHANDLE                 hgs,
    FHANDLE                 hpattern,
    FHANDLE                 himage,
    BOX_T                   search_window,
    INT                     threshold_mid,
    INT                     threshold_final,
    INT                     edge_detect,
    INT                     reverse_mode,
    INT                     pitch_x,
    INT                     pitch_y,
    INT                     pitch_angle,
    INT                     first_unit,
    INT                     last_unit,
    enum f_gs2_subpxl_neib  subpxl_neib,
    INT                     max_result_num,
    INT                     base_angle,
    INT                     plus_rot,
    INT                     minus_rot,
    SMP_RESULT_ROT          *result,
    INT                     *result_num
    );
static BOOL fnSMP_angle_diff_comp( INT a, INT b, INT pitch_angle );
static INT FVALGAPI fnSMP_comp_ex( VOID *search_result, const VOID *p_index1, const VOID *p_index2 );
static INT fnSMP_sort_result( FHANDLE hvarray, SMP_RESULT_ROT *result, INT pitch_x, INT pitch_y, INT pitch_angle, INT max_result_num, INT *result_num );
static INT fnSMP_result_add( F_GS_RESULT *result, FHANDLE hvarray, INT result_num, INT angle );

//------------------------------------------------
// 実装

/*
    回転対応 グレイサーチ.
    
    指定範囲内の角度を1度刻みでパターンを回転させグレイサーチを実行します.
    回転サーチ結果において、同一座標に複数出力されたとき角度差が pitch_angle 以上の場合は、別のサーチ結果と判断し出力します.

    base_angle を基準に±方向に指定角度分1度ずつ回転しサーチします.

    引数:
        [in,out]    hgs                 グレイサーチオブジェクト
        [in]        hpattern            グレイサーチパターンオブジェクト
        [in]        himage              サーチ対象となる画像オブジェクト ( type : uc8 )
                                        チャネル数は1でなければなりません.
                                        また、パタンのサイズよりも縦横共に大きい必要があります.
        [in]        search_window       サーチウインドウ
                                        画像オブジェクトの中でサーチを行う処理範囲を himage 上の座標で指定します.
                                        himage の内部に納まるように座標を指定する必要があります.
                                        また、パタンのサイズよりも大きい必要があります.
        [in]        threshold_mid       途中相関値 ( 1000 〜 9999 )
        [in]        threshold_final     最終相関値 ( 1000 〜 9999 )
        [in]        edge_detect         サーチウインドウ周囲接触フラグ
                                         - TRUE サーチウインドウに接したパターンも検出
                                         - FALSE サーチウインドウに接したパターンを検出しない
        [in]        reverse_mode        反転パターン検出モードフラグ 濃度反転しているパターンも検出するかどうかを指定します.
                                         - TRUE 反転パターンも検出
                                         - FALSE 反転パターンを検出しない
        [in]        pitch_x             最終的な回答の出力時に、同一の解であるとみなす範囲を指定します.
                                        0を入力した場合、パターンサイズの半分になります.
        [in]        pitch_y             最終的な回答の出力時に、同一の解であるとみなす範囲を指定します.
                                        0を入力した場合、パターンサイズの半分になります.
        [in]        pitch_angle         最終的な回答の出力時に、同一の解であるとみなす範囲を指定します.(degree)
                                        0を入力した場合、パターン回転総角度数の半分となります.
                                        ( 0 <= pitch_angle <= 180 )
        [in]        first_unit          サーチ開始圧縮度を指定します.( 0 〜 9 ) 
                                        圧縮度がnのとき、2の-n乗倍の画像でサーチを開始します.
        [in]        last_unit           サーチ終了圧縮度を指定します.( 0 〜 9 ) 
                                        圧縮度がnのとき、2の-n乗倍の画像でサーチを完了します.
        [in]        subpxl_neib         精サーチ・サブピクセル推定に用いる近傍を指定します.
                                        8近傍を指定する場合、サーチ終了圧縮度(last_unit)は 0 を推奨します.
                                         - F_GS2_SUBPXL_NEIB_4  4近傍
                                         - F_GS2_SUBPXL_NEIB_8  8近傍
        [in]        max_result_num      サーチ結果取得個数.
                                        相関値の上位からこの個数だけ回答します.
                                        result はこの個数よりも大きい配列でなければいけません.
        [in]        base_angle          処理開始角度 degreeの10倍値 ( 0 〜 3600)
                                        開始角度から時計回りに回転します.
        [in]        plus_rot            処理開始角度からの処理範囲プラス方向 degreeの10倍値 ( 0 〜 1800)
        [in]        minus_rot           処理開始角度からの処理範囲マイナス方向 degreeの10倍値 ( 0 〜 1800)
        [out]       result              サーチ結果
                                        サーチスコア( 0 〜 10000 )と結果座標の100倍値と角度の10倍値を返します.
        [out]       result_num          見つかったサーチ結果の個数 ( 0 〜 max_result_num )

    戻り値:
        F_ERR_NONE                      正常終了
        F_ERR_INVALID_PARAM             引数異常、範囲外の引数を与えた(pitchがマイナス等)
        F_ERR_INVALID_OBJECT            引数オブジェクトの種別が異常、引数オブジェクトがNULL
        F_ERR_NOMEMORY                  メモリ不足
        F_ERR_INVALID_IMAGE             対応していない画像が渡された
        F_ERR_NO_LICENCE                ライセンスエラー、または未初期化エラー
*/
INT fnSMP_gray_search_rot( 
    FHANDLE                 hgs,
    FHANDLE                 hpattern, 
    FHANDLE                 himage, 
    BOX_T                   search_window,
    INT                     threshold_mid,
    INT                     threshold_final,
    INT                     edge_detect,
    INT                     reverse_mode,
    INT                     pitch_x,
    INT                     pitch_y,
    INT                     pitch_angle,
    INT                     first_unit,
    INT                     last_unit,
    enum f_gs2_subpxl_neib  subpxl_neib,
    INT                     max_result_num,
    INT                     base_angle,
    INT                     plus_rot,
    INT                     minus_rot,
    SMP_RESULT_ROT          *result,
    INT                     *result_num
    )
{
    INT ret = F_ERR_UNKNOWN;
    
    FHANDLE hrot_pattern = NULL;
    INT clip_width = I32_MAX;//特に問題ない場合はI32_MAXを使用する.
    INT clip_height = I32_MAX;
    INT rotate_pattern = 0;//回転方法 Nearest neighbor. 
    F_GS_RESULT *result_buff = NULL;
    FHANDLE hvary   = NULL;

    //1度刻みで回転させる.
    INT inc_num = 10;
    INT total_count = ( plus_rot + minus_rot ) / inc_num + 1;

    //Pitch x yが0を指定されていた際のPitch取得用画像.
    FHANDLE pat_img = NULL;

    /* パラメータ確認 */
    ret = F_ERR_INVALID_PARAM;
    if( base_angle < 0 || base_angle > 3600 || plus_rot < 0 || plus_rot > 1800 || minus_rot < 0 || minus_rot > 1800 )
        goto EXIT;
    if( pitch_x < 0 || pitch_y < 0 )
        goto EXIT;
    if( pitch_angle < 0 || pitch_angle > 180 )
        goto EXIT;
    if( NULL == result || NULL == result_num )
        goto EXIT;

    ret = F_ERR_NOMEMORY;
    result_buff = (F_GS_RESULT*)fnOAL_calloc( max_result_num, sizeof(F_GS_RESULT) );
    if( NULL == result_buff ) goto EXIT;

    hvary = fnFIE_vectarray_alloc( (enum f_objtag)0, sizeof(SMP_RESULT_ROT), 0 );
    if( NULL == hvary ) goto EXIT;
    
    //Pitch設定.
    if( pitch_x == 0 || pitch_y == 0 )
    {
        ret = fnFIE_gs2_pattern_get_image( hpattern, &pat_img );
        if( F_ERR_NONE != ret ) goto EXIT;

        if( pitch_x == 0 )
            pitch_x = fnFIE_img_get_width( pat_img ) / 2;
        if( pitch_y == 0 )
            pitch_y = fnFIE_img_get_height( pat_img ) / 2;
    }
    if( pitch_angle == 0 )
            pitch_angle = ( plus_rot + minus_rot ) / 2;

    {
        INT i;
        INT start = base_angle - minus_rot;
        INT end = base_angle + plus_rot;
        INT rot_angle;
        FHANDLE himage_buff = himage;

        for( i = start; i <= end; i+= inc_num )
        {
            //角度を0〜360°の範囲に収める.
            rot_angle = i % 3600;
            if( rot_angle < 0 )
                rot_angle += 3600;

            ret = fnFIE_gs2_pattern_rotate(
                hpattern,
                rot_angle,
                clip_width,
                clip_height,
                rotate_pattern,
                &hrot_pattern
                );
            if( F_ERR_NONE != ret ) goto EXIT;

            ret =fnFIE_gs2_search_enforce2(
                    hgs,
                    hrot_pattern,
                    himage_buff,//二回目以降NULL.
                    search_window,
                    threshold_mid, threshold_final,
                    edge_detect,
                    reverse_mode,
                    pitch_x, pitch_y,
                    first_unit,
                    last_unit,
                    subpxl_neib,
                    result_buff,
                    max_result_num,
                    result_num
                    );
            if( F_ERR_NONE != ret ) goto EXIT;
            
            ret = fnSMP_result_add( result_buff, hvary, *result_num, rot_angle );
            if( F_ERR_NONE != ret ) goto EXIT;

            //次の回転パターン登録のためパターン開放 及び NULL初期化.
            fnFIE_free_object( hrot_pattern ); hrot_pattern = NULL;
            //二回目以降ターゲット画像をNULLにし、継続サーチへ切り替える.
            himage_buff = NULL;
        }

        //結果出力.
        ret = fnSMP_sort_result( hvary, result, pitch_x, pitch_y, pitch_angle, max_result_num, result_num );
        if( F_ERR_NONE != ret ) goto EXIT;
    }

EXIT:
    fnOAL_free( result_buff );
    fnFIE_free_object( hrot_pattern );
    fnFIE_free_object( pat_img );

    fnFIE_vectarray_free( hvary );

    return ret;
}

/*
    角度差判定.
    
    角度を単位ベクトルを求め内積を取ることでaとbの角度差を取得します。
    角度1と角度2の差が比較角度未満の場合はTRUE(1)返し、差が比較角度以上場合はFALSE(0)を返します.

    引数:
        [in]    a               角度1 (degree)
        [in]    b               角度2 (degree)
        [in]    pitch_angle     比較角度 (degree)

    戻り値:
        TRUE    角度差が比較角度未満
        FALSE   角度差が比較角度以上
*/
static BOOL fnSMP_angle_diff_comp( INT a, INT b, INT pitch_angle )
{
    DOUBLE rad_a = DEG_TO_RAD( a );
    DOUBLE rad_b = DEG_TO_RAD( b );
    DOUBLE pitch_angle_cos = cos( DEG_TO_RAD( pitch_angle ) );//比較角度の cosθ値.
    DOUBLE x1, y1;
    DOUBLE x2, y2;
    DOUBLE angle_sub_cos;
    
    //aの単位ベクトル.
    x1 = cos( rad_a );
    y1 = sin( rad_a );
    //bの単位ベクトル.
    x2 = cos( rad_b );
    y2 = sin( rad_b );

    //内積計算.
    angle_sub_cos = ( ( x1 * x2 ) + ( y1 * y2 ) );

    /*
        角度での比較では無く、角度のコサイン値による比較.
        角度が大きくなるとコサイン値が小さくなる(0〜PI:1〜-1)ため.
        角度の大小と比較方法が異なる.
    */
    if( angle_sub_cos > pitch_angle_cos )
        return TRUE;//角度差 < 比較角度
    else
        return FALSE;//角度差 >= 比較角度
}

/*
    比較関数.
    1. スコア降順
    2. 角度昇順
*/
static INT FVALGAPI fnSMP_comp_ex( VOID *search_result, const VOID *p_index1, const VOID *p_index2 )
{
    SMP_RESULT_ROT *result = (SMP_RESULT_ROT*)search_result;
    INT index1 = *(INT*)p_index1;
    INT index2 = *(INT*)p_index2;
    SMP_RESULT_ROT *result1 = result + index1;
    SMP_RESULT_ROT *result2 = result + index2;

    if( result1->score < result2->score )
        return 1;
    else if( result1->score > result2->score )
        return -1;
    else
    { /* result1->score == result2->score */
        if( result1->angle < result2->angle )
            return -1;
        else if( result1->angle > result2->angle )
            return 1;
        else
            return 0;
    }
}

/*
    結果のソート及び重複除去

        [in]        hvarray         サーチ結果(vectarray配列オブジェクト)
        [out]       result          ソート後サーチ結果
        [in]        pitch_x         最小x間隔(1以上)
        [in]        pitch_y         最小y間隔(1以上)
        [in]        pitch_angle     最小angle間隔(1以上)
        [in]        max_result_num  最大で上位何個回答するか(1以上)
        [out]       result_num      有効な結果の個数

        F_ERR_NONE                  正常終了
        F_ERR_INVALID_PARAM         引数異常
        F_ERR_NOMEMORY              メモリ不足
*/
static INT fnSMP_sort_result( 
    FHANDLE hvarray,
    SMP_RESULT_ROT *result,
    INT pitch_x, INT pitch_y, INT pitch_angle,
    INT max_result_num, INT *result_num
    )
{
    INT ret = F_ERR_UNKNOWN;

    SMP_RESULT_ROT *p_hvarray = NULL;
    INT ary_size;
    INT *sort_indexes = NULL;
    INT *hit_flags = NULL;
    
    //パラメータ確認.
    ret = F_ERR_INVALID_PARAM;
    if( NULL == hvarray || NULL == result || NULL == result_num ) goto EXIT;
    if( max_result_num < 1 ) goto EXIT;
    if( pitch_x < 1 || pitch_y < 1 || pitch_angle < 1 ) goto EXIT;

    ary_size = fnFIE_vectarray_getnum( hvarray );
    if( ary_size < 0 ) goto EXIT;

    ret = F_ERR_NOMEMORY;
    sort_indexes = (INT*)fnOAL_malloc( sizeof(INT) * ary_size );
    if( NULL == sort_indexes ) goto EXIT;
    hit_flags = (INT*)fnOAL_malloc( sizeof(INT) * ary_size );
    if( NULL == hit_flags ) goto EXIT;

    //indexes, hit_flags 初期化.
    {
        INT i;
        for( i = 0; i < ary_size; i++ )
        {
            sort_indexes[i] = i;
            hit_flags[i] = 0;
        }
    }

    //相関値が高い+角度が小さい順にソート.
    //最終結果で重複した際に角度が小さい方を優先する.
    p_hvarray = (SMP_RESULT_ROT*)fnFIE_vectarray_getptr( hvarray );
    //fnFIE_qsort()を用いると、配列の中身を直接入替えてしまい元のデータが消えてしまう.
    //そこで、結果配列のインデックスと等価なsort_indexesをソートすることで、元の配列を保持しつつソートを行う.
    fnFIE_qsort_ex( sort_indexes, ary_size, sizeof(INT), fnSMP_comp_ex, (SMP_RESULT_ROT*)p_hvarray );

    //ピッチを100倍値へ換算.
    pitch_x *= 100;
    pitch_y *= 100;

    //サーチ結果 重複フラグチェック.
    {
        INT i;
        for( i = 0; i < ary_size; i++ )
        {
            SMP_RESULT_ROT result = *( ( SMP_RESULT_ROT* )fnFIE_vectarray_getat( hvarray, sort_indexes[i] ) );
            INT x = result.x;
            INT y = result.y;
            INT angle = result.angle;
            INT j;
            for( j = i+1; j < ary_size; j++ )
            {
                SMP_RESULT_ROT dresult = *( ( SMP_RESULT_ROT* )fnFIE_vectarray_getat( hvarray, sort_indexes[j] ) );
                INT dx = abs( dresult.x - x );
                INT dy = abs( dresult.y - y );
                INT dangle = dresult.angle;
                //10倍値されている角度を元に戻して判定.
                BOOL angle_judge = fnSMP_angle_diff_comp( angle/10, dangle/10, pitch_angle );

                //重複と判別されたインデックスのフラグをオン(1)していく.
                if( ( dx < pitch_x ) && ( dy < pitch_y ) && ( angle_judge ) )
                {
                    hit_flags[j] = 1;
                }
            }
        }
    }

    //結果出力.
    {
        INT i;
        INT dst_ary_index = 0;
        for( i = 0; i < ary_size; i++ )
        {
            //上記フラグチェックで重複していないものを結果として出力していく.
            if( !(hit_flags[i]) )
            {
                SMP_RESULT_ROT result_buff = *( ( SMP_RESULT_ROT* )fnFIE_vectarray_getat( hvarray, sort_indexes[i] ) );
                result[dst_ary_index].score = result_buff.score;
                result[dst_ary_index].x     = result_buff.x;
                result[dst_ary_index].y     = result_buff.y;
                result[dst_ary_index].angle = result_buff.angle;
                dst_ary_index++;
            }
            //個数とindexの差は上記処理で埋められているのでそのまま比較.
            if( max_result_num <= dst_ary_index )
            {
                dst_ary_index = max_result_num;
                break;
            }
        }
        
        *result_num = dst_ary_index;
    }

    ret = F_ERR_NONE;

EXIT:
    fnOAL_free( sort_indexes );
    fnOAL_free( hit_flags );
    return ret;
}

/*
    サーチ結果 配列へ要素追加.

    引数:
        [in]        result      サーチ結果
        [out]       hvarray     サーチ結果配列(vectarray配列オブジェクト)
        [in]        result_num  サーチ結果個数
        [in]        angle       サーチ角度

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALD_PARAM      引数異常
        F_ERR_NOMEMORY          メモリ不足
*/
static INT fnSMP_result_add( F_GS_RESULT *result, FHANDLE hvarray, INT result_num, INT angle )
{
    INT ret = F_ERR_UNKNOWN;
    SMP_RESULT_ROT result_buff;
    INT i;

    if( NULL == result || NULL == hvarray )
        return F_ERR_INVALID_PARAM;
    if( result_num < 0 )
        return F_ERR_INVALID_PARAM;
    
    //可変長配列へデータを追加するため.
    //F_GS_RESULT → SMP_RESULT_ROT へ変更.
    for( i = 0; i < result_num; i++ )
    {
        result_buff.score   = result[i].score;
        result_buff.x       = result[i].x;
        result_buff.y       = result[i].y;
        result_buff.angle   = angle;

        ret = fnFIE_vectarray_push_back( hvarray, &result_buff );
        if( F_ERR_NONE != ret ) return ret;
    }

    return F_ERR_NONE;
}

//------------------------------------------------
// 実行サンプル

static INT execute()
{
    INT ret = F_ERR_UNKNOWN;

    FHANDLE pat_img = NULL;//パターン画像を読み込むための画像HANDLE.
    INT pattern_w, pattern_h;

    /* ---------------------- gray search params -------------------------- */
    FHANDLE hgs = NULL;
    FHANDLE himage = NULL;
    FHANDLE hpattern = NULL;

    BOX_T search_window;
    INT threshold_mid = 3500;//中間相関値.
    INT threshold_final = 5000;//最終相関値.
    INT edge_detect = TRUE;//サーチウインドウ周囲接触フラグ.
    INT reverse_mode = FALSE;//濃度反転フラグ.
    INT pitch_x = 0;
    INT pitch_y = 0;
    INT pitch_angle = 45;//pitch_angle未満の範囲を同一の範囲とみなす.
    INT first_unit = 5;//開始圧縮度.
    INT last_unit = 0;//終了圧縮度.
    enum f_gs2_subpxl_neib subpxl_neib = F_GS2_SUBPXL_NEIB_4;
    INT max_result_num = 100;//サーチ結果取得個数.
    INT base_angle = 0;//回転 処理開始位置.
    INT plus_rot = 1800;
    INT minus_rot = 1800;
    SMP_RESULT_ROT *result = NULL;
    INT result_num;

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

    //画像読込.
    //適宜目的の画像に置き換えてください.
    ret = fnFIE_load_img_file( "rot_gray_search.png", &himage, F_COLOR_IMG_TYPE_UC8 );
    if( F_ERR_NONE != ret ) goto EXIT;

    //パターン元画像読込及びパターン生成.
    ret = fnFIE_load_img_file( "rot_pattern.png", &pat_img, F_COLOR_IMG_TYPE_UC8 );
    if( F_ERR_NONE != ret ) goto EXIT;

    //回答座標を設定.
    //サンプルではパターン画像の中心とします.
    pattern_w = ( fnFIE_img_get_width( pat_img ) / 2 ) * 100;
    pattern_h = ( fnFIE_img_get_height( pat_img ) / 2 ) * 100;
    hpattern = fnFIE_gs2_pattern_alloc( pat_img, NULL, pattern_w, pattern_h, F_COMP_MODE_SMOOTH, &ret );
    if( F_ERR_NONE != ret ) goto EXIT;

    //グレイサーチオブジェクト生成.
    hgs = fnFIE_gs2_alloc( 0, 0, 0 );
    
    //サーチウインドウの設定 サーチ対象画像の中に納まるよう設定.
    search_window.st.x = 0;
    search_window.st.y = 0;
    search_window.ed.x = fnFIE_img_get_width( himage )-1;
    search_window.ed.y = fnFIE_img_get_height( himage )-1;

    //結果の保存先確保.
    result = (SMP_RESULT_ROT*)fnOAL_calloc( max_result_num, sizeof(SMP_RESULT_ROT) );
    if( NULL == result ) goto EXIT;

    //メインサンプル実行.
    ret = fnSMP_gray_search_rot( hgs,
                                 hpattern,
                                 himage,
                                 search_window,
                                 threshold_mid,
                                 threshold_final,
                                 edge_detect,
                                 reverse_mode,
                                 pitch_x,
                                 pitch_y,
                                 pitch_angle,
                                 first_unit,
                                 last_unit,
                                 subpxl_neib,
                                 max_result_num,
                                 base_angle,
                                 plus_rot,
                                 minus_rot,
                                 result,
                                 &result_num
                                );

    //結果表示 SAMPLE_PRINTが定義されている場合のみ実行されます.
    PRINT_RESULT( result, result_num );

EXIT:
    fnFIE_free_object( himage );
    fnFIE_free_object( pat_img );
    fnFIE_free_object( hpattern );
    fnFIE_free_object( hgs );
    fnOAL_free( result );

    return ret;
}

INT main( )
{
    INT ret = F_ERR_UNKNOWN;

    // FIEライブラリの使用前に必ずコールする必要があります.
    fnFIE_setup();

    ret = execute();

    // 終了処理
    fnFIE_teardown();

    return ret;
}


マルチパターングレイサーチ

微小変形する対象のサーチにおける位置決め
/*
    マルチパターングレイサーチ サンプルコード

    このサンプルコードは、複数のパターンにおいて、サーチを行い
    最もスコアが高いパターンの位置を出力することを目的としています.
    また、同一座標付近に複数パターン検出された場合、スコアが最大のものを回答します.
*/

#include <stdio.h>
#include "fie.h"
#include "oal_aloc.h"
#include <math.h>

/*
    サンプル用グレイサーチ結果 構造体
*/
typedef struct
{
    INT x;
    INT y;
    INT score;
    INT pattern_id;
}SMP_RESULT_MULTI;

#define SAMPLE_PRINT
#define PATTERN_NUM 4

//結果表示用マクロ.
#ifdef SAMPLE_PRINT
#define PRINT_RESULT( result, result_num )\
    {\
        INT num;\
        puts("result");\
        for( num = 0; num < result_num; num++ )\
        {\
            printf("No.%2d : score %d, ( %d, %d ) pattarn_id:%d\n",\
            num, result[num].score, result[num].x/100, result[num].y/100, result[num].pattern_id );\
        }\
    }
#else
#define PRINT_RESULT( result, result_num )
#endif

//------------------------------------------------
// 関数プロトタイプ

INT fnSMP_gray_search_multipattern( 
    FHANDLE                 hgs,
    FHANDLE                 *hpatterns,
    INT                     pattern_num,
    FHANDLE                 himage, 
    BOX_T                   search_window,
    INT                     threshold_mid,
    INT                     threshold_final,
    INT                     edge_detect,
    INT                     reverse_mode,
    INT                     pitch_x,
    INT                     pitch_y,
    INT                     first_unit,
    INT                     last_unit,
    enum f_gs2_subpxl_neib  subpxl_neib,
    INT                     max_result_num,
    SMP_RESULT_MULTI        *result,
    INT                     *result_num
    );
static INT fnSMP_alloc_patterns( CHAR **filenames, PNT_T *mark_offset, FHANDLE *hmasks, INT pattern_num, FHANDLE **hpatterns );
static VOID fnSMP_free_patterns( FHANDLE *hpatterns, INT pattern_num );
static INT FVALGAPI fnSMP_comp_ex( VOID *search_result, const VOID *p_index1, const VOID *p_index2 );
static INT fnSMP_sort_result( FHANDLE hvarray, SMP_RESULT_MULTI *result, INT pitch_x, INT pitch_y, INT max_result_num, INT *result_num );
static INT fnSMP_result_add( F_GS_RESULT *result, FHANDLE hvarray, INT result_num, INT pattern_id );
static INT fnSMP_pitch_calc( FHANDLE *hpatterns, INT pattern_num, INT *pitch_x, INT *pitch_y ); 

//------------------------------------------------
// 実装

/*
    マルチパターン グレイサーチ.
    複数のパターンによるグレイサーチを実施します.
    通常のグレイサーチ結果に検出したパターンIDを追加したものを出力します.

    同一座標とみなされる位置に複数のパターン検出された場合、スコアが最大のものを回答します.

    引数:
        [in,out]    hgs                 グレイサーチオブジェクト
        [in]        hpatterns           グレイサーチパターンオブジェクト配列
        [in]        pattern_num         パターンオブジェクト配列の要素数
        [in]        himage              サーチ対象となる画像オブジェクト ( type : uc8 )
                                        チャネル数は1でなければなりません.
                                        また、パタンのサイズよりも縦横共に大きい必要があります.
        [in]        search_window       サーチウインドウ
                                        画像オブジェクトの中でサーチを行う処理範囲を himage 上の座標で指定します.
                                        himage の内部に納まるように座標を指定する必要があります.
                                        また、パタンのサイズよりも大きい必要があります.
        [in]        threshold_mid       途中相関値 ( 1000 〜 9999 )
        [in]        threshold_final     最終相関値 ( 1000 〜 9999 )
        [in]        edge_detect         サーチウインドウ周囲接触フラグ
                                        - TRUE サーチウインドウに接したパターンも検出
                                        - FALSE サーチウインドウに接したパターンを検出しない
        [in]        reverse_mode        反転パターン検出モードフラグ 濃度反転しているパターンも検出するかどうかを指定します.
                                        - TRUE 反転パターンも検出
                                        - FALSE 反転パターンを検出しない
        [in]        pitch_x             最終的な回答の出力時に、同一の解であるとみなす範囲を指定します.
                                        0を入力した場合、各パターンサイズ平均の半分になります.
        [in]        pitch_y             最終的な回答の出力時に、同一の解であるとみなす範囲を指定します.
                                        0を入力した場合、各パターンサイズ平均の半分になります.
        [in]        first_unit          サーチ開始圧縮度を指定します.( 0 〜 9 ) 
                                        圧縮度がnのとき、2の-n乗倍の画像でサーチを開始します.
        [in]        last_unit           サーチ終了圧縮度を指定します.( 0 〜 9 ) 
                                        圧縮度がnのとき、2の-n乗倍の画像でサーチを完了します.
        [in]        subpxl_neib         精サーチ・サブピクセル推定に用いる近傍を指定します.
                                        8近傍を指定する場合、サーチ終了圧縮度(last_unit)は 0 を推奨します.
                                        - F_GS2_SUBPXL_NEIB_4  4近傍
                                        - F_GS2_SUBPXL_NEIB_8  8近傍
        [in]        max_result_num      サーチ結果取得個数.
                                        相関値の上位からこの個数だけ回答します.
                                        result はこの個数よりも大きい配列でなければいけません.
        [out]       result              サーチ結果
                                        サーチスコア( 0 〜 10000 )と結果座標の100倍値を返します.
        [out]       result_num          見つかったサーチ結果の個数 ( 0 〜 max_result_num )

    戻り値:
        F_ERR_NONE                  正常終了
        F_ERR_INVALID_PARAM         引数異常、範囲外の引数を与えた(pitchがマイナス等)
        F_ERR_INVALID_OBJECT        引数オブジェクトの種別が異常、引数オブジェクトがNULL
        F_ERR_NOMEMORY              メモリ不足
        F_ERR_INVALID_IMAGE         対応していない画像が渡された
        F_ERR_NO_LICENCE            ライセンスエラー、または未初期化エラー
*/
INT fnSMP_gray_search_multipattern( 
    FHANDLE                 hgs,
    FHANDLE                 *hpatterns,
    INT                     pattern_num,
    FHANDLE                 himage, 
    BOX_T                   search_window,
    INT                     threshold_mid,
    INT                     threshold_final,
    INT                     edge_detect,
    INT                     reverse_mode,
    INT                     pitch_x,
    INT                     pitch_y,
    INT                     first_unit,
    INT                     last_unit,
    enum f_gs2_subpxl_neib  subpxl_neib,
    INT                     max_result_num,
    SMP_RESULT_MULTI        *result,
    INT                     *result_num
    )
{
    INT ret = F_ERR_UNKNOWN;

    F_GS_RESULT *result_buff = NULL;
    FHANDLE hvary   = NULL;

    /* パラメータ確認 */
    ret = F_ERR_INVALID_PARAM;
    if( NULL == result || NULL == hpatterns ) goto EXIT;
    if( pitch_x < 0 || pitch_y < 0 ) goto EXIT;

    ret = F_ERR_NOMEMORY;
    result_buff = (F_GS_RESULT*)fnOAL_calloc( max_result_num, sizeof(F_GS_RESULT) );
    if( NULL == result_buff ) goto EXIT;

    hvary = fnFIE_vectarray_alloc( (enum f_objtag)0, sizeof(SMP_RESULT_MULTI), 0 );
    if( NULL == hvary ) goto EXIT;

    /*
        ピッチの入力が0の場合
        各パターン画像サイズ(幅, 高さ)平均の半分とする.
    */
    if( pitch_x == 0 || pitch_y == 0 )
    {
        ret = fnSMP_pitch_calc( hpatterns, pattern_num, &pitch_x, &pitch_y );
        if( F_ERR_NONE != ret ) goto EXIT;
    }

    ret = F_ERR_UNKNOWN;
    {
        INT i;

        FHANDLE himage_buff = himage;
        for( i = 0; i < pattern_num; i++ )
        {
            ret = fnFIE_gs2_search_enforce2(
                    hgs,
                    hpatterns[i],
                    himage_buff,//二回目以降NULL.
                    search_window,
                    threshold_mid, threshold_final,
                    edge_detect,
                    reverse_mode,
                    pitch_x, pitch_y,
                    first_unit,
                    last_unit,
                    subpxl_neib,
                    result_buff,
                    max_result_num,
                    result_num
                    );
            if( F_ERR_NONE != ret ) goto EXIT;
            
            ret = fnSMP_result_add( result_buff, hvary, *result_num, i );
            if( F_ERR_NONE != ret ) goto EXIT;

            //二回目以降ターゲット画像をNULLにし、継続サーチへ切り替える.
            himage_buff = NULL;
        }

        //結果出力.
        ret = fnSMP_sort_result( hvary, result, pitch_x, pitch_y, max_result_num, result_num );
        if( ret != F_ERR_NONE ) goto EXIT;
    }

EXIT:
    fnOAL_free( result_buff );
    fnFIE_vectarray_free( hvary );

    return ret;
}

/*
    パターンオブジェクト配列 生成

    引数のhpatternsはNULLで初期化したものを与えてください.

    生成された配列が不要になったら fnSMP_free_patterns()で解放してください.

    引数:
        [in]    filenames       パターン画像のPath配列
        [in]    mark_offset     回答座標オフセット配列
        [in]    hmask           マスク画像配列
        [in]    pattern_num     パターンオブジェクト配列の要素数
        [out]   hpatterns       パターンオブジェクト配列

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALID_PARAM     パターンオブジェクト配列がNULLでなかった
        F_ERR_NOMEMORY          メモリ不足
        F_ERR_GS_NO_CONTRAST    パターン画像に情報が足りないため、パターンにならない.(==マスク部を除いた画像の濃度分散が0.0になる)
*/
static INT fnSMP_alloc_patterns( CHAR **filenames, PNT_T *mark_offset, FHANDLE *hmasks, INT pattern_num, FHANDLE **hpatterns )
{
    INT ret = F_ERR_UNKNOWN;
    enum f_comp_filter comp_filter = F_COMP_MODE_SMOOTH;
    FHANDLE *hpat_buff = NULL;
    FHANDLE himg = NULL;

    //パラメータ確認.
    ret = F_ERR_INVALID_PARAM;
    if( NULL == hpatterns || NULL != (*hpatterns) ) goto EXIT;

    ret = F_ERR_NOMEMORY;
    hpat_buff = (FHANDLE*)fnOAL_calloc( pattern_num, sizeof(FHANDLE) );
    if( NULL == hpat_buff ) goto EXIT;

    {
        INT i;
        for( i = 0; i < pattern_num; i++ )
        {
            ret = fnFIE_load_img_file( filenames[i], &himg, F_COLOR_IMG_TYPE_UC8 );
            if( F_ERR_NONE != ret ) goto EXIT;

            hpat_buff[i] = fnFIE_gs2_pattern_alloc( himg, hmasks[i], mark_offset[i].x, mark_offset[i].y, comp_filter, &ret );
            if( F_ERR_NONE != ret ) goto EXIT;
        }
    }

EXIT:
    if( F_ERR_NONE == ret )     //正常終了.
        *hpatterns = hpat_buff;
    else                        //異常終了.
        fnSMP_free_patterns( hpat_buff, pattern_num );

    fnFIE_free_object( himg );
    return ret;
}

/*
    パターンオブジェクト配列 解放
    
    fnSMP_alloc_patterns()で確保した配列を解放します.

    引数:
        [out]   hpatterns       パターンオブジェクト配列
        [out]   pattern_num     パターンオブジェクト配列の要素数
*/
static VOID fnSMP_free_patterns( FHANDLE *hpatterns, INT pattern_num )
{
    INT i;
    
    if( NULL == hpatterns ) return;
    
    for( i =0; i < pattern_num; i++ )
    {
        fnFIE_free_object( hpatterns[i] );
    }
    
    fnOAL_free( hpatterns );
    return;
}

static INT FVALGAPI fnSMP_comp_ex( VOID *search_result, const VOID *p_index1, const VOID *p_index2 )
{
    SMP_RESULT_MULTI *result = (SMP_RESULT_MULTI*)search_result;
    INT index1 = *(INT*)p_index1;
    INT index2 = *(INT*)p_index2;
    SMP_RESULT_MULTI *result1 = result + index1;
    SMP_RESULT_MULTI *result2 = result + index2;

    if( result1->score < result2->score )
        return 1;
    else if( result1->score > result2->score )
        return -1;
    else
        return 0;
}

/*
    結果のソート

    サーチ結果をスコア順にソートし出力の構造体へ格納します.

    引数:
        [in]        hvarray         サーチ結果(vectarray配列オブジェクト)
        [out]       result          ソート後サーチ結果
        [in]        pitch_x         最小x間隔(1以上)
        [in]        pitch_y         最小y間隔(1以上)
        [in]        max_result_num  最大で上位何個回答するか(1以上)
        [out]       result_num      有効な結果の個数

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALID_PARAM     引数異常
        F_ERR_NOMEMORY          メモリ不足
*/
static INT fnSMP_sort_result( 
    FHANDLE hvarray,
    SMP_RESULT_MULTI *result,
    INT pitch_x, INT pitch_y,
    INT max_result_num, INT *result_num
    )
{
    INT ret = F_ERR_UNKNOWN;

    SMP_RESULT_MULTI *p_hvarray = NULL;
    INT ary_size;
    INT *sort_indexes = NULL;
    INT *hit_flags = NULL;
    
    //パラメータ確認.
    ret = F_ERR_INVALID_PARAM;
    if( NULL == result ) goto EXIT;

    ary_size = fnFIE_vectarray_getnum( hvarray );

    ret = F_ERR_NOMEMORY;
    sort_indexes = (INT*)fnOAL_malloc( sizeof(INT) * ary_size );
    if( NULL == sort_indexes ) goto EXIT;
    hit_flags = (INT*)fnOAL_malloc( sizeof(INT) * ary_size );
    if( NULL == hit_flags ) goto EXIT;

    //indexes, hit_flags 初期化.
    {
        INT i;
        for( i = 0; i < ary_size; i++ )
        {
            sort_indexes[i] = i;
            hit_flags[i] = 0;
        }
    }

    //相関値が高い順にソート.
    p_hvarray = (SMP_RESULT_MULTI*)fnFIE_vectarray_getptr( hvarray );
    fnFIE_qsort_ex( sort_indexes, ary_size, sizeof(INT), fnSMP_comp_ex, (SMP_RESULT_MULTI*)p_hvarray );

    //ピッチを100倍値と10倍値へ換算.
    pitch_x     *= 100;
    pitch_y     *= 100;

    //サーチ結果重複除去.
    {
        INT i;
        INT num = 0;
        for( i = 0; i < ary_size; i++ )
        {
            SMP_RESULT_MULTI result = *( ( SMP_RESULT_MULTI* )fnFIE_vectarray_getat( hvarray, sort_indexes[i] ) );
            INT x = result.x;
            INT y = result.y;
            INT j;
            for( j = i+1; j < ary_size; j++ )
            {
                SMP_RESULT_MULTI dresult = *( ( SMP_RESULT_MULTI* )fnFIE_vectarray_getat( hvarray, sort_indexes[j] ) );
                INT dx = abs( dresult.x - x );
                INT dy = abs( dresult.y - y );
                if( ( dx < pitch_x ) && ( dy < pitch_y ) )
                {
                    hit_flags[j] = 1;
                }
            }
        }
    }

    //結果出力.
    {
        INT i;
        INT dst_ary_index = 0;
        for( i = 0; i < ary_size; i++ )
        {
            if( !(hit_flags[i]) )
            {
                SMP_RESULT_MULTI result_buff = *( ( SMP_RESULT_MULTI* )fnFIE_vectarray_getat( hvarray, sort_indexes[i] ) );
                result[dst_ary_index].score         = result_buff.score;
                result[dst_ary_index].x             = result_buff.x;
                result[dst_ary_index].y             = result_buff.y;
                result[dst_ary_index].pattern_id        = result_buff.pattern_id;
                dst_ary_index++;
            }
            //個数とindexの差は上記処理で埋められているのでそのまま比較.
            if( max_result_num <= dst_ary_index )
            {
                dst_ary_index = max_result_num;
                break;
            }
        }
        
        *result_num = dst_ary_index;
    }

    ret = F_ERR_NONE;

EXIT:
    fnOAL_free( sort_indexes );
    fnOAL_free( hit_flags );
    return ret;
}

/*
    サーチ結果 配列へ要素追加.
    サーチ結果及びパタンIDを追加します.

    引数:
        [in]        result      サーチ結果
        [out]       hvarray     統合後サーチ結果
        [in]        result_num  サーチ結果個数
        [in]        pattern_id  パタンID

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALD_PARAM      引数異常
        F_ERR_NOMEMORY          メモリ不足
*/
static INT fnSMP_result_add( F_GS_RESULT *result, FHANDLE hvarray, INT result_num, INT pattern_id )
{
    INT ret = F_ERR_UNKNOWN;
    SMP_RESULT_MULTI result_buff;
    INT i;

    if( NULL == result || NULL == hvarray )
        return F_ERR_INVALID_PARAM;
    if( result_num < 0 )
        return F_ERR_INVALID_PARAM;
    
    //可変長配列へデータを追加するため.
    //F_GS_RESULT → SMP_RESULT_MULTI へ変更.
    for( i = 0; i < result_num; i++ )
    {
        result_buff.score       = result[i].score;
        result_buff.x           = result[i].x;
        result_buff.y           = result[i].y;
        result_buff.pattern_id  = pattern_id;

        ret = fnFIE_vectarray_push_back( hvarray, &result_buff );
        if( F_ERR_NONE != ret ) return ret;
    }

    return F_ERR_NONE;
}

/*
    ピッチ計算.

    各パターンサイズ(幅, 高さ)平均の半分を算出します.

    引数:
        [in]        hpatterns       パターンオブジェクト配列
        [in]        pattern_num     パターンオブジェクト配列の要素数
        [in,out]    pitch_x         最小x間隔(1以上)
        [in,out]    pitch_y         最小y間隔(1以上)

    戻り値:
        F_ERR_INVALID_PARAM         引数異常
        F_ERR_INVALID_OBJECT        パタンでないオブジェクトが渡された
        F_ERR_NOMEMORY              メモリ不足
        F_ERR_NONE                  正常終了
        F_ERR_NO_LICENCE            ライセンスエラー、または未初期化エラー
*/
static INT fnSMP_pitch_calc( FHANDLE *hpatterns, INT pattern_num, INT *pitch_x, INT *pitch_y )
{
    INT ret = F_ERR_UNKNOWN;
    DOUBLE x = 0.0;
    DOUBLE y = 0.0;
    INT i;
    FHANDLE pat_img = NULL;

    for( i = 0; i < pattern_num; i++ )
    {
        ret = fnFIE_gs2_pattern_get_image( hpatterns[i], &pat_img );
        if( F_ERR_NONE != ret ) goto EXIT;;

        x += fnFIE_img_get_width( pat_img );
        y += fnFIE_img_get_height( pat_img );

        fnFIE_free_object( pat_img ); pat_img = NULL;
    }
    
    if( pitch_x == 0 )
        *pitch_x = ( fnFIE_d4i5( x / (DOUBLE)pattern_num ) / 2 );
    if( pitch_y == 0 )
        *pitch_y = ( fnFIE_d4i5( y / (DOUBLE)pattern_num ) / 2 );

EXIT:
    fnFIE_free_object( pat_img );
    return ret;
}

//------------------------------------------------
// 実行サンプル

static INT execute()
{
    INT ret = F_ERR_UNKNOWN;

    FHANDLE hgs = NULL;
    FHANDLE himage = NULL;
    FHANDLE *hpatterns = NULL;
    INT     pattern_num = PATTERN_NUM;

    /* -----------------------gray search params -------------------------- */
    BOX_T search_window;
    INT threshold_mid = 3500;//中間相関値.
    INT threshold_final = 4000;//最終相関値.
    INT edge_detect = TRUE;//サーチウインドウ周囲接触フラグ.
    INT reverse_mode = FALSE;//濃度反転フラグ.
    INT pitch_x = 0;
    INT pitch_y = 0;
    INT first_unit = 5;//開始圧縮度.
    INT last_unit = 0;//終了圧縮度.
    enum f_gs2_subpxl_neib subpxl_neib = F_GS2_SUBPXL_NEIB_4;
    INT max_result_num = 100;//サーチ結果取得個数.
    SMP_RESULT_MULTI *result = NULL;
    INT result_num;

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

    //読み込むパターン画像Path 適宜変更してください.
    CHAR *filename[]=
    {
        "pattern_0.png", /* id 0 */
        "pattern_1.png", /* id 1 */
        "pattern_2.png", /* id 2 */
        "pattern_3.png"  /* id 3 */
    };
    //回答座標オフセット 適宜変更してください.
    PNT_T mark_offset[] = 
    {/* x, y */
        0, 0, /* id 0 */
        0, 0, /* id 1 */
        0, 0, /* id 2 */
        0, 0  /* id 3 */
    };
    //マスクにする画像.
    //サンプルではマスクは用いないため全てNULLのままです.
    //マスクが必要な場合は、リストに対応するマスク画像を確保して下さい.
    FHANDLE hmasks[] =
    {
         NULL,      /* id 0 */
         NULL,      /* id 1 */
         NULL,      /* id 2 */
         NULL       /* id 3 */
    };

    //画像読込.
    //適宜目的の画像に置き換えてください.
    ret = fnFIE_load_img_file( "gray_search_multi.png", &himage, F_COLOR_IMG_TYPE_UC8 );
    if( F_ERR_NONE != ret ) goto EXIT;

    //パターンオブジェクト配列 生成.
    //適宜確保してください サンプルでは上記のパターン画像4枚をサーチします.
    ret = fnSMP_alloc_patterns( filename, mark_offset, hmasks,pattern_num, &hpatterns );
    if( F_ERR_NONE != ret ) goto EXIT;

    //グレイサーチオブジェクト生成.
    hgs = fnFIE_gs2_alloc( 0, 0, 0 );
    
    //サーチウインドウの設定 サーチ対象画像の中に納まるよう設定.
    search_window.st.x = 0;
    search_window.st.y = 0;
    search_window.ed.x = fnFIE_img_get_width( himage ) - 1;
    search_window.ed.y = fnFIE_img_get_height( himage ) - 1;

    //結果領域確保.
    ret = F_ERR_NOMEMORY;
    result = (SMP_RESULT_MULTI*)fnOAL_calloc( max_result_num, sizeof(SMP_RESULT_MULTI) );
    if( NULL == result ) goto EXIT;

    //メインサンプル実行.
    ret = fnSMP_gray_search_multipattern(
        hgs,
        hpatterns,
        pattern_num,
        himage,
        search_window,
        threshold_mid,
        threshold_final,
        edge_detect,
        reverse_mode,
        pitch_x,
        pitch_y,
        first_unit,
        last_unit,
        subpxl_neib,
        max_result_num,
        result,
        &result_num
        );
    if( F_ERR_NONE != ret ) goto EXIT;

    //結果表示 SAMPLE_PRINTが定義されている場合のみ出力されます.
    PRINT_RESULT( result, result_num );

EXIT:
    fnSMP_free_patterns( hpatterns, pattern_num );
    fnFIE_free_object( himage );
    fnFIE_free_object( hgs );
    fnOAL_free( result );

    {
        INT i;
        for( i = 0; i < pattern_num; i++)
        {
            fnFIE_free_object( hmasks[i] );
        }
    }

    return ret;
}

INT main( )
{
    INT ret = F_ERR_UNKNOWN;

    // FIEライブラリの使用前に必ずコールする必要があります.
    fnFIE_setup();

    ret = execute();

    // 終了処理
    fnFIE_teardown();

    return ret;
}


マルチパターングレイサーチ

複数ワークの判別
/*
    マルチパターングレイサーチ サンプルコード

    このサンプルコードは、複数のパターンにおいてどの位置で検出されたかを全て回答し、
    何処で何が見つかったかを判別することを目的とします.
    また、同一座標付近に検出されたパターンに対しスコア比の行列を出力します.
*/

#include <stdio.h>
#include "fie.h"
#include "oal_aloc.h"
#include <math.h>

/*
    サンプル用グレイサーチ結果 構造体
*/
typedef struct
{
    INT x;
    INT y;
    INT score;
    INT pattern_id;
}SMP_RESULT_MULTI;

#define SAMPLE_PRINT
#define PATTERN_NUM 4

//結果表示用マクロ.
#ifdef SAMPLE_PRINT
#define PRINT_RESULT( result, result_num, mat )\
    {\
        INT i,j;\
        puts("result");\
        for( i = 0; i < result_num; i++ )\
        {\
            printf("No.%2d : score %d, ( %d, %d ) pattarn_id:%d\n", \
            i, result[i].score, result[i].x/100, result[i].y/100, result[i].pattern_id );\
        }\
        puts("");\
        for( i = 0; i < result_num; i++ )\
        {\
            for( j = 0; j < result_num; j++)\
            {\
                printf(" %.3f,", mat->m[i][j]);\
            }\
            puts("");\
        }\
    }
#else
#define PRINT_RESULT( result, result_num, mat )
#endif

//------------------------------------------------
// 関数プロトタイプ

INT fnSMP_gray_search_multipattern_mat( 
    FHANDLE                 hgs,
    FHANDLE                 *hpatterns,
    INT                     pattern_num,
    FHANDLE                 himage, 
    BOX_T                   search_window,
    INT                     threshold_mid,
    INT                     threshold_final,
    INT                     edge_detect,
    INT                     reverse_mode,
    INT                     pitch_x,
    INT                     pitch_y,
    INT                     first_unit,
    INT                     last_unit,
    enum f_gs2_subpxl_neib  subpxl_neib,
    INT                     max_result_num,
    SMP_RESULT_MULTI        *result,
    INT                     *result_num,
    FMATRIX                 **result_comp_mat
    );
static INT fnSMP_alloc_patterns( CHAR **filenames, PNT_T *mark_offset, FHANDLE *hmasks, INT pattern_num, FHANDLE **hpatterns );
static VOID fnSMP_free_patterns( FHANDLE *hpatterns, INT pattern_num );
static INT FVALGAPI fnSMP_comp_ex( VOID *search_result, const VOID *p_index1, const VOID *p_index2 );
static INT fnSMP_sort_result( FHANDLE hvarray, SMP_RESULT_MULTI *result, INT max_result_num, INT *result_num );
static INT fnSMP_result_add( F_GS_RESULT *result, FHANDLE hvarray, INT result_num, INT pattern_id );
static INT fnSMP_result_comp_matrix( SMP_RESULT_MULTI *result, FMATRIX *result_comp_mat, INT result_num, INT pitch_x, INT pitch_y );
static INT fnSMP_pitch_calc( FHANDLE *hpatterns, INT pattern_num, INT *pitch_x, INT *pitch_y );

//------------------------------------------------
// 実装

/*
    マルチパターン グレイサーチ.

    複数パターンによるグレイサーチを実施します.
    通常のグレイサーチ結果に検出したパターンIDを追加したものを出力します.
    また、同一の解とされる座標に出現したパターンのスコア比を計算します.
    無関係な位置にいるパターンは比較を行わず該当する行列位置に0を格納します.

    result_compはNULLで初期化してください.NULL出ない場合はエラーを出力します.
    自動的にサーチ結果個数分の正方行列を確保します.
    result_compが不要になったら、fnFIE_mat_afree()で解放して下さい.

    引数:
        [in,out]    hgs                 グレイサーチオブジェクト
        [in]        hpatterns           グレイサーチパターンオブジェクト配列
        [in]        pattern_num         パターンオブジェクト配列の要素数
        [in]        himage              サーチ対象となる画像オブジェクト ( type : uc8 )
                                        チャネル数は1でなければなりません.
                                        また、パタンのサイズよりも縦横共に大きい必要があります.
        [in]        search_window       サーチウインドウ
                                        画像オブジェクトの中でサーチを行う処理範囲を himage 上の座標で指定します.
                                        himage の内部に納まるように座標を指定する必要があります.
                                        また、パタンのサイズよりも大きい必要があります.
        [in]        threshold_mid       途中相関値 ( 1000 〜 9999 )
        [in]        threshold_final     最終相関値 ( 1000 〜 9999 )
        [in]        edge_detect         サーチウインドウ周囲接触フラグ
                                        - TRUE サーチウインドウに接したパターンも検出
                                        - FALSE サーチウインドウに接したパターンを検出しない
        [in]        reverse_mode        反転パターン検出モードフラグ 濃度反転しているパターンも検出するかどうかを指定します.
                                        - TRUE 反転パターンも検出
                                        - FALSE 反転パターンを検出しない
        [in]        pitch_x             最終的な回答の出力時に、同一の解であるとみなす範囲を指定します.
                                        0を入力した場合、各パターンサイズ平均の半分になります.
        [in]        pitch_y             最終的な回答の出力時に、同一の解であるとみなす範囲を指定します.
                                        0を入力した場合、各パターンサイズ平均の半分になります.
        [in]        first_unit          サーチ開始圧縮度を指定します.( 0 〜 9 ) 
                                        圧縮度がnのとき、2の-n乗倍の画像でサーチを開始します.
        [in]        last_unit           サーチ終了圧縮度を指定します.( 0 〜 9 ) 
                                        圧縮度がnのとき、2の-n乗倍の画像でサーチを完了します.
        [in]        subpxl_neib         精サーチ・サブピクセル推定に用いる近傍を指定します.
                                        8近傍を指定する場合、サーチ終了圧縮度(last_unit)は 0 を推奨します.
                                        - F_GS2_SUBPXL_NEIB_4  4近傍
                                        - F_GS2_SUBPXL_NEIB_8  8近傍
        [in]        max_result_num      サーチ結果取得個数.
                                        相関値の上位からこの個数だけ回答します.
                                        result はこの個数よりも大きい配列でなければいけません.
        [out]       result              サーチ結果 
                                        サーチスコア( 0 〜 10000 )と結果座標の100倍値を返します.
        [out]       result_num          見つかったサーチ結果の個数 ( 0 〜 max_result_num )
        [out]       result_comp_mat     サーチ結果比較行列( result_num x result_num )

    戻り値:
        F_ERR_NONE                      正常終了
        F_ERR_INVALID_PARAM             引数異常、範囲外の引数を与えた(pitchがマイナス等)
        F_ERR_INVALID_OBJECT            引数オブジェクトの種別が異常、引数オブジェクトがNULL
        F_ERR_NOMEMORY                  メモリ不足
        F_ERR_INVALID_IMAGE             対応していない画像が渡された
        F_ERR_NO_LICENCE                ライセンスエラー、または未初期化エラー
*/
INT fnSMP_gray_search_multipattern_mat( 
    FHANDLE                 hgs,
    FHANDLE                 *hpatterns,
    INT                     pattern_num,
    FHANDLE                 himage, 
    BOX_T                   search_window,
    INT                     threshold_mid,
    INT                     threshold_final,
    INT                     edge_detect,
    INT                     reverse_mode,
    INT                     pitch_x,
    INT                     pitch_y,
    INT                     first_unit,
    INT                     last_unit,
    enum f_gs2_subpxl_neib  subpxl_neib,
    INT                     max_result_num,
    SMP_RESULT_MULTI        *result,
    INT                     *result_num,
    FMATRIX                 **result_comp_mat
    )
{
    INT ret = F_ERR_UNKNOWN;

    F_GS_RESULT *result_buff = NULL;
    FHANDLE hvary   = NULL;

    /* パラメータ確認 */
    ret = F_ERR_INVALID_PARAM;
    if( NULL == result || NULL == hpatterns ) goto EXIT;
    if( NULL != (*result_comp_mat) ) goto EXIT;
    if( pitch_x < 0 || pitch_y < 0 ) goto EXIT;

    ret = F_ERR_NOMEMORY;
    result_buff = (F_GS_RESULT*)fnOAL_calloc( max_result_num, sizeof(F_GS_RESULT) );
    if( NULL == result_buff ) goto EXIT;

    hvary = fnFIE_vectarray_alloc( (enum f_objtag)0, sizeof(SMP_RESULT_MULTI), 0 );
    if( NULL == hvary ) goto EXIT;

    /*
        ピッチの入力が0の場合
        各パターン画像サイズ(幅, 高さ)平均の半分とする.
    */
    if( pitch_x == 0 || pitch_y == 0 )
    {
        ret = fnSMP_pitch_calc( hpatterns, pattern_num, &pitch_x, &pitch_y );
        if( F_ERR_NONE != ret ) goto EXIT;
    }

    ret = F_ERR_UNKNOWN;
    {
        INT i;
        FHANDLE himage_buff = himage;

        for( i = 0; i < pattern_num; i++ )
        {
            ret = fnFIE_gs2_search_enforce2(
                    hgs,
                    hpatterns[i],
                    himage_buff,//二回目以降NULL.
                    search_window,
                    threshold_mid, threshold_final,
                    edge_detect,
                    reverse_mode,
                    pitch_x, pitch_y,
                    first_unit,
                    last_unit,
                    subpxl_neib,
                    result_buff,
                    max_result_num,
                    result_num
                    );
            if( F_ERR_NONE != ret ) goto EXIT;
            
            ret = fnSMP_result_add( result_buff, hvary, *result_num, i );
            if( F_ERR_NONE != ret ) goto EXIT;

            //二回目以降ターゲット画像をNULLにし、継続サーチへ切り替える.
            himage_buff = NULL;
        }

        //結果出力.
        ret = fnSMP_sort_result( hvary, result, max_result_num, result_num );
        if( ret != F_ERR_NONE ) goto EXIT;

        //行列 領域確保.
        *result_comp_mat = fnFIE_mat_aalloc( *result_num, *result_num );
        if( NULL == (*result_comp_mat) )
            return F_ERR_NOMEMORY;

        ret = fnSMP_result_comp_matrix( result, *result_comp_mat, *result_num, pitch_x, pitch_y );
        if( F_ERR_NONE != ret ) goto EXIT;
    }

EXIT:
    fnOAL_free( result_buff );
    fnFIE_vectarray_free( hvary );

    return ret;
}

/*
    パターンオブジェクト配列 生成.

    引数のhpatternsはNULLで初期化したものを与えてください.

    生成された配列が不要になったら fnSMP_free_patterns()で解放してください.

    引数:
        [in]    filenames       パターン画像のPath配列
        [in]    mark_offset     回答座標オフセット配列
        [in]    hmask           マスク画像配列
        [in]    pattern_num     パターンオブジェクト配列の要素数
        [out]   hpatterns       パターンオブジェクト配列

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALID_PARAM     パターンオブジェクト配列がNULLでなかった
        F_ERR_NOMEMORY          メモリ不足
        F_ERR_GS_NO_CONTRAST    パターン画像に情報が足りないため、パターンにならない.(==マスク部を除いた画像の濃度分散が0.0になる)
*/
static INT fnSMP_alloc_patterns( CHAR **filenames, PNT_T *mark_offset, FHANDLE *hmasks, INT pattern_num, FHANDLE **hpatterns )
{
    INT ret = F_ERR_UNKNOWN;
    enum f_comp_filter comp_filter = F_COMP_MODE_SMOOTH;
    FHANDLE *hpat_buff = NULL;
    FHANDLE himg = NULL;

    //パラメータ確認.
    ret = F_ERR_INVALID_PARAM;
    if( NULL == hpatterns || NULL != (*hpatterns) ) goto EXIT;

    ret = F_ERR_NOMEMORY;
    hpat_buff = (FHANDLE*)fnOAL_calloc( pattern_num, sizeof(FHANDLE) );
    if( NULL == hpat_buff ) goto EXIT;

    {
        INT i;
        for( i = 0; i < pattern_num; i++ )
        {
            ret = fnFIE_load_img_file( filenames[i], &himg, F_COLOR_IMG_TYPE_UC8 );
            if( F_ERR_NONE != ret ) goto EXIT;

            hpat_buff[i] = fnFIE_gs2_pattern_alloc( himg, hmasks[i], mark_offset[i].x, mark_offset[i].y, comp_filter, &ret );
            if( F_ERR_NONE != ret ) goto EXIT;
        }
    }

EXIT:
    if( F_ERR_NONE == ret )     //正常終了.
        *hpatterns = hpat_buff;
    else                        //異常終了.
        fnSMP_free_patterns( hpat_buff, pattern_num );

    fnFIE_free_object( himg );
    return ret;
}

/*
    パターンオブジェクト配列 解放.
    
    fnSMP_alloc_patterns()で確保した配列を解放します.

    引数:
        [out]   hpatterns       パターンオブジェクト配列
        [out]   pattern_num     パターンオブジェクト配列の要素数
*/
static VOID fnSMP_free_patterns( FHANDLE *hpatterns, INT pattern_num )
{
    INT i;
    
    if( NULL == hpatterns ) return;
    
    for( i =0; i < pattern_num; i++ )
    {
        fnFIE_free_object( hpatterns[i] );
    }

    fnOAL_free( hpatterns );
    return;
}

/*
    比較関数.
    スコア降順.
*/
static INT FVALGAPI fnSMP_comp_ex( VOID *search_result, const VOID *p_index1, const VOID *p_index2 )
{
    SMP_RESULT_MULTI *result = (SMP_RESULT_MULTI*)search_result;
    INT index1 = *(INT*)p_index1;
    INT index2 = *(INT*)p_index2;
    SMP_RESULT_MULTI *result1 = result + index1;
    SMP_RESULT_MULTI *result2 = result + index2;

    if( result1->score < result2->score )
        return 1;
    else if( result1->score > result2->score )
        return -1;
    else
        return 0;
}

/*
    結果のソート

    サーチ結果をスコア順にソートし出力の構造体へ格納します.

    引数:
        [in]        hvarray         サーチ結果(vectarray配列オブジェクト)
        [out]       result          ソート後サーチ結果
        [in]        pitch_x         最小x間隔(1以上)
        [in]        pitch_y         最小y間隔(1以上)
        [in]        max_result_num  最大で上位何個回答するか(1以上)
        [out]       result_num      有効な結果の個数

    戻り値:
        F_ERR_NONE                  正常終了
        F_ERR_INVALID_PARAM         引数異常
        F_ERR_NOMEMORY              メモリ不足
*/
static INT fnSMP_sort_result( 
    FHANDLE hvarray,
    SMP_RESULT_MULTI *result,
    INT max_result_num, INT *result_num
    )
{
    INT ret = F_ERR_UNKNOWN;

    SMP_RESULT_MULTI *p_hvarray = NULL;
    INT ary_size;
    INT *sort_indexes = NULL;
    
    //パラメータ確認.
    ret = F_ERR_INVALID_PARAM;
    if( NULL == result ) goto EXIT;

    ary_size = fnFIE_vectarray_getnum( hvarray );

    ret = F_ERR_NOMEMORY;
    sort_indexes = (INT*)fnOAL_malloc( sizeof(INT) * ary_size );
    if( NULL == sort_indexes ) goto EXIT;

    //indexes初期化.
    {
        INT i;
        for( i = 0; i < ary_size; i++ )
        {
            sort_indexes[i] = i;
        }
    }

    //相関値が高い順にソート.
    p_hvarray = (SMP_RESULT_MULTI*)fnFIE_vectarray_getptr( hvarray );
    fnFIE_qsort_ex( sort_indexes, ary_size, sizeof(INT), fnSMP_comp_ex, (SMP_RESULT_MULTI*)p_hvarray );

    //結果出力.
    {
        INT i;  
        if( max_result_num < ary_size )
            ary_size = max_result_num;
        
        for( i = 0; i < ary_size; i++ )
        {
            SMP_RESULT_MULTI result_buff = *( ( SMP_RESULT_MULTI* )fnFIE_vectarray_getat( hvarray, sort_indexes[i] ) );
            result[i].score         = result_buff.score;
            result[i].x             = result_buff.x;
            result[i].y             = result_buff.y;
            result[i].pattern_id    = result_buff.pattern_id;
        }
        *result_num = ary_size;
    }

    ret = F_ERR_NONE;

EXIT:
    fnOAL_free( sort_indexes );

    return ret;
}

/*
    サーチ結果 配列へ要素追加.
    
    サーチ結果及びパタンIDを追加します.

    引数:
        [in]        result      サーチ結果
        [out]       hvarray     統合後サーチ結果
        [in]        result_num  サーチ結果個数
        [in]        pattern_id  パタンID

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALD_PARAM      引数異常
        F_ERR_NOMEMORY          メモリ不足
*/
static INT fnSMP_result_add( F_GS_RESULT *result, FHANDLE hvarray, INT result_num, INT pattern_id )
{
    INT ret = F_ERR_UNKNOWN;
    SMP_RESULT_MULTI result_buff;
    INT i;

    if( NULL == result || NULL == hvarray )
        return F_ERR_INVALID_PARAM;
    if( result_num < 0 )
        return F_ERR_INVALID_PARAM;
    
    //可変長配列へデータを追加するため.
    //F_GS_RESULT → SMP_RESULT_MULTI へ変更.
    for( i = 0; i < result_num; i++ )
    {
        result_buff.score       = result[i].score;
        result_buff.x           = result[i].x;
        result_buff.y           = result[i].y;
        result_buff.pattern_id  = pattern_id;

        ret = fnFIE_vectarray_push_back( hvarray, &result_buff );
        if( F_ERR_NONE != ret ) return ret;
    }

    return F_ERR_NONE;
}
/*
    サーチ結果比較

    同一の解とされる座標に出現したパターンのスコア比を計算します.
    無関係な座標のパターンは該当する行列位置に 0 を格納します.
    行列の対角線要素は同一リザルトのスコア比となり 1 が出力されます。

    [in]    result          サーチ結果
    [out]   result_comp_mat サーチ比較行列
    [in]    result_num      サーチ個数
    [in]    pitch_x         最小x間隔(1以上)
    [in]    pitch_y         最小y間隔(1以上)
*/
static INT fnSMP_result_comp_matrix( SMP_RESULT_MULTI *result, FMATRIX *result_comp_mat, INT result_num, INT pitch_x, INT pitch_y )
{
    INT ret = F_ERR_UNKNOWN;
    INT i, j;
    
    ret = F_ERR_INVALID_PARAM;
    if( pitch_x < 0 || pitch_y < 0 )
        return ret;
    if( result_num > result_comp_mat->col || result_num > result_comp_mat->row )
        return ret;

    pitch_x *= 100;
    pitch_y *= 100;

    for( i = 0; i < result_num; i++ )
    {
        SMP_RESULT_MULTI result_row = result[i];
        INT score_row = result_row.score;
        INT x= result_row.x;
        INT y= result_row.y;
        INT id_row = result_row.pattern_id; 
        for( j = 0; j < result_num; j++ )
        {
            SMP_RESULT_MULTI result_col = result[j];
            INT score_col = result_col.score;
            INT dx = abs( result_col.x - x );
            INT dy = abs( result_col.y - y );
            INT id_col = result_col.pattern_id;
            /*
                まず座標が近いか判別
                近ければスコア比を計算します.
                遠ければ0とします.
            */
            if( ( dx < pitch_x ) && ( dy < pitch_y ) )
            {
                DOUBLE comp = (DOUBLE)score_col / (DOUBLE)score_row;
                result_comp_mat->m[i][j] = comp;
            }
            else
                result_comp_mat->m[i][j] = 0;
        }
    }

    return F_ERR_NONE;
}

/*
    ピッチ計算

    各パターンサイズ(幅, 高さ)平均の半分を算出します.

    引数:
        [in]        hpatterns       パターンオブジェクト配列
        [in]        pattern_num     パターンオブジェクト配列の要素数
        [in,out]    pitch_x         最小x間隔(1以上)
        [in,out]    pitch_y         最小y間隔(1以上)
    戻り値:
        F_ERR_INVALID_PARAM         引数異常
        F_ERR_INVALID_OBJECT        パタンでないオブジェクトが渡された
        F_ERR_NOMEMORY              メモリ不足
        F_ERR_NONE                  正常終了
        F_ERR_NO_LICENCE            ライセンスエラー、または未初期化エラー
*/
static INT fnSMP_pitch_calc( FHANDLE *hpatterns, INT pattern_num, INT *pitch_x, INT *pitch_y )
{
    INT ret = F_ERR_UNKNOWN;
    DOUBLE x = 0.0;
    DOUBLE y = 0.0;
    INT i;
    FHANDLE pat_img = NULL;

    for( i = 0; i < pattern_num; i++ )
    {
        ret = fnFIE_gs2_pattern_get_image( hpatterns[i], &pat_img );
        if( F_ERR_NONE != ret ) goto EXIT;;

        x += fnFIE_img_get_width( pat_img );
        y += fnFIE_img_get_height( pat_img );

        fnFIE_free_object( pat_img ); pat_img = NULL;
    }

    if( pitch_x == 0 )
        *pitch_x = ( fnFIE_d4i5( x / (DOUBLE)pattern_num ) / 2 );
    if( pitch_y == 0 )
        *pitch_y = ( fnFIE_d4i5( y / (DOUBLE)pattern_num ) / 2 );

EXIT:
    fnFIE_free_object( pat_img );
    return ret;
}


//------------------------------------------------
// 実行サンプル

static INT execute()
{
    INT ret = F_ERR_UNKNOWN;

    FHANDLE hgs = NULL;
    FHANDLE himage = NULL;
    FHANDLE *hpatterns = NULL;
    INT     pattern_num = PATTERN_NUM;

    /* -----------------------gray search params -------------------------- */
    BOX_T search_window;
    INT threshold_mid = 3500;//中間相関値.
    INT threshold_final = 4000;//最終相関値.
    INT edge_detect = TRUE;//サーチウインドウ周囲接触フラグ.
    INT reverse_mode = FALSE;//濃度反転フラグ.
    INT pitch_x = 0;
    INT pitch_y = 0;
    INT first_unit = 5;//開始圧縮度.
    INT last_unit = 0;//終了圧縮度.
    enum f_gs2_subpxl_neib subpxl_neib = F_GS2_SUBPXL_NEIB_4;
    INT max_result_num = 100;//サーチ結果取得個数.
    SMP_RESULT_MULTI *result = NULL;
    INT result_num;
    FMATRIX *result_comp_mat = NULL;

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

    //読み込むパターン画像Path 適宜変更してください.
    CHAR *filename[]=
    {
        "pattern_0.png", /* id 0 */
        "pattern_1.png", /* id 1 */
        "pattern_2.png", /* id 2 */
        "pattern_3.png"  /* id 3 */
    };
    //回答座標オフセット 適宜変更してください.
    PNT_T mark_offset[] = 
    {/* x, y */
        0, 0, /* id 0 */
        0, 0, /* id 1 */
        0, 0, /* id 2 */
        0, 0  /* id 3 */
    };
    //マスクにする画像.
    //サンプルではマスクは用いないため全てNULLのままです.
    //マスクが必要な場合は、リストに対応するマスク画像を確保して下さい.
    FHANDLE hmasks[] =
    {
         NULL,      /* id 0 */
         NULL,      /* id 1 */
         NULL,      /* id 2 */
         NULL       /* id 3 */
    };

    //画像読込.
    //適宜目的の画像に置き換えてください.
    ret = fnFIE_load_img_file( "gray_search_multi.png", &himage, F_COLOR_IMG_TYPE_UC8 );
    if( F_ERR_NONE != ret ) goto EXIT;

    //パターンオブジェクト配列 生成.
    //適宜確保してください サンプルでは上記のパターン画像4枚をサーチします.
    ret = fnSMP_alloc_patterns( filename, mark_offset, hmasks,pattern_num, &hpatterns );
    if( F_ERR_NONE != ret ) goto EXIT;

    //グレイサーチオブジェクト生成.
    hgs = fnFIE_gs2_alloc( 0, 0, 0 );
    
    //サーチウインドウの設定 サーチ対象画像の中に納まるよう設定.
    search_window.st.x = 0;
    search_window.st.y = 0;
    search_window.ed.x = fnFIE_img_get_width( himage )-1;
    search_window.ed.y = fnFIE_img_get_height( himage )-1;

    //結果領域確保.
    ret = F_ERR_NOMEMORY;
    result = (SMP_RESULT_MULTI*)fnOAL_calloc( max_result_num, sizeof(SMP_RESULT_MULTI) );
    if( NULL == result ) goto EXIT;

    //メインサンプル実行.
    ret = fnSMP_gray_search_multipattern_mat(
        hgs,
        hpatterns,
        pattern_num,
        himage,
        search_window,
        threshold_mid,
        threshold_final,
        edge_detect,
        reverse_mode,
        pitch_x,
        pitch_y,
        first_unit,
        last_unit,
        subpxl_neib,
        max_result_num,
        result,
        &result_num,
        &result_comp_mat
        );
    if( F_ERR_NONE != ret ) goto EXIT;

    //結果表示 SAMPLE_PRINTが定義されている場合のみ実行されます.
    PRINT_RESULT( result, result_num, result_comp_mat );

EXIT:
    fnSMP_free_patterns( hpatterns, pattern_num );
    fnFIE_free_object( himage );
    fnFIE_free_object( hgs );
    fnOAL_free( result );
    fnFIE_mat_afree( result_comp_mat );

    {
        INT i;
        for( i = 0; i < pattern_num; i++)
        {
            fnFIE_free_object( hmasks[i] );
        }
    }
    return ret;
}

INT main( )
{
    INT ret = F_ERR_UNKNOWN;

    // FIEライブラリの使用前に必ずコールする必要があります.
    fnFIE_setup();

    ret = execute();

    // 終了処理
    fnFIE_teardown();

    return ret;
}


Documentation copyright © 2009-2024 FAST Corporation.
Generated on Fri Aug 9 16:38:48 2024 for FIEライブラリ by doxygen 1.5.6-FASTSP-p2