ハフ円検出
[ビジョンツール]


関数

INT FVALGAPI fnFIE_hough_circles_detect (const DEDGE_T *vpEdgePnts, INT iPntsNum, DOUBLE dRMin, DOUBLE dRMax, DOUBLE dRsl, INT iQRange, INT iEdgePercent, INT iMinScore, INT iCircleColor, INT bRefine, INT iMergeXY, INT iMergeR, HCD_CIRCLE_T **vppCircles, INT *ipCirclesNum, INT *ipPntsIndex)
 ハフ変換投票によって点列から円を検出する

関数

INT FVALGAPI fnFIE_hough_circles_detect ( const DEDGE_T vpEdgePnts,
INT  iPntsNum,
DOUBLE  dRMin,
DOUBLE  dRMax,
DOUBLE  dRsl,
INT  iQRange,
INT  iEdgePercent,
INT  iMinScore,
INT  iCircleColor,
INT  bRefine,
INT  iMergeXY,
INT  iMergeR,
HCD_CIRCLE_T **  vppCircles,
INT *  ipCirclesNum,
INT *  ipPntsIndex 
)

ハフ変換投票によって点列から円を検出する

ハフ変換投票を行って、入力点列 vpEdgePnts から円を検出します。入力点数は4点以上 なければなりません。点の種類はDEDGE_Tですが、エッジの向き情報を使うことで投票を高速化しています。 この関数は入力点の中に重複点がない前提で処理を行います。

入力パラメーター dRMin(円の最小許容半径)dRMax(円の最大許容半径) の差は処理時 間と直接関係があり、差が大きくなればなるほど処理時間は増えます。

分解能はパラメーター dRsl によって指定できます。分解能は検出する円の中心座標と半径の単位であり、 0.5を指定すれば0.5画素単位で中心座標や半径を求めることができます。ただし dRsl のみ小さくして いった時は円の検出性能は低下します。分解能を小さく設定すると、投票次数は線形に、投票空間は 二乗に増大するので、あまり小さい値を入力しないで下さい。また dRMindRsl の3倍以上でなければなりません。

エッジ向きの情報を活用するため、その誤差範囲をパラメーター iQRange として指定できます。 エッジの向きに対して誤差範囲の分(±iQRange)だけ投票を行うため iQRange を小さくすると高速になります。 iQRange は角度単位で、0以上、90未満の整数値でなければなりません。

投票空間から円を検出する際に二つの閾値を使います。円上のエッジ点数の閾値は iMinScore で、 エッジ点数と円周長の百分率の閾値は iEdgePercent です。円周長は検出する円の半径によって算出するので、 分解能 dRsl によって変わりません。 iEdgePercent は0以上100未満の整数値 iMinScore は4以上 でなければなりません。 iMinScoreiEdgePercent ともにあまりに小さい値を設定すると、 計算不可能エラーが出でくる可能性があります。円を検出するときに使用する閾値は下記によって算出します。: 

\[ T = MAX( S \cdot iEdgePercent, iMinScore ) \]

ここで:

  • $S$ は(投票空間における)円周上の画素数
  • $MAX$ は二つ数値の大きい値を選ぶ
  • $T$ は判定用閾値

エッジの向きは必ず黒から白へ向かいます。白い円を検出する場合 iCircleColor は WHITE_COLORに設定してください。反対に黒い円を検出する場合にはBLACK_COLORに設定してください。 色が未知、或いは両方ともに検出したい場合にはBLACK_WHITE_COLORに設定します。 それ以外の値を iCircleColor に入力するとパラメーター不正になります。

パラメータ bRefine がTRUEに設定されているときには、ロバスト最小2乗法( fnPAL_rbs_circle() ) を使って、円の再算出を行います。円のパラメーターを再算出をしないときには bRefine をFALSEに設定します。

検出された円の半径と円心位置が近いもの同士を同一円として統合しますが、 iMergeXY を円心位置の統合範囲、 iMergeR を円半径の統合範囲として指定します。 例えば、iMergeXY = 2、iMergeR = 1と設定して円検出を行った場合、 ある円は局所領域内(半径±1、円心位置±2)に存在するスコアが最大の円に統合されます。 すなわち、その領域内で検出された他の円はすべてこのスコア最大の円に統合される、ということです。 iMergeXYiMergeR は統合したい座標範囲で、 dRsl との依存関係はありません。 iMergeXYiMergeR ともに0以上の値を設定しなければなりません。 両方ともに0に設定した場合は統合処理は行われません。

vppCirclesipCirclesNum には検出された円とその数が格納されます。 vppCircles には内部で確保したメモリのポインタを返します。 返されたメモリは、不要になったら fnOAL_free() を使用してユーザーが解放してください。 本関数の呼び出し時 *vppCircles はNULLでなければなりません。

出力の各円に対応する点を取得したい時は、予め入力点列と同サイズのINTの配列を確保して、 パラメーター ipPntsIndex として渡します。 ipPntsIndex には点が対応するの円のindexが格納されます。 点の対応する円がないときは ipPntsIndex[i] = -1 となっています。 各点と円との対応関係が不要なときは ipPntsIndex にNULLを指定してください。

引数:
[in] vpEdgePnts 入力点列
[in] iPntsNum 入力点数, 4点以上なければならない
[in] dRMin 検出目標にする円の最小半径 ( >= 3 * dRsl )
[in] dRMax 検出目標にする円の最大半径
[in] dRsl 分解能
[in] iQRange エッジ方向の片幅, 度単位で[0, 90)
[in] iEdgePercent 円上のエッジ点数と円周の百分率の閾値, [0, 99]
[in] iMinScore 円上のエッジ点数の閾値, 数値は4以上を指定する
[in] iCircleColor 円の色。
  • BLACK_COLOR: 黒い円を検出する
  • WHITE_COLOR: 白い円を検出する
  • BLACK_WHITE_COLOR: 円の色は未知、未定の場合
[in] bRefine ロバスト推定法による検出円のパラメータを算出
  • TRUE: ロバスト推定法を使う
  • FALSE:ロバスト推定法を使わない
[in] iMergeXY 検出された円を統合する円心範囲サイズ、0以上に設定する
[in] iMergeR 検出された円を統合する円半径範囲サイズ、0以上に設定する
[out] vppCircles 出力円の配列, メモリは関数内部で確保するので、必ずNULLで入力する
[out] ipCirclesNum 検出された円の数量
[in,out] ipPntsIndex 検出された円上の点のindex
戻り値:
F_ERR_NONE 正常終了
F_ERR_NOMEMORY メモリ不足
F_ERR_INVALID_PARAM パラメーター不正
F_ERR_CALC_IMPOSSIBLE iMinScore の値が小さ過ぎるので、計算不可能
F_ERR_NO_LICENCE ライセンスエラー、または未初期化エラー
使用例:
// エラー処理は省略しているので注意して下さい。
#include "fie.h"
#include "oal_aloc.h"   //メモリ管理

//検出結果を画像へ描画し,保存する
VOID img_draw_save(FHANDLE hsrc, INT width, INT height, F_DEDGE* pedges, 
              INT edge_num, HCD_CIRCLE_T* pcircles, INT circles_num)
{
    //ループ用
    INT i;

    //検出した円の描画用変数
    DPNT_T center;                      //円の中心のX,Y座標
    DOUBLE plot_val[] = { 255, 0, 0 };  //描画の際の上書き濃度値
    DOUBLE radius;                      //円の半径

    //ルート画像
    FHANDLE hedge = NULL;       //エッジ検出画像
    FHANDLE hcircle = NULL;     //円検出画像

    //チャイルド画像
    FHANDLE hcolor0 = NULL;     //R画像
    FHANDLE hcolor1 = NULL;     //G画像
    FHANDLE hcolor2 = NULL;     //B画像

    //エッジ検出画像の領域を確保します
    hedge = fnFIE_img_root_alloc( F_IMG_UC8, 3, width, height );
    //円検出画像の領域を確保します
    hcircle = fnFIE_img_root_alloc( F_IMG_UC8, 3, width, height );

    //エッジ検出画像のチャイルド画像領域を確保します
    hcolor0 = fnFIE_img_child_alloc_single_ch(hedge, 0, 0, 0, width, height);
    hcolor1 = fnFIE_img_child_alloc_single_ch(hedge, 1, 0, 0, width, height);
    hcolor2 = fnFIE_img_child_alloc_single_ch(hedge, 2, 0, 0, width, height);

    //エッジの検出結果と円検出結果をカラーで表示するために3chの画像に入力画像の濃度値をコピー
    fnFIE_img_copy(hsrc, hcolor0);
    fnFIE_img_copy(hsrc, hcolor1);
    fnFIE_img_copy(hsrc, hcolor2);
    fnFIE_img_copy(hedge, hcircle);

    for(i=0; i<edge_num; i++)
    {
        // 取得したエッジ点を画像へ描画する(サブピクセル精度で点列を取得しているため四捨五入する)
        fnFIE_img_set_dens(hedge, 1, (INT)(pedges[i].x + 0.5), (INT)(pedges[i].y + 0.5), 255);
    }

    for(i=0; i<circles_num; i++)
    {
        //描画のため変数の型を変更し,格納する
        center.x = pcircles[i].xc;
        center.y = pcircles[i].yc;
        radius   = pcircles[i].r;

        //円を画像へ描画する
        fnFIE_draw_circle(hcircle, plot_val, F_DRAW_LINE, center, radius);
    }

    //結果画像を保存します
    fnFIE_save_png("edge.png", hedge, -1);
    fnFIE_save_png("circle.png", hcircle, -1);

    //解放
    fnFIE_free_object( hedge );
    fnFIE_free_object( hcircle );
    fnFIE_free_object( hcolor0 );
    fnFIE_free_object( hcolor1 );
    fnFIE_free_object( hcolor2 );
}


//円ハフ検出をする
VOID hough_circle(F_DEDGE* pedges, INT edge_num, 
                  HCD_CIRCLE_T** ppcircles, INT* pcircles_num)
{
    //ループ用変数
    INT i;

    //円ハフパラメータ
    DEDGE_T* pedge_pnts = NULL;     //入力点列
    DOUBLE r_min;                   //検出目標にする円の最小半径 ( >= 3 * rsl )
    DOUBLE r_max;                   //検出目標にする円の最大半径
    DOUBLE r_sl;                    //分解能
    INT q_range;                    //エッジ方向の片幅, 度単位で[0, 90)
    INT edge_percent;               //円上のエッジ点数と円周の百分率の閾値, [0, 99]
    INT min_score;                  //円上のエッジ点数の閾値, 数値は4以上を指定する
    INT circle_color;               //円の色
    INT refine;                     //ロバスト推定法による検出円のパラメータを算出
    INT merge_xy;                   //検出された円の統括する円心範囲サイズ、0以上に設定する 
    INT merge_r;                    //検出された円の統括する円半径範囲サイズ、0以上に設定する 

    //円ハフパラメータ設定
    r_min = 10;
    r_max = 40;
    r_sl = 1;
    q_range = 10;
    edge_percent = 30;
    min_score = 8;
    circle_color = BLACK_WHITE_COLOR; 
    refine = TRUE;
    merge_xy = 2;
    merge_r = 1;

    //円ハフ検出の点列入力配列の領域を確保
    pedge_pnts = (DEDGE_T*)fnOAL_malloc((sizeof(DEDGE_T)) * edge_num);

    //エッジ検出結果の点列格納構造体と円ハフ検出に用いる点列の構造体の型が違うため,格納しなおす
    for(i=0;i<edge_num;i++)
    {
        pedge_pnts[i].x=pedges[i].x;    //X座標
        pedge_pnts[i].y=pedges[i].y;    //Y座標
        pedge_pnts[i].q=pedges[i].q;    //エッジの勾配
    }

    //円ハフの検出(2番目の引数は入力点数,4点以上でなければならない)
    fnFIE_hough_circles_detect(pedge_pnts, edge_num, r_min, r_max, r_sl, q_range,
                               edge_percent, min_score, circle_color,TRUE, merge_xy,
                               merge_r, ppcircles, pcircles_num, NULL);

    fnOAL_free( pedge_pnts );       //取得したエッジの開放
}


//エッジを検出する
VOID edge_sobel(FHANDLE hsrc, F_DEDGE** ppedges, INT* pedge_num)
{
    // エッジ検出パラメータ
    F_EDGE_SOBEL_PARAMS params;
    UINT feat_mode;
    INT border_mode;
    DPNT_T offset;
 
    // エッジパラメータの設定
    params.mag_threshold = 40;  // 強度しきい値
    params.nms_length = 1;      // 非極大抑制のフィルタ片幅

    feat_mode = F_EDGE_FEAT_DIRECT;     // 勾配方向、強度データ保持
    border_mode = F_BORDER_NONE;        // ボーダー処理
    offset.x = 0;                       // オフセット量
    offset.y = 0;                       // オフセット量

    // ソーベルフィルタを利用したエッジ検出の実行
    //通常版ではエッジの勾配の値が度(degree)のため勾配がradianで出力されるサブピクセル版を使用
    fnFIE_edge_sobel_subpix(hsrc, NULL,&params, feat_mode,
                            border_mode, offset,ppedges, pedge_num);
}


//エッジ検出->円ハフ検出->結果画像を出力する
VOID circles_detect_sample()
{
    //ルート画像
    FHANDLE hsrc = NULL;

    //入力画像の幅と高さ
    INT width, height;

    F_DEDGE* pedges = NULL;             //取得されるエッジ
    INT edge_num ;                      //取得されるエッジの数
    HCD_CIRCLE_T* pcircles = NULL;      //出力円の配列, メモリは関数内部で確保するので、必ずNULLで入力する
    INT circles_num ;                   //検出された円の数量 

    //入力画像の読み込み
    //適当な画像を読み込んでください
    fnFIE_load_png( "floppy1.png", &hsrc, F_COLOR_IMG_TYPE_UC8 );

    //入力画像の幅と高さを取得します
    width   = fnFIE_img_get_width( hsrc );
    height  = fnFIE_img_get_height( hsrc ); 

    //Sobelフィルタによるエッジ検出
    edge_sobel(hsrc, &pedges, &edge_num);

    //円ハフ検出
    hough_circle(pedges, edge_num, &pcircles, &circles_num);

    //検出結果を画像へ描画し、保存する
    img_draw_save(hsrc, width , height, pedges, edge_num, pcircles, circles_num);

    //解放
    fnOAL_free(pedges);         //取得したエッジの開放
    fnOAL_free(pcircles);       //取得した円の解放
    fnFIE_free_object( hsrc );  //入力画像領域の解放
}


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

    //エッジ検出->円ハフ検出->結果画像を出力する
    circles_detect_sample();

    // 終了処理
    fnFIE_teardown();

    return 0;
}
処理結果例:
以下は、 fnFIE_edge_sobel_subpix() にて検出したエッジに対してハフ円検出をした結果です。
fnFIE_edge_sobel_subpix() で指定したパラメータは以下の通りです。
  • params.mag_threshold = 40
  • params.nms_length = 1
本関数にて指定したパラメータは以下の通りです。
  • dRMin = 10
  • dRMax = 40
  • dRsl = 1
  • iQRange = 10
  • iEdgePercent = 30
  • iMinScore = 8
  • iCircleColor = BLACK_WHITE_COLOR
  • bRefine = TRUE
  • iMergeXY = 2
  • iMergeR = 1
fie_edge2d_sobel.png

入力エッジ画像

fie_hough_circle.png

処理結果画像


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