エッジ検出 応用例
[サンプルコード一覧]


一次元エッジ検出(矩形)

/*
    指定された箱上を走査して、画像データのエッジ検出を高速に行うサンプルコードです。
*/

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

enum smp_scan_direction
{
    R_DIRECT,
    L_DIRECT,
    D_DIRECT,
    U_DIRECT
};
INT fnSMP_box_edge(
    FHANDLE                         hsrc,
    DPNT_T                          start,
    DOUBLE                          width,
    DOUBLE                          height,
    DOUBLE                          rad,
    enum smp_scan_direction         scan_dir,
    F_EDGE1D_DIFF_FILTER            diff_filter,
    INT                             arnd,
    enum f_edge1d_edge_direction    direct_mode,
    enum f_diff_type                diff_type,
    DOUBLE                          diff_thr,
    enum f_edge1d_sort_mode         detection_mode,
    INT                             prj_width,
    F_DEDGE**                       edges,
    INT*                            edge_num
);
static INT fnSMP_scan_edges(FHANDLE hsrc, INT prj_width, F_EDGE1D_DIFF_FILTER diff_filter, INT arnd, enum f_edge1d_edge_direction direct_mode,
    enum f_diff_type diff_type, DOUBLE diff_thr, enum f_edge1d_sort_mode detection_mode, F_DEDGE **edges, INT *edge_num, FMATRIX *inverse_af);
static INT fnSMP_mat_affine(DPNT_T start, DOUBLE width, DOUBLE  height, DOUBLE rad, INT prj_width,  FMATRIX **mat_aff, enum smp_scan_direction scan_dir);
static INT fnSMP_edges_outside(FHANDLE hsrc, DPNT_T *box, DOUBLE box_height, INT prj_width, F_EDGE1D_DIFF_FILTER diff_filter, INT arnd,
    enum f_edge1d_edge_direction direct_mode, enum f_diff_type diff_type, DOUBLE diff_thr, enum f_edge1d_sort_mode detection_mode, F_DEDGE **edges, INT *edge_num);


/*

    箱指定によるエッジ検出

    指定した箱の各計測ライン(検出線)上におけるエッジを高速に検出します。各計測ライン上で1点のエッジのみを出力します。

    start,width,height,radのパラメータにより、箱の領域を決定します。
    検出箱はradにて指定した角度だけ回転されます。回転中心はstartで指定した始点と同じ位置です。
    回転にはfnFIE_geotrans_calc_rotate_matrix()を使用します。
    エッジを検出する方向を指定できます。計測ラインの方向は、scan_dirによって指定します。
    各計測ライン上で検出されるエッジの種類はdetection_modeによって指定します。
    計測ラインの間隔は1画素となります。
    
    計測ライン方向scan_dir
    −−−−−−    −−−−−−    −−−−−−    −−−−−−
    |−−−→|    |←−−−|    ||||||    |↑↑↑↑|
    |−−−→|    |←−−−|    ||||||    ||||||
    |−−−→|    |←−−−|    ||||||    ||||||
    |−−−→|    |←−−−|    |↓↓↓↓|    ||||||
    −−−−−−    −−−−−−    −−−−−−    −−−−−−
      R_DIRECT        L_DIRECT        D_DIRECT        U_DIRECT

    各計測ラインでのエッジ取得にはfnFIE_edge1d_detect_edges_diff()が使用されます。diff_filter,arnd,direct_mode,diff_type,diff_thrのパラメータの詳細はそちらを参照してください。
    検出片幅のprj_widthパラメータの意味はfnFIE_edge1d_projection_line()を参照してください。
    この他、エッジ検出関数に共通する性質については、1次元エッジ検出の説明を参照してください。

    本関数では、検出片幅によって指定される大きさの濃度投影を、複数の計測ラインでまとめて実行することで高速化しています。
    一方で、検出片幅を含んだ箱領域が入力画像外に出た場合は計測ラインごと独立にエッジ検出を行うため、高速化されません。

    検出されたエッジ点数はedge_numに出力されます。
    検出されたエッジ点はedgesに出力されます。
    *edges は必ずNULLで初期化されていなければなりません。
    検出されたエッジ点数が1以上のとき、新たにメモリ確保されます。
    検出されたエッジ点数が0のとき、メモリ確保されずNULLが出力されます。
    新たに確保されたメモリは、使用後にfnOAL_free()で解放してください。

    引数:
        [in]hsrc            入力画像(type:uc8,us16,double/ch:1)
        [in]start           箱の左上の座標
        [in]width           箱の幅(1以上)
        [in]height          箱の高さ(1以上)
        [in]rad             回転角(ラジアン)
        [in]scan_dir        計測ライン方向
                            - R_DIRECT 検出箱の左から右に向かって計測ラインを設定します
                            - L_DIRECT 検出箱の右から左に向かって計測ラインを設定します
                            - D_DIRECT 検出箱の上から下に向かって計測ラインを設定します
                            - U_DIRECT 検出箱の下から上に向かって計測ラインを設定します
        [in]diff_filter     微分フィルタの指定
                            - minus_foot 負側のfoot長さ(1以上)
                            - minus_len 負側のlen長さ(0以上)
                            - plus_foot 正側のfoot長さ(1以上)
                            - plus_len 正側のlen長さ(0以上)
        [in]arnd            サブピクセル計算に使用する周辺情報の片幅(0以上)
                            検出した濃度微分データのピーク位置(または、または最初にしきい値を超えた位置)に対して、
                            ここで指定した画素分の両端の濃度微分データを使用してサブピクセル精度の計算をします
        [in]direct_mode     検出するエッジ方向の指定
                            - F_DRK_TO_BRI 暗→明のエッジ
                            - F_BRI_TO_DRK 明→暗のエッジ
                            - F_DTC_BOTH 両極性
        [in]diff_type       濃度差しきい値の種別
                            - F_ABSOLUTE_THR 絶対濃度差
                            - F_RELATIVE_THR 相対濃度差
        [in]diff_thr        エッジの最小濃度差しきい値
                            - F_ABSOLUTE_THR を指定した場合:[0, 2147483647]
                                内部で、 fnFIE_d4i5() にて四捨五入されます
                            - F_RELATIVE_THR を指定した場合:[0,100](%)
                                生成された濃度プロファイル内の最大値と最小値の差に対する割合
                                求められたしきい値は、 fnFIE_d4i5() にて四捨五入されます
        [in]detection_mode  各計測ラインで検出されるエッジの種類を指定
                            - F_MAG_SORT 計測ライン上で強度が最も大きいエッジ
                            - F_POS_SORT 計測ライン上で最初に見つかったエッジ
        [in]prj_width       検出片幅(0以上、単位:画素) 0を指定した場合は、計測ライン上のみ有意となります
                            計測ラインをはさむ指定画素分の平均値を求めるため、この値を大きくするとノイズに強くなります
        [out]edges          検出されたエッジ点保存配列のポインタ
                            *edges はNULLでなければなりません
                            - x x座標
                            - y y座標
                            - q 勾配方向
                                - 暗→明のエッジ 1
                                - 明→暗のエッジ -1
                            - mag 強度(0以上)
        [out]edge_num       検出されたエッジ点個数

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALID_PARAM     不正なパラメータが渡された
        F_ERR_NO_LICENCE        ライセンスエラー、または未初期化エラー
        F_ERR_INVALID_IMAGE     不正な画像が渡された
        F_ERR_NOMEMORY          メモリ不足
        F_ERR_NODATA            指定された箱の有効領域が存在しない
        F_ERR_CALC_IMPOSSIBLE   不正な箱の領域が渡された
    
*/
INT fnSMP_box_edge(
    FHANDLE                         hsrc,
    DPNT_T                          start,
    DOUBLE                          width,
    DOUBLE                          height,
    DOUBLE                          rad,
    enum smp_scan_direction         scan_dir,
    F_EDGE1D_DIFF_FILTER            diff_filter,
    INT                             arnd,
    enum f_edge1d_edge_direction    direct_mode,
    enum f_diff_type                diff_type,
    DOUBLE                          diff_thr,
    enum f_edge1d_sort_mode         detection_mode,
    INT                             prj_width,
    F_DEDGE**                       edges,
    INT*                            edge_num
)
{
    INT ret = F_ERR_UNKNOWN;

    FHANDLE affine_img = NULL; //アフィン変換後の画像
    FHANDLE average_img = NULL;//平滑化後の画像
    FMATRIX *mat_aff = NULL;   //アフィン変換で用いる行列
    FMATRIX *inverse_aff = NULL;//mat_affの逆行列

    INT hsrc_width, hsrc_height, hsrc_type;
    INT i;
    BOOL is_inside;
    DOUBLE box_w, box_h;

    DPNT_T box_rearranged[4];//原点基準・傾きなしに変換された箱4隅の座標点
    DPNT_T box_original[4];//変換前の入力画像上における箱4隅の座標点

    //パラメータ確認
    ret = F_ERR_INVALID_PARAM;
    if (prj_width < 0)goto EXIT;
    if (width < 1 || height < 1)goto EXIT;
    switch (scan_dir)
    {
        case R_DIRECT:
        case L_DIRECT:
        case D_DIRECT:
        case U_DIRECT:
            break;
        default:
            goto EXIT;
    }
    if (edges == NULL || *edges != NULL)goto EXIT;

    ret = fnFIE_img_get_params(hsrc, NULL, &hsrc_type, NULL, &hsrc_width, &hsrc_height);
    if (ret != F_ERR_NONE)goto EXIT;
    ret = F_ERR_INVALID_IMAGE;
    switch (hsrc_type)
    {
    case F_IMG_UC8:
    case F_IMG_US16:
    case F_IMG_DOUBLE:
        break;
    default:
        goto EXIT;
    }

    //作業用行列を確保
    ret = F_ERR_NOMEMORY;
    mat_aff = fnFIE_mat_aalloc(3, 3);
    if (mat_aff == NULL)goto EXIT;
    inverse_aff = fnFIE_mat_aalloc(3, 3);
    if (inverse_aff == NULL)goto EXIT;

    //アフィン変換で用いる行列を生成
    //このアフィン変換行列は箱の位置/箱の傾き/計測ライン方向を吸収するために使用する
    ret = fnSMP_mat_affine(start, width, height, rad, prj_width, &mat_aff, scan_dir);
    if (ret != F_ERR_NONE)goto EXIT;

    //mat_affの逆行列を生成
    ret = fnFIE_mat_inverse3(mat_aff, inverse_aff, NULL);
    if (ret != F_ERR_NONE)goto EXIT;

    //検出片幅を含んだ箱領域の幅、高さ
    if (scan_dir == R_DIRECT || scan_dir == L_DIRECT)
    {
        box_w = width;
        box_h = height + prj_width * 2;
    }
    else
    {
        box_w = height;
        box_h = width + prj_width * 2;
    }

    //箱左上座標
    box_rearranged[0].x = 0;
    box_rearranged[0].y = 0;
    //箱右上座標
    box_rearranged[1].x = box_w - 1;
    box_rearranged[1].y = 0;
    //箱左下座標
    box_rearranged[2].x = 0;
    box_rearranged[2].y = box_h - 1;
    //箱右下座標
    box_rearranged[3].x = box_w - 1;
    box_rearranged[3].y = box_h - 1;
    
    //検出片幅を含んだ回転・平行移動前の4隅の座標点を生成
    ret= fnFIE_geotrans_affine_npoints(box_rearranged, box_original, 4, inverse_aff);
    if (ret != F_ERR_NONE)goto EXIT;

    //回転・平行移動前の4隅の座標点が入力画像内か判定
    is_inside = TRUE;
    for (i = 0; i < 4; i++)
    {
        if (box_original[i].x < 0 || box_original[i].x >= hsrc_width || box_original[i].y < 0 || box_original[i].y >= hsrc_height)
        {
            is_inside = FALSE;
            break;
        }
    }
    
    if (is_inside)
    {
        INT size_m, size_n;

        //求めた行列により画像のアフィン変換
        affine_img = fnFIE_img_root_alloc(hsrc_type, 1, (INT)box_w, (INT)box_h);
        if (affine_img == NULL)
        {
            ret = F_ERR_NOMEMORY;
            goto EXIT;
        }
        ret = fnFIE_geotrans_affine(hsrc, affine_img, NULL, mat_aff, FALSE, F_SAMPLING_BILINEAR);
        if (ret != F_ERR_NONE)goto EXIT;

        //平滑化
        average_img = fnFIE_img_root_alloc(F_IMG_DOUBLE, 1, (INT)box_w, (INT)box_h);
        if (average_img == NULL)
        {
            ret = F_ERR_NOMEMORY;
            goto EXIT;
        }
        size_m = 1;
        size_n = prj_width * 2 + 1;
        ret = fnFIE_averageMxN(affine_img, average_img, size_m, size_n, F_BORDER_NONE, 0);
        if (ret != F_ERR_NONE)goto EXIT;

        //エッジ検出
        ret = fnSMP_scan_edges(average_img, prj_width, diff_filter, arnd, direct_mode, diff_type, diff_thr, detection_mode, edges, edge_num, inverse_aff);
        if (ret != F_ERR_NONE)goto EXIT;

    }
    else
    {
        //検出幅を含んだ箱領域が入力画像外にはみ出した場合のエッジ検出
        ret = fnSMP_edges_outside(hsrc, box_original, box_h,prj_width, diff_filter, arnd, direct_mode, diff_type, diff_thr, detection_mode, edges, edge_num);
        if (ret != F_ERR_NONE)goto EXIT;

    }

EXIT:
    fnFIE_mat_afree(mat_aff);
    fnFIE_free_object(affine_img);
    fnFIE_free_object(average_img);

    return ret;
}

/*
    指定範囲がすべて入力画像内の時のエッジ検出

    引数:
        [in]hsrc            入力画像
        [in]prj_width       検出片幅
        [in]diff_filter     微分フィルタの指定
        [in]arnd            サブピクセル計算に使用する周辺情報の片幅(0以上)
        [in]direct_mode     検出するエッジ方向の指定
        [in]diff_type       濃度差しきい値の種別
        [in]diff_thr        エッジの最小濃度差しきい値
        [in]detection_mode  検出されるエッジの種類を指定
        [out]edges          検出されたエッジ位置
        [out]edge_num       検出されたエッジ点個数
        [in]inverse_aff     mat_affの逆行列

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_NOMEMORY          メモリ不足
        F_ERR_INVALID_PARAM     不正なパラメータが渡された
        F_ERR_NODATA            濃度プロファイルに有効領域が存在しない
        F_ERR_NO_LICENCE        ライセンスエラー、または未初期化エラー
*/
static INT fnSMP_scan_edges(FHANDLE hsrc, INT prj_width, F_EDGE1D_DIFF_FILTER diff_filter, INT arnd,enum f_edge1d_edge_direction direct_mode,
    enum f_diff_type diff_type,DOUBLE diff_thr, enum f_edge1d_sort_mode detection_mode, F_DEDGE **edges, INT *edge_num, FMATRIX *inverse_aff)
{

    INT ret = F_ERR_UNKNOWN;

    INT_PTR prj_step;

    DOUBLE* prj;
    INT  y;
    INT edge_cnt;
    F_DEDGE* edge_buff_wk = NULL;
    F_DEDGE* edges_on_line = NULL;
    FHANDLE hvarray = NULL;

    DPNT_T *src = NULL;
    DPNT_T *dst = NULL;
    INT i;

    INT height, width;

    ret = fnFIE_img_get_params(hsrc, NULL, NULL, NULL, &width, &height);
    if (ret != F_ERR_NONE)goto EXIT;

    prj_step = fnFIE_img_get_step(hsrc);
    prj = (DOUBLE*)fnFIE_img_get_adrs(hsrc);

    //エッジ保存用メモリを確保
    ret = F_ERR_NOMEMORY;
    edges_on_line = (F_DEDGE*)fnOAL_malloc(width * sizeof(F_DEDGE));
    if (edges_on_line == NULL)goto EXIT;
    hvarray = fnFIE_vectarray_alloc((enum f_objtag)0, sizeof(F_DEDGE), 0);
    if (hvarray == NULL)goto EXIT;
    
    //横一列ずつエッジを検出
    edge_cnt = 0;
    for (y = prj_width; y < height - prj_width; y++)
    {
        INT line_edge_num;
        const DOUBLE* wk_p = prj + (prj_step * y);
        ret = fnFIE_edge1d_detect_edges_diff(wk_p, width, diff_filter, arnd, 0,
            direct_mode, diff_type, diff_thr, detection_mode,
            edges_on_line, width, &line_edge_num, NULL, width);
        if (ret != F_ERR_NONE)goto EXIT;

        //エッジが複数検出された場合は、最初のエッジのみを保存
        if (line_edge_num > 0)
        {
            edges_on_line->y = y;
            ret = fnFIE_vectarray_push_back(hvarray, edges_on_line);
            if (F_ERR_NONE != ret)goto EXIT;
            edge_cnt++;
        }

    }

    // エッジ点が検出されないケースを処理
    if (edge_cnt == 0) {
        *edges = NULL;
        *edge_num = 0;
        ret = F_ERR_NONE;
        goto EXIT;
    }

    //入力画像での座標に変換
    ret = F_ERR_NOMEMORY;
    src = (DPNT_T*)fnOAL_malloc(edge_cnt * sizeof(DPNT_T));
    if (src == NULL)goto EXIT;
    dst = (DPNT_T*)fnOAL_malloc(edge_cnt * sizeof(DPNT_T));
    if (dst == NULL)goto EXIT;
    
    for (i = 0; i < edge_cnt; i++)
    {
        src[i].x = (*(F_DEDGE*)fnFIE_vectarray_getat(hvarray, i)).x;
        src[i].y = (*(F_DEDGE*)fnFIE_vectarray_getat(hvarray, i)).y;
    }
    
    ret = fnFIE_geotrans_affine_npoints(src, dst, edge_cnt, inverse_aff);
    if (ret != F_ERR_NONE)goto EXIT;
    
    edge_buff_wk = (F_DEDGE*)fnOAL_malloc(edge_cnt * sizeof(F_DEDGE));
    if (edge_buff_wk == NULL)
    {
        ret = F_ERR_NOMEMORY;
        goto EXIT;
    }

    for (i = 0; i < edge_cnt; i++)
    {
        edge_buff_wk[i] = *(F_DEDGE*)fnFIE_vectarray_getat(hvarray, i);
        edge_buff_wk[i].x = dst[i].x;
        edge_buff_wk[i].y = dst[i].y;
    }


    *edges = edge_buff_wk;
    *edge_num = edge_cnt;
    ret = F_ERR_NONE;

EXIT:
    fnOAL_free(edges_on_line);
    fnFIE_vectarray_free(hvarray);
    fnOAL_free(src);
    fnOAL_free(dst);
    if (ret != F_ERR_NONE) {
        fnOAL_free(edge_buff_wk);
    }

    return ret;
}
/*
    アフィン変換で用いる行列を求めます。

    処理の高速性のため、回転を含む箱領域上で直接エッジ検出は行いません。
    代わりに、検出片幅を含んだ箱領域が以下の性質を満たすように入力画像を変換します。
    - 原点基準(左上座標が(0,0))
    - 傾きは0
    - 計測ラインの向きは常に右

    本関数では、上記の変換を達成するアフィン変換行列を求めます。

    作成する行列は次の3つの行列の積になります。
    - 検出箱の回転(入力画像の逆回転)
    - 計測ライン方向が常に左から右になるような回転
    - 原点に移動する行列

    引数:
        [in]start       箱の左上の座標(始点)
        [in]width       箱の幅
        [in]height      箱の高さ
        [in]rad         回転角(ラジアン)
        [in]prj_width   検出片幅(0以上、単位:画素)
        [out]mat_aff    アフィン変換時に用いる行列
        [in]scan_dir    計測ライン方向

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_INVALID_PARAM     不正なパラメータが渡された
        F_ERR_NO_LICENCE        ライセンスエラー、または未初期化エラー
        F_ERR_NOMEMORY          メモリ確保エラー
*/
static INT fnSMP_mat_affine(DPNT_T start, DOUBLE width, DOUBLE height, DOUBLE rad, INT prj_width, FMATRIX **mat_aff, enum smp_scan_direction scan_dir)
{
    INT ret = F_ERR_UNKNOWN;
    FMATRIX *mat_rotate = NULL;
    FMATRIX *mat_shift = NULL;
    FMATRIX *mat_line_rotate = NULL;
    FMATRIX *mat_mul = NULL;
    DOUBLE line_angle;
    DOUBLE shift_x, shift_y;
    
    //作業用行列を確保
    ret = F_ERR_NOMEMORY;

    mat_rotate = fnFIE_mat_aalloc(3, 3);
    if (mat_rotate == NULL) goto EXIT;

    mat_shift = fnFIE_mat_aalloc(3, 3);
    if (mat_shift == NULL)goto EXIT;

    mat_line_rotate = fnFIE_mat_aalloc(3, 3);
    if (mat_line_rotate == NULL)goto EXIT;

    mat_mul = fnFIE_mat_aalloc(3, 3);
    if (mat_mul == NULL)goto EXIT;

    //検出箱の回転行列。
    //回転中心は始点startとする。
    //直接検出箱を回転させるのではなく入力画像を回転させるため、回転方向は負となる。
    ret = fnFIE_geotrans_calc_rotate_matrix(mat_rotate, -rad, start.x, start.y);
    if (ret != F_ERR_NONE) goto EXIT;

    //計測ライン方向別パラメータ
    switch (scan_dir)
    {
        case R_DIRECT:
            {
                line_angle = 0;
                shift_x = -start.x;
                shift_y = -start.y + prj_width;
            }
            break;
        case L_DIRECT:
            {
                line_angle = PI;
                shift_x = -start.x + width - 1;
                shift_y = -start.y + height + prj_width - 1;    
            }
            break;
        case D_DIRECT:
            {
                line_angle = 1.5*PI;
                shift_x = -start.x;
                shift_y = -start.y + width + prj_width - 1;
            }
            break;
        case U_DIRECT:
            {
                line_angle = 0.5*PI;
                shift_x = -start.x + height - 1;
                shift_y = -start.y + prj_width;
            }
            break;
        default:
            break;
    }

    //計測ライン方向が左から右になるような回転行列
    ret = fnFIE_geotrans_calc_rotate_matrix(mat_line_rotate, line_angle, start.x, start.y);
    if (ret != F_ERR_NONE) goto EXIT;

    //原点に平行移動する変換行列
    ret = fnFIE_geotrans_calc_shift_matrix(mat_shift, shift_x, shift_y);
    if (ret != F_ERR_NONE)goto EXIT;

    ret = fnFIE_mat_mul_aa(mat_shift, mat_line_rotate, mat_mul);
    if (ret != F_ERR_NONE)goto EXIT;

    ret = fnFIE_mat_mul_aa(mat_mul, mat_rotate, *mat_aff);
    if (ret != F_ERR_NONE)goto EXIT;

EXIT:
    fnFIE_mat_afree(mat_rotate);
    fnFIE_mat_afree(mat_shift);
    fnFIE_mat_afree(mat_line_rotate);
    fnFIE_mat_afree(mat_mul);

    return ret;
}

/*
    箱指定範囲を1次元エッジ検出します。
    箱指定範囲が入力画像外にはみ出した場合に呼ばれる関数です。

    本関数では、高レベルの一次元エッジ検出関数fnFIE_edge1d_line2()を使用します。
    この関数は、計測ラインの一部が入力画像外であっても適切に処理可能です(詳細は1次元エッジ検出の説明を参照してください)。
    しかし、計測ラインごとに逐一濃度プロファイルを作成するため、処理速度は低速です。

    引数:
        [in]hsrc            入力画像
        [in]box             検出幅を含んだ4隅の座標点
        [in]box_height      計測ライン方向を左から右に揃えた後の箱の高さ
        [in]prj_width       検出片幅
        [in]diff_filter     微分フィルタの指定
        [in]arnd            サブピクセル計算に使用する周辺情報の片幅(0以上)
        [in]direct_mode     検出するエッジ方向の指定
        [in]diff_type       濃度差しきい値の種別
        [in]diff_thr        エッジの最小濃度差しきい値
        [in]detection_mode  検出されるエッジの種類を指定
        [out]edges          検出されたエッジ位置
        [out]edge_num       検出されたエッジ点個数

    戻り値:
        F_ERR_NONE              正常終了
        F_ERR_NOMEMORY          メモリ不足
        F_ERR_INVALID_PARAM     不正なパラメータが渡された
        F_ERR_NODATA            濃度プロファイルに有効領域が存在しない
        F_ERR_NO_LICENCE        ライセンスエラー、または未初期化エラー
*/
static INT fnSMP_edges_outside(FHANDLE hsrc, DPNT_T *box,DOUBLE box_height,INT prj_width, F_EDGE1D_DIFF_FILTER diff_filter, INT arnd,
    enum f_edge1d_edge_direction direct_mode, enum f_diff_type diff_type, DOUBLE diff_thr, enum f_edge1d_sort_mode detection_mode, F_DEDGE **edges, INT *edge_num)
{
    INT ret = F_ERR_UNKNOWN;

    DPNT_T srt[2], end[2];
    DOUBLE unit_x, unit_y;
    DOUBLE norm;
    DSGMT_T  line;
    INT i;
    INT edge_cnt = 0;
    F_DEDGE* edge_buff_wk = NULL;
    F_DEDGE* edges_on_line = NULL;
    FHANDLE hvarray = NULL;

    //計測ラインそれぞれの位置を求めるため、予め箱の高さ方向の単位方向ベクトル
    //(隣り合う計測ライン同士の差分ベクトル)を求める
    srt[0] = box[0];
    end[0] = box[1];
    srt[1] = box[2];
    end[1] = box[3];

    unit_x = srt[1].x - srt[0].x;
    unit_y = srt[1].y - srt[0].y;
    norm = sqrt(unit_x*unit_x + unit_y*unit_y);

    unit_x /= norm;
    unit_y /= norm;

    //エッジ保存用メモリを確保
    hvarray = fnFIE_vectarray_alloc((enum f_objtag)0, sizeof(F_DEDGE), 0);
    if (hvarray == NULL)
    {
        ret = F_ERR_NOMEMORY;
        goto EXIT;
    }

    //1ラインずつエッジ検出
    for (i = prj_width; i < box_height - prj_width; i++)
    {
        INT line_edge_num = 0;

        //計測ラインを求める
        line.st.x = i*unit_x + srt[0].x;
        line.st.y = i*unit_y + srt[0].y;
        line.ed.x = i*unit_x + end[0].x;
        line.ed.y = i*unit_y + end[0].y;

        ret = fnFIE_edge1d_line2(hsrc, line, prj_width, diff_filter, arnd, 0, direct_mode,
            diff_type, diff_thr, detection_mode, &edges_on_line, &line_edge_num, NULL, NULL);

        //エッジが複数検出された場合は、最初のエッジのみを保存
        if (line_edge_num > 0)
        {
            ret = fnFIE_vectarray_push_back(hvarray, edges_on_line);
            if (F_ERR_NONE != ret)goto EXIT;
            edge_cnt++;
        }

        fnOAL_free(edges_on_line); edges_on_line = NULL;
    }

    // エッジ点が検出されないケースを処理
    if (edge_cnt == 0) {
        *edges = NULL;
        *edge_num = 0;
        ret = F_ERR_NONE;
        goto EXIT;
    }

    edge_buff_wk = (F_DEDGE*)fnOAL_malloc(edge_cnt * sizeof(F_DEDGE));
    if (edge_buff_wk == NULL)
    {
        ret = F_ERR_NOMEMORY;
        goto EXIT;
    }

    for (i = 0; i < edge_cnt; i++)
    {
        edge_buff_wk[i] = *(F_DEDGE*)fnFIE_vectarray_getat(hvarray, i);
    }

    *edges = edge_buff_wk;
    *edge_num = edge_cnt;
    ret = F_ERR_NONE;
EXIT:

    fnOAL_free(edges_on_line);
    fnFIE_vectarray_free(hvarray);
    if (ret != F_ERR_NONE) {
        fnOAL_free(edge_buff_wk);
    }

    return ret;
}

//実行サンプル
static INT execute()
{
    INT ret = F_ERR_UNKNOWN;

    //入力
    FHANDLE                         hsrc = NULL;//入力画像

    //箱情報
    DPNT_T                          start;      //箱の始点
    DOUBLE                          width;      //箱の幅
    DOUBLE                          height;     //箱の高さ
    DOUBLE                          rad;        //箱回転角度(ラジアン)

    enum smp_scan_direction         scan_dir;   //計測ライン方向

    //エッジ情報
    F_EDGE1D_DIFF_FILTER            diff_filter;    //微分フィルタの指定
    INT                             arnd;           //サブピクセル計算に使用する周辺情報の片幅(0以上)
    enum f_edge1d_edge_direction    direct_mode;    //検出するエッジ方向の指定
    enum f_diff_type                diff_type;      //濃度差しきい値の種別
    DOUBLE                          diff_thr;       //エッジの最小濃度差しきい値
    enum f_edge1d_sort_mode         detection_mode; //検出されるエッジの種類

    INT                             prj_width;      //検出片幅

    //出力
    F_DEDGE*                        edges = NULL;   //検出されたエッジ点保存配列のポインタ 
    INT                             edge_num;       //検出されたエッジ点個数

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

    //箱情報
    start.x = 250;
    start.y = 200;
    width = 200;
    height = 150;
    rad = 0 * PI;
    
    //計測ライン方向
    scan_dir = R_DIRECT;
    
    //エッジ情報
    diff_filter.minus_foot = 3;
    diff_filter.minus_len = 1;
    diff_filter.plus_foot = 3;
    diff_filter.plus_len = 1;
    arnd = 5;
    direct_mode = F_DRK_TO_BRI;
    diff_type = F_RELATIVE_THR;
    diff_thr = 75;
    detection_mode = F_POS_SORT;

    //検出片幅
    prj_width = 5;

    //メインサンプル実行.
    ret = fnSMP_box_edge(
        hsrc,
        start,
        width,
        height,
        rad,
        scan_dir,
        diff_filter,
        arnd,
        direct_mode,
        diff_type,
        diff_thr,
        detection_mode,
        prj_width,
        &edges,
        &edge_num
    );
    if (F_ERR_NONE != ret) goto EXIT;

    //結果表示
    {
        INT i;
        
        for (i = 0; i < edge_num; i++)
        {
            printf("%03d: x=%0.5f, y=%0.5f, q=%0.1f, mag=%0.5f\n",
                i, edges[i].x, edges[i].y, edges[i].q ,edges[i].mag);
        }

    }

EXIT:
    fnFIE_free_object(hsrc);
    fnOAL_free(edges);

    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