見出し画像

ラベリング処理解説

ラベリング処理解説


2024年11月27初稿


1.ラベリング概要

2値化画像変換の中で「距離変換」と言う結果画像が
多値画像に成る処理を説明しましたが、骨格化に応用した
距離変換よりも更に画像処理では多く使用されるラベリング
に関して本格的にクラス「Filter」でのソースコード
解説に先立ち解説します!

ラベリング元2値化画像

何も無い所が、無効画素「=0」で有効画素「=1」と2値化
画像が有る事を示します

ラベリング結果画像

8連結ラベリングを行うと「1」から「9」まで番号が付いた
8連結の塊が有る事が分かるでしょう

画像処理でのラベリングは、「4連結8連結と何れかの仕様
でツナガリのアル一塊の図形に対してナンバリング(1から
順番にシリアルナンバーを付ける)して行きます」、そして
ラベルが付けられた個別識別(番号で区別)された図形への
各種幾何計測≪図形の面積(画素数)・図形の水平幅・
垂直幅、図形の重心点位置、図形を楕円形と見なして長径・
短径・長径の画像に対しての傾き等々多数≫

2.ラベリング処理ソースコード解説

2-1.ラベリングLabelingImage(仮引数){・・・}

code:ラベリング

/************************************************************************/
/*****  LABELING,c,s,d                           ラベリング      *****/
/************************************************************************/

int             Filter::LabelingImage(
    TypeArray   *ps,                                // S配列:画像
    TypeArray   *pd,                                // D配列:LBL画像
    int         c                                   // 連結性 4/8
){
    int         n;                                  // ラベル個数
    int         size;                               // Bufferのサイズ

    InitLabelingData();                             // LBL情報初期化
    n = LabelingExecute( ps, pd, c, FALSE );        // ラベル画像作成
    if( n < 0 ){                                    // エラー発生時は
        return( n );                                // エラーを返す
    }                                               //
    size      = sizeof(TypeSf) * ( n + 1 );         // 結果BufSize算出し
    lbl_buf   = (TypeSf*)malloc( size );            // 結果Bufを確保し
    if( lbl_buf == 0 ){                             // 確保失敗時は
        return( STI_MEM );                          // 左記を返す
    }                                               //
    up_fill_void( 0, lbl_buf, size );               // 0クリア
    lbl_image = *pd;                                // LBL画像情報保存
    lbl_c     = c;                                  // 連結性保存
    lbl_n     = n;                                  // ラベル個数保存
    return( END_STI );                              // 正常終了
}

(1)関数名

「Labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「Image」は、画像

(2)仮引数

code:

int             Filter::LabelingImage(
    TypeArray   *ps,                                // S配列:画像
    TypeArray   *pd,                                // D配列:LBL画像
    int         c                                   // 連結性 4/8
){

「TypeArrayps,」は、元画像(S≒ソース画像)
「TypeArraypd,」は、結果画像(D≒ディスティネー
ション画像)
「intc,」は、連結性を示す選択コードで「4」なら4連結
「8」なら8連結です!

(3)ローカル変数

code:

){
    int         n;                                  // ラベル個数
    int         size;                               // Bufferのサイズ

「int n;」は、ラベルの結果個数
「int size;」は、計測結果格納用のメモリ確保サイズ!

(4)アルゴリズム

code:アルゴリズム

    InitLabelingData();                             // LBL情報初期化
    n = LabelingExecute( ps, pd, c, FALSE );        // ラベル画像作成
    if( n < 0 ){                                    // エラー発生時は
        return( n );                                // エラーを返す
    }                                               //
    size      = sizeof(TypeSf) * ( n + 1 );         // 結果BufSize算出し
    lbl_buf   = (TypeSf*)malloc( size );            // 結果Bufを確保し
    if( lbl_buf == 0 ){                             // 確保失敗時は
        return( STI_MEM );                          // 左記を返す
    }                                               //
    up_fill_void( 0, lbl_buf, size );               // 0クリア
    lbl_image = *pd;                                // LBL画像情報保存
    lbl_c     = c;                                  // 連結性保存
    lbl_n     = n;                                  // ラベル個数保存
    return( END_STI );                              // 正常終了
}

「InitLabelingData();」は、ラベル情報の初期化
「n=LabelingExecute(ps,pd,c,FALSE);」は、ラベリングを
処理しラベル個数をセット
「if(n<0){return(n);}」は、個数が負の数は、エラー
コードと考えられるのでエラーコードを関数辺値とし返し
終了!
「size=sizeof(TypeSf)(n+1);」は、2値化画像幾何計測値
格納用クラス「TypeSf」で定義したデータ構造に
計測結果を格納する事に成るので「sizeof(TypeSf)」で
型のサイズを取り出し「(n+1)」と余裕分を含めた必要な数
メモリ確保に必要なサイズを算出!
「lbl_buf=(TypeSf)malloc(size);」は、メモリ確保し、
「if(lbl_buf==0){return(STI_MEM);}」は、確保を失敗し
た場合にエラーコード「STI_MEM」を関数辺値とし返し終了
「up_fill_void(0,lbl_buf,size);」は、確保した領域を
一旦、0クリア!
「lbl_image=*pd;lbl_c=c;lbl_n=n;」はクラス「Filter」

code:Filter.h

class Filter : public Calculate                 // クラス定義
{

・・・・・今回は途中が長いので省略・・・・・

/********************************************************************************************/
/*****  内部で使用するデータ                                                            *****/
/********************************************************************************************/

protected:
    /***********************************************************************/
    /*****  このモジュールで参照する大域変数                            ****/
    /*****  このモジュールで使用する型定義&変数                        ****/
    /*****  ①  ラベリング用構造体フラグ                                ****/
    /*****  ②  2次モーメント一時待避用構造体                          ****/
    /*****  ③  ラベリング計測情報                                      ****/
    /*****  ④  2次モーメント演算用テーブル                            ****/
    /*****  ⑤  重心計算用設定値                                        ****/
    /*****  ⑥  2値変換出力値(1~255)                            ****/
    /***********************************************************************/

    int         init_labeling;              // ラベリング用構造体フラグ
                                            // 偽:未初期化
                                            // 真:初期化済み

    TypeArray   lbl_image;                  // ラベル画像
    int         lbl_c;                      // ラベル画像の連結性
    int         lbl_n;                      // ラベル個数(最大ラベル番号)
    TypeSf*     lbl_buf;                    // 計測結果へのポインタ
    int         lbl_fere_ok;                // フェレ径計測済みフラグ
    int         lbl_pos_ok;                 // ラベル始点計測済みフラグ
    int         lbl_area_ok;                // 面積計測済みフラグ
    int         lbl_mmt1_ok;                // 1次モーメント計測済みフラグ
    int         lbl_mmt2_ok;                // 2次モーメント計測済みフラグ

    int*        tbl_mmt2;                   // tbl[i]=Σi×i 表:整数版[1024]
    double*     tbl_mmt2f;                  // tbl[i]=Σi×i 表:実数版[4096]

    int         m_TblModeMeasureBase[10];   // MeasureBase系設定テーブル
                                            // [1] 0     :2値変換無効
                                            //     1..255:MEASURE前処理

    int         m_swMeasureBorder;          // 真→輪郭線図形計測、偽→任意図形


};

の「protected:」属性で「内部で使用するデータ」とし
定義したクラス「Filter」全体の大域変数としセット
「return(END_STI);」は、正常終了!

2-2.ラベリング初期設定InitLabelingData(void){・・・}

code:ラベリング初期設定

/************************************************************************/
/*****  このモジュールの初期化                                      *****/
/*****  「init_labeling」の指定で初回のみの処理を含む               *****/
/************************************************************************/

void        Filter::InitLabelingData(void)
{
    lbl_image.adr = 0;                          // ラベル画像=空
    lbl_c         = 0;                          // 連結性=不定
    lbl_n         = 0;                          // ラベル個数初期化
    if( init_labeling ){                        // 初期化済みなら
        if( lbl_buf != 0 ){                     // メモリ確保済みなら
            free( lbl_buf );                    // 計測結果BufPtr解放
        }                                       //
        lbl_buf = 0;                            // 計測結果BufPtr=空
    }else{                                      // 未初期化なら
        init_labeling = TRUE;                   // 初期化済みをセット
        lbl_buf = 0;                            // 計測結果BufPtr=空
    }                                           //
    lbl_fere_ok = FALSE;                        // フェレ径計測未完
    lbl_pos_ok  = FALSE;                        // ラベル始点計測未完
    lbl_area_ok = FALSE;                        // 面積未完
    lbl_mmt1_ok = FALSE;                        // 1次モーメント未完
    lbl_mmt2_ok = FALSE;                        // 2次モーメント未完
}

(1)関数名

「Init」は、英単語「initialize」の省略形です!
「Labeling」は、ラベル付け≪ナンバリングと番号付け≫
「Data」は、データで「LabelingData」でラベリング計測
での計測結果を意味します!詰まり、ラベリング計測結果
を初期化する関数です!

(2)アルゴリズム

code:アルゴリズム

{
    lbl_image.adr = 0;                          // ラベル画像=空
    lbl_c         = 0;                          // 連結性=不定
    lbl_n         = 0;                          // ラベル個数初期化
    if( init_labeling ){                        // 初期化済みなら
        if( lbl_buf != 0 ){                     // メモリ確保済みなら
            free( lbl_buf );                    // 計測結果BufPtr解放
        }                                       //
        lbl_buf = 0;                            // 計測結果BufPtr=空
    }else{                                      // 未初期化なら
        init_labeling = TRUE;                   // 初期化済みをセット
        lbl_buf = 0;                            // 計測結果BufPtr=空
    }                                           //
    lbl_fere_ok = FALSE;                        // フェレ径計測未完
    lbl_pos_ok  = FALSE;                        // ラベル始点計測未完
    lbl_area_ok = FALSE;                        // 面積未完
    lbl_mmt1_ok = FALSE;                        // 1次モーメント未完
    lbl_mmt2_ok = FALSE;                        // 2次モーメント未完
}

「lbl_image.adr=0;」は、ラベル画像ポインタを無効化
「lbl_c=0;」は、連結性を不定化
「lbl_n=0;」は、ラベル個数を0クリア
「if(init_labeling){・成立中身・}else{・不成立中身・}
」は、初期化済みならば、成立中身を実行し、その中身は
「if(lbl_buf!=0){free(lbl_buf);}」とメモリ確保して
有ったら、ラベル計測結果格納データ構造をメモリ解放、
「lbl_buf=0;」と解放した事を示す空ポインタをセット!
不成立中身は、
「init_labeling=TRUE;lbl_buf=0;」で初期化済みフラグを
立て、メモリ解放した事を示す空ポインタをセット!
「lbl_fere_ok=FALSE;lbl_pos_ok=FALSE;
lbl_area_ok=FALSE;lbl_mmt1_ok=FALSE;
lbl_mmt2_ok=FALSE;」は、フェレ径計測・ラベル始点計測
面積・1次モーメント・2次モーメントを未完≪計測結果を
算出して無い事を示す≫をセット

2-3.ラベル画像作成LabelingExecute(仮引数){・・・}

code:ラベル画像作成

/************************************************************************/
/*****      ラベル画像作成                  :実行部:画像変換のみ  *****/
/*****      LABELING,s,d,c                            *****/
/************************************************************************/

int             Filter::LabelingExecute(
    TypeArray   *ps,                                // S画像情報
    TypeArray   *pd,                                // D画像情報
    int         c,                                  // 連結性
    int         sw                                  // 真:孤立点除去
){
    return( labeling( ps, pd, c, sw, 0 ) );         // 通常画像LABELING
}

(1)関数名

「Labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「Execute」は、英単語「execute」で「実行する」を意味

(2)アルゴリズム

code:アルゴリズム

){
    return( labeling( ps, pd, c, sw, 0 ) );         // 通常画像LABELING
}

「return(labeling(ps,pd,c,sw,0));」は、下請け関数「
labeling(ps,pd,c,sw,0));」で処理します!

2-4.ラベル画像作成labeling(仮引数){・・・}

code:ラベル画像作成

/************************************************************************/
/*****      ラベル画像作成(通常/反転画像):実行部:画像変換のみ  *****/
/*****      引数検査を行い、(通常/反転)用の2値化を行い、        *****/
/*****      その画像に対してラベリング画像を生成する                *****/
/*****      ※注:実行エラー時→ラベリング計測に使用しても暴走させ  *****/
/*****      ※注:ないように結果のラベル画像を0クリアする※        *****/
/*****      LABELING,s,d,c                            *****/
/************************************************************************/

int             Filter::labeling(
    TypeArray   *ps,                                // S画像情報
    TypeArray   *pd,                                // D画像情報
    int         c,                                  // 連結性
    int         sw,                                 // 真:孤立点除去
    int         rev_sw                              // 0:通常/1:反転
){
    short       lut[ 256 ];                         // LUT:2値化用(2バイト)
    BYTE        lutB[ 256 ];                        // LUT:2値化用(1バイト)
    TypeArray   d;                                  // D画像情報:局所
    int         w;                                  // 処理幅(1,2バイト)
    int         n;                                  // ラベル個数

    if( c != 4 && c != 8 ){                         // 連結性間違い時
        return( STI_FLG );                          // 左記を返す
    }                                               //
    if( ps->h < 3 || pd->h < 3 ){                   // 水平幅が3未満
        return( STI_ARY_2 );                        // 左記を返す
    }else if( ps->v < 3 || pd->v < 3 ){             // 垂直幅が3未満
        return( STI_ARY_2 );                        // 左記を返す
    }else if( ps->w != 1 ){                         // S画像単位が仕様外
        return( STI_ARY_5 );                        // 左記を返す
    }                                               //
    w = pd->w;                                      // D画像単位取得
    if( w == 1 ){                                   // D画像単位が1バイト仕様なら
        if( rev_sw == 0 ){                          // 通常LABELING時
            lutB[ 0 ] = 0;                          // 2値化用LUT
            up_fill( 1, &lutB[ 1 ], 255 );          // パターン作成
            ConvertByte( ps, pd, lutB );            // LUT変換:S→D
            ClearRoundImage( pd, 0, 1 );            // 周辺クリア:0
            n = 1;                                  // ラベル番号初期値
        }else{                                      // 反転画像時
            lutB[ 0 ] = 1;                          // 反転画像用LUT
            up_fill( 0, &lutB[ 1 ], 255 );          // パターン作成
            ConvertByte( ps, pd, lutB );            // LUT変換:S→D
            ClearRoundImage( pd, 1, 1 );            // 周辺クリア:1
            n = 2;                                  // ラベル番号初期値
        }                                           //
    }else if( w == 2 ){                             // D画像単位が2バイト仕様なら
        if( rev_sw == 0 ){                          // 通常LABELING時
            lut[ 0 ] = 0;                           // 2値化用LUT
            up_fill_short( 1, &lut[ 1 ], 255 );     // パターン作成
            ConvertWord( ps, pd, lut );             // LUT変換:S→D
            ClearRoundImage( pd, 0, 1 );            // 周辺クリア:0
            n = 1;                                  // ラベル番号初期値
        }else{                                      // 反転画像時
            lut[ 0 ] = 1;                           // 反転画像用LUT
            up_fill_short( 0, &lut[ 1 ], 255 );     // パターン作成
            ConvertWord( ps, pd, lut );             // LUT変換:S→D
            ClearRoundImage( pd, 1, 1 );            // 周辺クリア:1
            n = 2;                                  // ラベル番号初期値
        }                                           //
    }else{                                          // D画像単位が仕様外
        return( STI_ARY_5 );                        // 左記を返す
    }                                               //
    d   = *pd;                                      // 一旦、D画像情報
    pd  = &d;                                       // を局所化
    if( w == 1 ){                                   // D画像単位が1バイト仕様なら
        n = labelingBbase( pd, c, sw, n );          // 基本部で処理☆1バイト専用☆
    }else{                                          // D画像単位が2バイト仕様なら
        n = labelingWbase( pd, c, sw, n );          // 基本部で処理
        if( n > CVT_BUF ){                          // ラベルOverなら
            n = STI_RUN;                            // 実行エラーセット
        }                                           //
    }                                               // 
    if( n == STI_RUN ){                             // 実行エラー発生時
        Clear( pd, 0 );                             // 結果画像0クリア
    }                                               //
    return( n );                                    // ラベル個数を返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味

(2)仮引数

code:仮引数

int             Filter::labeling(
    TypeArray   *ps,                                // S画像情報
    TypeArray   *pd,                                // D画像情報
    int         c,                                  // 連結性
    int         sw,                                 // 真:孤立点除去
    int         rev_sw                              // 0:通常/1:反転
){

「TypeArray* ps,」は、元画像(S≒ソース画像)
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
「int c,」は、連結性を示す選択コードで「4」なら
4連結、「8」なら8連結です!
「int sw,」は、スィッチ「sw」が真(有効)ならば、
孤立点(面積が1の図形)は、計測シナイ事を選択!
偽(無効)ならば、孤立点も計測結果を算出!
「int rev_sw」は、計測画素反転スイッチとして「=0」
なら、黒画素(=0)を背景とし、白画素(≠0)を図形画素
とし処理、スイッチ「≠0」で白画素(≠0)を背景とし、
黒画素(=0)を図形画素として計測する

(3)ローカル変数

code:ローカル変数

){
    short       lut[ 256 ];                         // LUT:2値化用(2バイト)
    BYTE        lutB[ 256 ];                        // LUT:2値化用(1バイト)
    TypeArray   d;                                  // D画像情報:局所
    int         w;                                  // 処理幅(1,2バイト)
    int         n;                                  // ラベル個数

「short lut[256];」は、2値化画像作成用のLUTです
※備考:「short」と16ビット符号付の型は、ラベル計測
に使用する2値化画像⇒ラベル画像が16ビットを使用する
事が標準と考えて下さい!
「BYTE lutB[256];」も2値化画像作成用のLUTです
※備考:「BYTE」と8ビット無符号の型は、ラベル計測
に使用する2値化画像⇒ラベル画像が8ビットを使用する
事が高速な処理だが特別な場合と考えて変数名にワザと
「B」を付けて居る事に注意して下さい!
「TypeArray d;」は、結果画像情報をローカル変数とし
変更可能にする為のローカル変数
「int w;」は、結果画素の処理幅≪1バイト・2バイト≫
を意味
「int n;」は、結果のラベル数

(4)アルゴリズム

code:アルゴリズム

    if( c != 4 && c != 8 ){                         // 連結性間違い時
        return( STI_FLG );                          // 左記を返す
    }                                               //
    if( ps->h < 3 || pd->h < 3 ){                   // 水平幅が3未満
        return( STI_ARY_2 );                        // 左記を返す
    }else if( ps->v < 3 || pd->v < 3 ){             // 垂直幅が3未満
        return( STI_ARY_2 );                        // 左記を返す
    }else if( ps->w != 1 ){                         // S画像単位が仕様外
        return( STI_ARY_5 );                        // 左記を返す
    }                                               //
    w = pd->w;                                      // D画像単位取得
    if( w == 1 ){                                   // D画像単位が1バイト仕様なら
        if( rev_sw == 0 ){                          // 通常LABELING時
            lutB[ 0 ] = 0;                          // 2値化用LUT
            up_fill( 1, &lutB[ 1 ], 255 );          // パターン作成
            ConvertByte( ps, pd, lutB );            // LUT変換:S→D
            ClearRoundImage( pd, 0, 1 );            // 周辺クリア:0
            n = 1;                                  // ラベル番号初期値
        }else{                                      // 反転画像時
            lutB[ 0 ] = 1;                          // 反転画像用LUT
            up_fill( 0, &lutB[ 1 ], 255 );          // パターン作成
            ConvertByte( ps, pd, lutB );            // LUT変換:S→D
            ClearRoundImage( pd, 1, 1 );            // 周辺クリア:1
            n = 2;                                  // ラベル番号初期値
        }                                           //
    }else if( w == 2 ){                             // D画像単位が2バイト仕様なら
        if( rev_sw == 0 ){                          // 通常LABELING時
            lut[ 0 ] = 0;                           // 2値化用LUT
            up_fill_short( 1, &lut[ 1 ], 255 );     // パターン作成
            ConvertWord( ps, pd, lut );             // LUT変換:S→D
            ClearRoundImage( pd, 0, 1 );            // 周辺クリア:0
            n = 1;                                  // ラベル番号初期値
        }else{                                      // 反転画像時
            lut[ 0 ] = 1;                           // 反転画像用LUT
            up_fill_short( 0, &lut[ 1 ], 255 );     // パターン作成
            ConvertWord( ps, pd, lut );             // LUT変換:S→D
            ClearRoundImage( pd, 1, 1 );            // 周辺クリア:1
            n = 2;                                  // ラベル番号初期値
        }                                           //
    }else{                                          // D画像単位が仕様外
        return( STI_ARY_5 );                        // 左記を返す
    }                                               //
    d   = *pd;                                      // 一旦、D画像情報
    pd  = &d;                                       // を局所化
    if( w == 1 ){                                   // D画像単位が1バイト仕様なら
        n = labelingBbase( pd, c, sw, n );          // 基本部で処理☆1バイト専用☆
    }else{                                          // D画像単位が2バイト仕様なら
        n = labelingWbase( pd, c, sw, n );          // 基本部で処理
        if( n > CVT_BUF ){                          // ラベルOverなら
            n = STI_RUN;                            // 実行エラーセット
        }                                           //
    }                                               // 
    if( n == STI_RUN ){                             // 実行エラー発生時
        Clear( pd, 0 );                             // 結果画像0クリア
    }                                               //
    return( n );                                    // ラベル個数を返す
}

「if(c!=4&&c!=8){return(STI_FLG);}」は、連結性「4,8」
以外でエラーコード「STI_FLG」を関数辺値とし返し終了
「if(ps->h<3||pd->h<3){return(STI_ARY_2);}else」は、
水平幅が元画像・結果画像で3×3サイズ未満ならエラー
コード「STI_ARY_2」を関数辺値とし返し終了
「if(ps->v<3||pd->v<3){return(STI_ARY_2);}else」は、
垂直幅が元画像・結果画像で3×3サイズ未満ならエラー
コード「STI_ARY_2」を関数辺値とし返し終了
「if(ps->w!=1){return(STI_ARY_5);}」は、元画像の画素
単位が1バイト整数以外ならエラーコード「STI_ARY_5」を
関数辺値とし返し終了
「w=pd->w;」は、結果画像画素単位を取り出し
「if(w==1){・条件成立(結果画像画素=1)・}」は、
条件「w==1」と結果画像画素1バイト整数の場合で成立
中身が、「if(rev_sw==0){・成立中身・}else{
・不成立中身・}」で内側の成立中身が
「lutB[0]=0;up_fill(1,&lutB[1],255);」で1バイト結果用
のLUTを2値化用に作成し、
「ConvertByte(ps,pd,lutB);ClearRoundImage(pd,0,1);」で
元画像を結果画像にLUT変換し、結果画像の外周一回りを
0クリア、「n=1;」でラベル番号を1番に初期化
内側の不成立中身が
「lutB[0]=1;up_fill(0,&lutB[1],255);」で1バイト結果用
のLUTを反転2値化用に作成し、
「ConvertByte(ps,pd,lutB);ClearRoundImage(pd,1,1);」で
元画像を結果画像にLUT変換し、結果画像の外周一回りを
1クリア、「n=2;」でラベル番号を2番に初期化
「}else if(w==2){・条件成立(結果画像画素=2)・}」は
条件「w==2」と結果画像画素2バイト整数の場合で成立
中身が、「if(rev_sw==0){・成立中身・}else{
・不成立中身・}」で内側の成立中身が
「lut[0]=0;up_fill_short(1,&lut[1],255);」で2バイト
結果用のLUTを2値化用に作成し、
「ConvertWord(ps,pd,lut);ClearRoundImage(pd,0,1);」で
元画像を結果画像にLUT変換し、結果画像の外周一回りを
0クリア、「n=1;」でラベル番号を1番に初期化
内側の不成立中身が
「lut[0]=1;up_fill_short(0,&lut[1],255);」で2バイト
結果用のLUTを反転2値化用に作成し、
「ConvertWord(ps,pd,lut);ClearRoundImage(pd,1,1);」で
元画像を結果画像にLUT変換し、結果画像の外周一回りを
1クリア、「n=2;」でラベル番号を2番に初期化
「}else{return(STI_ARY_5);}」で画素単位がその他の場合
エラーコード「STI_ARY_5」を関数辺値とし返し終了
「d=*pd;pd=&d;」は、結果画像情報をローカル変数に付替え
「if(w==1){n=labelingBbase(pd,c,sw,n);}」は、
条件「w==1」と結果画像画素1バイト整数の場合で成立時
「n=labelingBbase(pd,c,sw,n);」で下請け関数
「labelingBbase(pd,c,sw,n)」で処理し、結果ラベル数を
「n」にセット
「}else{}」は
条件「w==2」と結果画像画素2バイト整数の場合で成立
中身
「n=labelingWbase(pd,c,sw,n);if(n>CVT_BUF){
n=STI_RUN;}}」で下請け関数
「labelingWbase(pd,c,sw,n)」で処理し、結果ラベル数を
「n」にセット
「if(n==STI_RUN){Clear(pd,0);}」は、ラベル数が実行
エラーなら「Clear(pd,0);」で結果画像を0クリア
「return(n);」は、ラベル数(エラーコードも有り)を関数
辺値とし返し終了

2-5.ラベル画像作成labelingBbase(仮引数){・・・}

code:ラベル画像作成

/************************************************************************/
/*****      ラベル画像作成                  :基本部:画像変換のみ  *****/
/*****      ☆1バイト単位専用☆                                    *****/
/*****      ※注:  ラベル番号初期値は、通常画像で1から始まり      *****/
/*****      ※注:  反転画像で2から始まります、反転画像は、        *****/
/*****      ※注:  周囲一周を1でクリアしているので新規ラベルは    *****/
/*****      ※注:  2から始まるからです。                          *****/
/************************************************************************/

int             Filter::labelingBbase(
    TypeArray   *pd,                                        // D画像情報
    int         c,                                          // 連結性
    int         sw,                                         // 真:孤立点除去
    int         n                                           // 次回のラベル番号
){
    BYTE        cvt[256];                                   // LABEL変換Table
    int         size;                                       // 変換Table有効サイズ

    if( c == 4 ){                                           // 4連結なら
        size = ( pd->h + 1 ) / 2 * pd->v;                   // 左記でサイズ算出
    }else{                                                  // 8連結なら
        size = ( pd->h + 1 ) / 2 * ( pd->v + 1 ) / 2;       // 左記でサイズ算出
    }                                                       // 
    if( size > 256 ){                                       // 規定サイズOverなら
        return( STI_RUN );                                  // 左記を返す
    }                                                       // 
    MakeConvertTableB( cvt, size );                         // テーブル初期化
    if( sw ){                                               // 孤立点除去版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingB4Isolation( pd, cvt, n );          // 左記で処理
        }else{                                              // 8連結なら
            n = labelingB8Isolation( pd, cvt, n );          // 左記で処理
        }                                                   //
    }else{                                                  // 全図形有効版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingB4Base( pd, cvt, n );               // 左記で処理
        }else{                                              // 8連結なら
            n = labelingB8Base( pd, cvt, n );               // 左記で処理
        }                                                   //
    }                                                       //
    if( n < 0 ){                                            // エラー発生の場合
        return( n );                                        // 左記を返す
    }                                                       // 
    if( n > 1 ){                                            // 図形が存在すれば
        n = ArrangeConvertTableB( cvt, n );                 // 変換Table整理
        if( n < 0 ){                                        // エラー発生の場合
            free( cvt );                                    // メモリ解放
            return( n );                                    // 左記を返す
        }                                                   // 
        ConvertLabelTableB( pd, cvt, pd->v );               // LABEL番号変換
    }                                                       //
    return( n - 1 );                                        // ラベル数を返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「base」は、基本の意味

(2)仮引数

code:

int             Filter::labelingBbase(
    TypeArray   *pd,                                        // D画像情報
    int         c,                                          // 連結性
    int         sw,                                         // 真:孤立点除去
    int         n                                           // 次回のラベル番号
){

「TypeArraypd,」は、結果画像(D≒ディスティネー
ション画像)
「int c,」は、連結性を示す選択コードで「4」なら
4連結、「8」なら8連結です!
「int sw,」は、スィッチ「sw」が真(有効)ならば、
孤立点(面積が1の図形)は、計測シナイ事を選択!
偽(無効)ならば、孤立点も計測結果を算出!
「int rev_sw」は、計測画素反転スイッチとして「=0」
なら、黒画素(=0)を背景とし、白画素(≠0)を図形画素
とし処理、スイッチ「≠0」で白画素(≠0)を背景とし、
黒画素(=0)を図形画素として計測する
「int n」は、次回のラベル番号

(3)ローカル変数

code:

){
    BYTE        cvt[256];                                   // LABEL変換Table
    int         size;                                       // 変換Table有効サイズ

「BYTE cvt[256];」は、ラベル変換テーブル※備考:隣接す
るラベル番号を依り小さい番後に整理する為の連結番号テー
ブルで1バイト画素画像なので1バイトに格納可能な数値が
255なのでココまで用意
「int size;」は、画像の水平幅・垂直幅で理論上、ラベル
番号上限を算出し、それの格納場所

(4)アルゴリズム

code:アルゴリズム

    if( c == 4 ){                                           // 4連結なら
        size = ( pd->h + 1 ) / 2 * pd->v;                   // 左記でサイズ算出
    }else{                                                  // 8連結なら
        size = ( pd->h + 1 ) / 2 * ( pd->v + 1 ) / 2;       // 左記でサイズ算出
    }                                                       // 
    if( size > 256 ){                                       // 規定サイズOverなら
        return( STI_RUN );                                  // 左記を返す
    }                                                       // 
    MakeConvertTableB( cvt, size );                         // テーブル初期化
    if( sw ){                                               // 孤立点除去版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingB4Isolation( pd, cvt, n );          // 左記で処理
        }else{                                              // 8連結なら
            n = labelingB8Isolation( pd, cvt, n );          // 左記で処理
        }                                                   //
    }else{                                                  // 全図形有効版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingB4Base( pd, cvt, n );               // 左記で処理
        }else{                                              // 8連結なら
            n = labelingB8Base( pd, cvt, n );               // 左記で処理
        }                                                   //
    }                                                       //
    if( n < 0 ){                                            // エラー発生の場合
        return( n );                                        // 左記を返す
    }                                                       // 
    if( n > 1 ){                                            // 図形が存在すれば
        n = ArrangeConvertTableB( cvt, n );                 // 変換Table整理
        if( n < 0 ){                                        // エラー発生の場合
            free( cvt );                                    // メモリ解放
            return( n );                                    // 左記を返す
        }                                                   // 
        ConvertLabelTableB( pd, cvt, pd->v );               // LABEL番号変換
    }                                                       //
    return( n - 1 );                                        // ラベル数を返す
}

「if(c==4){size=(pd->h+1)/2pd->v;}」は、4連結の場合
「size=(pd->h+1)/2pd->v;」で(水平幅÷2)×垂直幅の
様にしてラベル番号の理論上最大値を概算見積
「else{size=(pd->h+1)/2*(pd->v+1)/2;}」は、8連結の
場合で「size=(pd->h+1)/2*(pd->v+1)/2;}」で
(水平幅÷2)×(垂直幅の÷2)の様にしてラベル番号の
理論上最大値を概算見積
「if(size>256){return(STI_RUN);}」は、最大値を概算見積
が256を超えたらエラーコード「STI_RUN」を関数辺値と
し返し終了
「MakeConvertTableB(cvt,size);」は、ラベル変換テーブル
を初期化します!
「if(sw){・成立中身・}else{・不成立中身・}」は、
成立中身が条件「sw」で孤立点除去時は、
「if(c==4){n=labelingB4Isolation(pd,cvt,n);
}else{n=labelingB8Isolation(pd,cvt,n);}」で内側の条件
「c==4」は、4連結の場合なので下請け関数
「n=labelingB4Isolation(pd,cvt,n);」で処理し、
「}else{」と8連結の場合はで下請け関数
「n=labelingB8Isolation(pd,cvt,n);}」で処理します!
不成立中身は、孤立点も含めて計測する場合は、
「if(c==4){n=labelingB4Base(pd,cvt,n);
}else{n=labelingB8Base(pd,cvt,n);}」で内側の条件
「c==4」は、4連結の場合なので下請け関数
「n=labelingB4Base(pd,cvt,n);」で処理し、
「}else{」と8連結の場合はで下請け関数
「n=labelingB8Base(pd,cvt,n);}」で処理します!
「if(n<0){return(n);}」は、ラベル数結果が負の場合は、
エラー発生としエラーコードを関数辺値とし返し終了!
「if(n>1){・・成立中身・・}」は、条件「n>1」で結果が
1を超えるとは有効な計測結果が有るとして成立中身を処理
「n=ArrangeConvertTableB(cvt,n);if(n<0){
free(cvt);return(n);}」と
「n=ArrangeConvertTableB(cvt,n);」でラベル番号を整理≪
整理前を【仮ラベル】と言う不完全な状態で整理する事で
隣接(4連結8・連結)するラベル番号を小さい方に整理し
統合し【真ラベル】と言う完全に連結した状態に成りますの
でラベル数は元の数以下に成る≫し、
「if(n<0){free(cvt);return(n);}」でラベル数結果が負の
場合は、エラー発生とし、エラーコードを関数辺値とし返し
終了!※★最注意:備考:「free(cvt);」と明らかにバグを
発見しました?!元々、「size」を算出後、動的メモリ確保
関数「malloc()」でラベル変換テーブルを確保した残滓が残
っていた様ですが、「if(n<0){return(n);}」で既にエラー
を判別して居るので恐らくコノ「free(cvt);return(n);}」
は通過シナイ筈ですし、アプリケーションを色々実行し変な
事は起きませんでした★
「ConvertLabelTableB(pd,cvt,pd->v);」でラベル番号を
整理したラベル変換テーブル「cvt」に従い、ラベル画像の
ラベル画像画素が【真ラベル】の値に整理します!
「return(n-1);」は、ラベル数を返し終了※備考:「-1」
して居る事に注意!

2-6.ラベル変換テーブル初期化(仮引数){・・・}

code:

/************************************************************************/
/*****      ラベル画像作成用変換テーブル操作:テーブル初期化        *****/
/*****      インクリメントパターン発生★1バイト単位専用★          *****/
/************************************************************************/

void            Filter::MakeConvertTableB(
    BYTE        *cvt,                               // LABEL変換TablePtr
    int         size                                // サイズ
){
    int         i;                                  // カウンタ

    for( i = 0; i < size; i++ ){                    // 0 .. サイズ-1 と
        *cvt++ = i;                                 // インクリメント
    }                                               // パターン発生
}

(1)関数名

「Make」は、作成するを意味!
「Convert」は、変換を意味!
「Table」は、テーブルを意味し、「ConvertTable」で
変換テーブルを意味する!
「B」は、1バイト整数を意味

(2)仮引数

code:

void            Filter::MakeConvertTableB(
    BYTE        *cvt,                               // LABEL変換TablePtr
    int         size                                // サイズ
){

「BYTE* cvt,」は、ラベル変換テーブルへのポインタ
「int size」は、ラベル変換テーブルのサイズ

(3)ローカル変数

code:

){
    int         i;                                  // カウンタ

「int i;」は、ループカウンタ

(4)アルゴリズム

code:アルゴリズム

    for( i = 0; i < size; i++ ){                    // 0 .. サイズ-1 と
        *cvt++ = i;                                 // インクリメント
    }                                               // パターン発生
}

「for(i=0;i<size;i++){*cvt++=i;}」は、極教科書的な
forループ構文でループカウンタを「0」からサイズ「
int size」手前まで増加しながら繰り返しループ中身
「*cvt++=i;」とカウンタをテーブルに昇順に格納

2-7.ラベル画像作成labelingB4Isolation(仮引数){
・・・}

code:

/****************************************************************************/
/*****      ラベル画像作成:基本部:4連結:1回目変換                  *****/
/*****      ☆1バイト単位専用☆                                        *****/
/*****      ☆孤立点除去版:孤立点の除去を行ったラベル画像を作成☆      *****/
/*****  ※注意1※演算範囲を(1,1)~(X終点,Y終点)とするため「h-1,v-1」 *****/
/*****          ※ただし、孤立点検査用に「下」を検査するので「h-1,v-2」 *****/
/*****          ※完全な一回り内側と混同しないように注意すること        *****/
/*****  ※注意2※最終行は、孤立点除去版ではなく通常の処理を使用        *****/
/****************************************************************************/

int             Filter::labelingB4Isolation(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 1;                                        // 演算サイズ補正
    v  -= 2;                                        // 補正※注意1※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB4IsolationX( ptr, cvt,         // 水平方向処理
                                    inc, h, n );    //
    }                                               //
    n = labelingB4BaseX( ptr, cvt, inc, h, n );     // 水平方向処理※注意2※
    return( n );                                    // 次ラベル番号返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「4」は、4連結の場合を意味します!
「Isolation」は、孤立点除去を意味します

(2)仮引数

code:

int             Filter::labelingB4Isolation(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){

「TypeArray* p,」は、画像情報へのポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

「BYTE* ptr;」は、画像画素アクセスポインタ
「int h;」は、水平幅
「int v;」は、垂直幅
「int y;」は、垂直(y座標方向)ループカウンタ
「int inc;」は、垂直方向増加幅

(4)アルゴリズム

code:アルゴリズム

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 1;                                        // 演算サイズ補正
    v  -= 2;                                        // 補正※注意1※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB4IsolationX( ptr, cvt,         // 水平方向処理
                                    inc, h, n );    //
    }                                               //
    n = labelingB4BaseX( ptr, cvt, inc, h, n );     // 水平方向処理※注意2※
    return( n );                                    // 次ラベル番号返す
}

「h=p->h;v=p->v;inc=p->inc;」は、水平幅・垂直幅と垂直
方向の増加幅取り出し
「ptr=(BYTE*)p->adr+(inc+1);」は、画素アクセスポインタ
を画像(1,1)位置にセット※備考:限定(0,0)なので右下
1画素分と1画素内側をセット
「h-=1;v-=2;」は、処理範囲を一回り内側にするが、関数の
ソースコード上部のコメントに記した事を読んで下さい!
「for(y=1;y<=v;y++,ptr+=inc){・・ループ本体}」は、
教科書的forループ構文「for(y=1;y<=v;y++,」で垂直(
座標方向)ループカウンタを「1」から「v」まで垂直幅増加
させ繰り返し、「ptr+=inc」と画像画素アクセスポインタを
垂直方向増加幅分移動しながらループ本体を
「n=labelingB4IsolationX(ptr,cvt,inc,h,n);」と下請け
関数「labelingB4IsolationX(ptr,cvt,inc,h,n);」で1行
づつ水平方向に処理「孤立点除去ラベル画像作成」し、
「v-=2;」と最後の行は処理しない!
「n=labelingB4BaseX(ptr,cvt,inc,h,n);」は、最後の行を
下請け関数「labelingB4BaseX(ptr,cvt,inc,h,n);」で1行
水平方向に処理「通常ラベル画像作成」する!
「return(n);」は、次のラベル番号≪結果ラベル個数と成る
≫を関数辺値とし返し終了

2-8.ラベル画像作成labelingB4IsolationX(仮引数){
・・}

code:

/************************************************************************/
/*****      ラベル画像作成:基本部:4連結:1回目変換:x方向      *****/
/*****      ☆1バイト単位専用☆                                    *****/
/*****      ★注意:4連結では、上と左は、直接接していないので      *****/
/*****      ★      上と左が両方連結時は小さい方の値を連結する必要が*****/
/*****      ★      あります。他の場合は、そのままの値で連結します。*****/
/*****      ☆孤立点除去版:孤立点の除去を行ったラベル画像を作成☆  *****/
/************************************************************************/

int             Filter::labelingB4IsolationX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         h,                                  // 水平幅
    int         n                                   // ラベル番号
){
    BYTE        d1;                                 // データ:上
    BYTE        d2;                                 // データ:左

    for( ; --h >= 0; px++ ){                        // 水平方向に繰返
        if( *px ){                                  // 注視点があれば
            d1 = *( px - inc );                     // 上側を取り出し
            d2 = *( px - 1 );                       // 左側を取り出し
            if( d1 == 0 ){                          // 上が無効で
                if( d2 == 0 ){                      // 左が無効なら
                    if( *( px + 1 ) == 0            // 右と下が無効(孤
                     && *( px + inc ) == 0 ){       // 立点)なら
                        *px = 0;                    // 除去する
                    }else{                          // 非孤立点なら
                        *px = n;                    // 番号をセット
                        n++;                        // 番号アップ
                    }                               //
                }else{                              // 左が有効なら
                    *px = d2;                       // 左の値をセット
                }                                   //
            }else{                                  // 上が有効で
                if( d2 == 0 ){                      // 左が無効なら
                    *px = d1;                       // 上の値をセット
                }else{                              // 両方が有効なら
                    if( d1 == d2 ){                 // 上=左の時は
                        *px = d1;                   // 上の値をセット
                    }else{                          // 上≠左の時は
                        d1 = cvt[ d1 ];             // 一旦、変換表で
                        d2 = cvt[ d2 ];             // 整理
                        if( d1 < d2 ){              // 上<左の時は
                            *px       = d1;         // 上の値をセット
                            cvt[ d2 ] = d1;         // 変換TBL d2→d1
                        }else{                      // 上>左の時は
                            *px       = d2;         // 左の値をセット
                            cvt[ d1 ] = d2;         // 変換TBL d1→d2
                        }                           //
                    }                               //
                }                                   //
            }                                       //
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「4」は、4連結の場合を意味します!
「Isolation」は、孤立点除去を意味します
「X」は、水平方向処理を意味します

(2)仮引数

code:

int             Filter::labelingB4IsolationX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         h,                                  // 水平幅
    int         n                                   // ラベル番号
){

「BYTE* px,」は、水平(X座標)方向画素処理ポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int inc,」は、垂直方向増加幅
「int h,」は、水平方向処理幅
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        d1;                                 // データ:上
    BYTE        d2;                                 // データ:左

(4)アルゴリズム

code:アルゴリズム

    for( ; --h >= 0; px++ ){                        // 水平方向に繰返
        if( *px ){                                  // 注視点があれば
            d1 = *( px - inc );                     // 上側を取り出し
            d2 = *( px - 1 );                       // 左側を取り出し
            if( d1 == 0 ){                          // 上が無効で
                if( d2 == 0 ){                      // 左が無効なら
                    if( *( px + 1 ) == 0            // 右と下が無効(孤
                     && *( px + inc ) == 0 ){       // 立点)なら
                        *px = 0;                    // 除去する
                    }else{                          // 非孤立点なら
                        *px = n;                    // 番号をセット
                        n++;                        // 番号アップ
                    }                               //
                }else{                              // 左が有効なら
                    *px = d2;                       // 左の値をセット
                }                                   //
            }else{                                  // 上が有効で
                if( d2 == 0 ){                      // 左が無効なら
                    *px = d1;                       // 上の値をセット
                }else{                              // 両方が有効なら
                    if( d1 == d2 ){                 // 上=左の時は
                        *px = d1;                   // 上の値をセット
                    }else{                          // 上≠左の時は
                        d1 = cvt[ d1 ];             // 一旦、変換表で
                        d2 = cvt[ d2 ];             // 整理
                        if( d1 < d2 ){              // 上<左の時は
                            *px       = d1;         // 上の値をセット
                            cvt[ d2 ] = d1;         // 変換TBL d2→d1
                        }else{                      // 上>左の時は
                            *px       = d2;         // 左の値をセット
                            cvt[ d1 ] = d2;         // 変換TBL d1→d2
                        }                           //
                    }                               //
                }                                   //
            }                                       //
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

「for(;--h>=0;px++){if(*px){・・処理本体・・}}」は、
forループ構文「for(;--h>=0;px++)」で水平幅分、
水平方向処理「px++」で注視点ポインタを左から右へ増加
しつつif構文「if(px)」で条件「px」と注視点が有効
画素ならば、処理本体を実行するで処理本体は、
「d1=(px-inc);d2=(px-1);」は、上側と左側の画素を取
り出し!
「if(d1==0){・・上側画素無効処理・・}else{
・・上側画素有効処理・・}」とし、条件「d1==0」の上側
画素無効処理では、
「if(d2==0){・・左側画素無効処理・・}else{px=d2;}」
で条件「d2==0」の左側画素無効処理と不成立での
「px=d2;」と注視点に左側画素データをセットし、
左側画素無効処理では、
「if((px+1)==0&&(px+inc)==0){px=0;}
else{px=n;n++;}」で条件「(px+1)==0&&(px+inc)==0」
と右側画素と下側画素何れも無効なら注視点を無効化
※備考:孤立点除去操作に成ります!
そして「else{*px=n;n++;}」と条件不成立の場合は、
「*px=n;n++;」と新しいラベル番号を付け、次ラベル番号
を増加!一段ifブロックを戻し、左側画素有処理では、
「*px=d2;」と注視点に左側をセット
一段ifブロックを戻し、上側素有処理では、
「if(d2==0){*px=d1;}else{・・左側画素有効処理内側・・
」は、条件「d2==0」で左側画素無効時は
「*px=d1;」で注視点を上側画素セット、そして左側画素
有効時は、
「d1=cvt[d1];d2=cvt[d2];」で上側と左側画素データを
ラベル変換テーブルでリンク先ラベルデータ変換し、
「if(d1<d2){*px=d1;cvt[d2]=d1;}
else{*px=d2;cvt[d1]=d2;}}」で上側<左側画素データ場合
「*px=d1;cvt[d2]=d1;」と注視点を上側データをセットし、
ラベル変換テーブルの添字「cvt[d2]」と「d2」でアクセス
する場所「左側データの場所」を「d1」上側画素データで
置き換える。更に「else{*px=d2;cvt[d1]=d2;}」で
注視点を左側データをセットし、
ラベル変換テーブルの添字「cvt[d1]」と「d1」でアクセス
する場所「上側データの場所」を「d2」左側画素データで
置き換える。
「return(n);」は、新しいラベル番号を関数辺値とし返し
終了

2-9.ラベル画像作成labelingB4BaseX(仮引数){・・}

code:

/************************************************************************/
/*****      ラベル画像作成:基本部:4連結:1回目変換:x方向      *****/
/*****      ☆1バイト単位専用☆                                    *****/
/*****      ★注意:4連結では、上と左は、直接接していないので      *****/
/*****      ★      上と左が両方連結時は小さい方の値を連結する必要が*****/
/*****      ★      あります。他の場合は、そのままの値で連結します。*****/
/************************************************************************/

int             Filter::labelingB4BaseX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         h,                                  // 水平幅
    int         n                                   // ラベル番号
){
    BYTE        d1;                                 // データ:上
    BYTE        d2;                                 // データ:左

    for( ; --h >= 0; px++ ){                        // 水平方向に繰返
        if( *px ){                                  // 注視点があれば
            d1 = *( px - inc );                     // 上側を取り出し
            d2 = *( px - 1 );                       // 左側を取り出し
            if( d1 == 0 ){                          // 上が無効で
                if( d2 == 0 ){                      // 左が無効なら
                    *px = n;                        // 番号をセット
                    n++;                            // 番号アップ
                }else{                              // 左が有効なら
                    *px = d2;                       // 左の値をセット
                }                                   //
            }else{                                  // 上が有効で
                if( d2 == 0 ){                      // 左が無効なら
                    *px = d1;                       // 上の値をセット
                }else{                              // 両方が有効なら
                    if( d1 == d2 ){                 // 上=左の時は
                        *px = d1;                   // 上の値をセット
                    }else{                          // 上≠左の時は
                        d1 = cvt[ d1 ];             // 一旦、変換表で
                        d2 = cvt[ d2 ];             // 整理
                        if( d1 < d2 ){              // 上<左の時は
                            *px       = d1;         // 上の値をセット
                            cvt[ d2 ] = d1;         // 変換TBL d2→d1
                        }else{                      // 上>左の時は
                            *px       = d2;         // 左の値をセット
                            cvt[ d1 ] = d2;         // 変換TBL d1→d2
                        }                           //
                    }                               //
                }                                   //
            }                                       //
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「4」は、4連結の場合を意味します!
「Base」は、基本的な処理(孤立点除去を伴わない)を
意味します
「X」は、水平方向処理を意味します

(2)仮引数

code:

int             Filter::labelingB4BaseX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         h,                                  // 水平幅
    int         n                                   // ラベル番号
){

「BYTE* px,」は、水平(X座標)方向画素処理ポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int inc,」は、垂直方向増加幅
「int h,」は、水平方向処理幅
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        d1;                                 // データ:上
    BYTE        d2;                                 // データ:左

「BYTE d1;」は、注視点上側画素データ
「BYTE d2;」は、注視点左側画素データ

(4)アルゴリズム

code:アルゴリズム

    for( ; --h >= 0; px++ ){                        // 水平方向に繰返
        if( *px ){                                  // 注視点があれば
            d1 = *( px - inc );                     // 上側を取り出し
            d2 = *( px - 1 );                       // 左側を取り出し
            if( d1 == 0 ){                          // 上が無効で
                if( d2 == 0 ){                      // 左が無効なら
                    *px = n;                        // 番号をセット
                    n++;                            // 番号アップ
                }else{                              // 左が有効なら
                    *px = d2;                       // 左の値をセット
                }                                   //
            }else{                                  // 上が有効で
                if( d2 == 0 ){                      // 左が無効なら
                    *px = d1;                       // 上の値をセット
                }else{                              // 両方が有効なら
                    if( d1 == d2 ){                 // 上=左の時は
                        *px = d1;                   // 上の値をセット
                    }else{                          // 上≠左の時は
                        d1 = cvt[ d1 ];             // 一旦、変換表で
                        d2 = cvt[ d2 ];             // 整理
                        if( d1 < d2 ){              // 上<左の時は
                            *px       = d1;         // 上の値をセット
                            cvt[ d2 ] = d1;         // 変換TBL d2→d1
                        }else{                      // 上>左の時は
                            *px       = d2;         // 左の値をセット
                            cvt[ d1 ] = d2;         // 変換TBL d1→d2
                        }                           //
                    }                               //
                }                                   //
            }                                       //
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

「for(;--h>=0;px++){if(*px){・・処理本体・・}}」は、
forループ構文「for(;--h>=0;px++)」で水平幅分、
水平方向処理「px++」で注視点ポインタを左から右へ増加
しつつif構文「if(px)」で条件「px」と注視点が有効
画素ならば、処理本体を実行するで処理本体は、
「d1=(px-inc);d2=(px-1);」は、上側と左側の画素を取
り出し!
「if(d1==0){・・上側画素無効処理・・}else{
・・上側画素有効処理・・}」とし、条件「d1==0」の上側
画素無効処理では、
「if(d2==0){・・左側画素無効処理・・}else{*px=d2;}」
で条件「d2==0」の左側画素無効処理と不成立での
「*px=d2;」と注視点に左側画素データをセットし、
上側画素有効処理のブロックの処理は、
「if(d2==0){*px=d1;}else{・・左側画素有効処理内側・・
」は、条件「d2==0」つまり左側無効時で
「*px=d1;」と注視点を上側画素をセット、不成立の左側
画素有効処理内側は、
「if(d1==d2){*px=d1;}else{・・内側不成立処理・・}」
で条件「d1==d2」つまり上と左側画素のデータが同じ場合
「*px=d1;」注視点を上側画素セット、内側不成立処理の
場合、
「d1=cvt[d1];d2=cvt[d2];」で上側と左側画素データを
ラベル変換テーブルでリンク先ラベルデータ変換し、
「if(d1<d2){*px=d1;cvt[d2]=d1;}
else{*px=d2;cvt[d1]=d2;}}」で上側<左側画素データ場合
「*px=d1;cvt[d2]=d1;」と注視点を上側データをセットし、
ラベル変換テーブルの添字「cvt[d2]」と「d2」でアクセス
する場所「左側データの場所」を「d1」上側画素データで
置き換える。更に「else{*px=d2;cvt[d1]=d2;}」で
注視点を左側データをセットし、
ラベル変換テーブルの添字「cvt[d1]」と「d1」でアクセス
する場所「上側データの場所」を「d2」左側画素データで
置き換える。
「return(n);」は、新しいラベル番号を関数辺値とし返し
終了

2-10.ラベル画像作成labelingB4Base(仮引数){・・}

code:

/****************************************************************************/
/*****      ラベル画像作成:基本部:4連結:1回目変換                  *****/
/*****      ☆1バイト単位専用☆                                        *****/
/*****  ※注意1※演算範囲を(1,1)~(X終点,Y終点)とするため「h-1,v-1」 *****/
/*****          ※完全な一回り内側と混同しないように注意すること        *****/
/****************************************************************************/

int             Filter::labelingB4Base(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 1;                                        // 演算サイズを
    v  -= 1;                                        // 補正※注意1※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB4BaseX( ptr, cvt, inc, h, n ); // 水平方向処理
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「4」は、4連結の場合を意味します!
「Base」は、基本的な処理を意味します

(2)仮引数

code:

int             Filter::labelingB4Base(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){

「TypeArray* p,」は、画像情報へのポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

「BYTE* ptr;」は、画像画素アクセスポインタ
「int h;」は、水平幅
「int v;」は、垂直幅
「int y;」は、垂直(y座標方向)ループカウンタ
「int inc;」は、垂直方向増加幅

(4)アルゴリズム

code:アルゴリズム

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 1;                                        // 演算サイズを
    v  -= 1;                                        // 補正※注意1※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB4BaseX( ptr, cvt, inc, h, n ); // 水平方向処理
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

「h=p->h;v=p->v;inc=p->inc;」は、水平幅・垂直幅と垂直
方向の増加幅取り出し
「ptr=(BYTE*)p->adr+(inc+1);」は、画素アクセスポインタ
を画像(1,1)位置にセット※備考:限定(0,0)なので右下
1画素分と1画素内側をセット
「h-=1;v-=1;」は、処理範囲を一回り内側にするが、関数の
ソースコード上部のコメントに記した事を読んで下さい!
「for(y=1;y<=v;y++,ptr+=inc){・・ループ本体}」は、
教科書的forループ構文「for(y=1;y<=v;y++,」で垂直(
座標方向)ループカウンタを「1」から「v」まで垂直幅増加
させ繰り返し、「ptr+=inc」と画像画素アクセスポインタを
垂直方向増加幅分移動しながらループ本体を
「n=labelingB4BaseX(ptr,cvt,inc,h,n);」と下請け関数で
「return(n);」は、次のラベル番号≪結果ラベル個数と成る
≫を関数辺値とし返し終了終了

2-11.ラベル画像作成labelingB8Isolation(仮引数){
・・}

code:

/****************************************************************************/
/*****      ラベル画像作成:基本部:8連結:1回目変換                  *****/
/*****      ☆1バイト単位専用☆                                        *****/
/*****      ☆孤立点除去版:孤立点の除去を行ったラベル画像を作成☆      *****/
/*****  ※注意1※演算範囲を(1,1)~(X終点,Y終点)とするため「h-1,v-1」 *****/
/*****          ※とすべきだが、「右上」の検査があるので水平方向の処理は*****/
/*****          ※(1,1)~(X終点-1,Y終点)まででまた、孤立点除去版の  *****/
/*****          ※為、「下」の検査があるので「h-2,v-2」とします。       *****/
/*****  ※注意2※「右上」の検査を右端では行わないので右端専用処理を使用*****/
/*****  ※注意2※右端専用処理は、孤立点除去ではなく通常の処理を使用    *****/
/*****  ※注意3※最後の行は、孤立点除去ではなく通常の処理を使用        *****/
/****************************************************************************/

int             Filter::labelingB8Isolation(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 2;                                        // 演算サイズを
    v  -= 2;                                        // 補正※注意1※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB8IsolationX( ptr, cvt,         // 水平方向処理
                                    inc, h, n );    // ※注意1※
        n = labelingB8BaseXE( ptr + h, cvt,         // 右端処理
                                        inc, n );   // ※注意2※
    }                                               //
    n = labelingB8BaseX( ptr, cvt, inc, h, n );     // 水平方向処理※注意3※
    n = labelingB8BaseXE( ptr + h, cvt, inc, n );   // 右端処理※注意3※
    return( n );                                    // 次ラベル番号返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「8」は、8連結の場合を意味します!
「Isolation」は、孤立点除去を意味します

(2)仮引数

code:

int             Filter::labelingB8Isolation(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){

「TypeArray* p,」は、画像情報へのポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

「BYTE* ptr;」は、画像画素アクセスポインタ
「int h;」は、水平幅
「int v;」は、垂直幅
「int y;」は、垂直(y座標方向)ループカウンタ
「int inc;」は、垂直方向増加幅

(4)アルゴリズム

code:アルゴリズム

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 2;                                        // 演算サイズを
    v  -= 2;                                        // 補正※注意1※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB8IsolationX( ptr, cvt,         // 水平方向処理
                                    inc, h, n );    // ※注意1※
        n = labelingB8BaseXE( ptr + h, cvt,         // 右端処理
                                        inc, n );   // ※注意2※
    }                                               //
    n = labelingB8BaseX( ptr, cvt, inc, h, n );     // 水平方向処理※注意3※
    n = labelingB8BaseXE( ptr + h, cvt, inc, n );   // 右端処理※注意3※
    return( n );                                    // 次ラベル番号返す
}

「h=p->h;v=p->v;inc=p->inc;」は、水平幅・垂直幅と垂直
方向の増加幅取り出し
「ptr=(BYTE*)p->adr+(inc+1);」は、画素アクセスポインタ
を画像(1,1)位置にセット※備考:限定(0,0)なので右下
1画素分と1画素内側をセット
「h-=2;v-=2;」は、処理範囲を一回り内側にするが、関数の
ソースコード上部のコメントに記した事を読んで下さい!
「for(y=1;y<=v;y++,ptr+=inc){・・ループ本体}」は、
教科書的forループ構文「for(y=1;y<=v;y++,」で垂直(
座標方向)ループカウンタを「1」から「v」まで垂直幅増加
させ繰り返し、「ptr+=inc」と画像画素アクセスポインタを
垂直方向増加幅分移動しながらループ本体を
「n=labelingB8IsolationX(ptr,cvt,inc,h,n);」と下請け
関数「n=labelingB8IsolationX(ptr,cvt,inc,h,n);」と
下請け関数「labelingB8BaseXE(ptr+h,cvt,inc,h,n);」と
右端処理して1行づつ水平方向に処理「孤立点除去ラベル
画像作成」し、
「v-=2;」と最後の行は処理しない!
「n=labelingB8BaseX(ptr,cvt,inc,h,n);」は、最後の行を
下請け関数「labelingB8BaseX(ptr,cvt,inc,h,n);」で1行
水平方向に処理「通常ラベル画像作成」する!
最後に下請け
関数「labelingB8BaseXE(ptr+h,cvt,inc,h,n);」と右端処理
して
「return(n);」は、次のラベル番号≪結果ラベル個数と成る
≫を関数辺値とし返し終了

2-12.ラベル画像作成labelingB8IsolationX(仮引数){
・・}

code:

/************************************************************************/
/*****      ラベル画像作成:基本部:8連結:1回目変換:x方向処理  *****/
/*****      ☆1バイト単位専用☆                                    *****/
/*****  ★注意:8連結では、(左上・右上)と(右上・左)は、直接接し*****/
/*****  ★      ていないので(左上・右上)と(右上・左)の組み合わせ*****/
/*****  ★      連結時は小さい方の値を連結する必要があります。      *****/
/*****  ★      他の場合は、そのままの値で連結します。              *****/
/*****      ☆孤立点除去版:孤立点の除去を行ったラベル画像を作成☆  *****/
/*****  ※ここでは、画像の(1,y)~(X終点座標-1,y)の範囲で処理※ *****/
/************************************************************************/

int             Filter::labelingB8IsolationX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         h,                                  // 水平幅
    int         n                                   // ラベル番号
){
    BYTE        *p;                                 // 左上側/左下側Ptr
    BYTE        *pend;                              // 終点Ptr
    int         offset_up;                          // 左上用OFFSET
    int         offset_dw;                          // 左下用OFFSET
    BYTE        d1;                                 // データ:左上
    BYTE        d2;                                 // データ:上
    BYTE        d3;                                 // データ:右上
    BYTE        d4;                                 // データ:左

    offset_up = -( inc + 1 );                       // 左上用OFFSET算出
    offset_dw = inc - 1;                            // 左下用OFFSET算出
    pend      = px + h;                             // 終点Ptrを算出
    for( ; px < pend; px++ ){                       // 水平方向に繰返
        if( *px ){                                  // 注視点があれば
            p  = px + offset_up;                    // 左上側Ptrをセット
            d1 = *p;                                // 左上側を取り出し
            d2 = *( p + 1 );                        // 上側を取り出し
            d3 = *( p + 2 );                        // 右上側を取り出し
            d4 = *( px - 1 );                       // 左側を取り出し
            if( d1 == 0 ){                          // 左上が無効なら
                if( d2 == 0 ){                      // 上が無効なら
                    if( d3 == 0 ){                  // 右上が無効で
                        if( d4 == 0 ){              // 左が無効なら
                            p = px + offset_dw;     // 左下側Ptrをセット
                            if( *( px + 1 ) == 0    // 右と左下と下と
                             && *p == 0             // 右下が無効
                             && *( p + 1 ) == 0     // (孤立点)なら
                             && *( p + 2 ) == 0 ){  //
                                *px = 0;            // 除去する
                            }else{                  // 非孤立点なら
                                *px = n;            // 番号をセット
                                n++;                // 番号アップ
                            }                       //
                        }else{                      // 左が有効なら
                            *px = d4;               // 左の値をセット
                        }                           //
                    }else{                          // 右上が有効で
                        if( d4 == 0 ){              // 左が無効なら
                            *px = d3;               // 右上の値をセット
                        }else{                      // 左が有効なら
                            if( d3 == d4 ){         // 右上=左の時は
                                *px = d3;           // 右上の値をセット
                            }else{                  // 右上≠左の時は
                                d3 = cvt[ d3 ];     // 一旦、変換表で
                                d4 = cvt[ d4 ];     // 整理
                                if( d3 < d4 ){      // 右上<左の時は
                                    *px       = d3; // 右上の値をセット
                                    cvt[ d4 ] = d3; // 変換TBL d4→d3
                                }else{              // 右上>左の時は
                                    *px       = d4; // 左の値をセット
                                    cvt[ d3 ] = d4; // 変換TBL d3→d4
                                }                   //
                            }                       //
                        }                           //
                    }                               //
                }else{                              // 上が有効なら
                    *px = d2;                       // 上の値をセット
                }                                   //
            }else{                                  // 左上が有効で
                if( d3 == 0 ){                      // 右上が無効なら
                    *px = d1;                       // 左上の値をセット
                }else{                              // 右上が有効なら
                    if( d3 == d1 ){                 // 右上=左上の時は
                        *px = d3;                   // 右上の値をセット
                    }else{                          // 右上≠左上の時は
                        d1 = cvt[ d1 ];             // 一旦、変換表で
                        d3 = cvt[ d3 ];             // 整理
                        if( d3 < d1 ){              // 右上<左上の時は
                            *px       = d3;         // 右上の値をセット
                            cvt[ d1 ] = d3;         // 変換TBL d1→d3
                        }else{                      // 右上>左上の時は
                            *px       = d1;         // 左上の値をセット
                            cvt[ d3 ] = d1;         // 変換TBL d3→d1
                        }                           //
                    }                               //
                }                                   //
            }                                       //
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「8」は、8連結の場合を意味します!
「Isolation」は、孤立点除去を意味します
「X」は、水平方向処理を意味します

(2)仮引数

code:

int             Filter::labelingB8IsolationX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         h,                                  // 水平幅
    int         n                                   // ラベル番号
){

「BYTE* px,」は、水平(X座標)方向画素処理ポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int inc,」は、垂直方向増加幅
「int h,」は、水平方向処理幅
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        *p;                                 // 左上側/左下側Ptr
    BYTE        *pend;                              // 終点Ptr
    int         offset_up;                          // 左上用OFFSET
    int         offset_dw;                          // 左下用OFFSET
    BYTE        d1;                                 // データ:左上
    BYTE        d2;                                 // データ:上
    BYTE        d3;                                 // データ:右上
    BYTE        d4;                                 // データ:左
画素位置

「BYTE* p;」は、比較用に左上側・左下側の位置をセット
する画素ポインタ
「BYTE* pend;」は、forループ構文での終端目印に使用
※備考:元々ADS社の画像処理装置のCPUはMC68K
系でコンパイラにMCC68Kと言う処理系を使用した為に
CPU内部レジスターに変数を割り当てる時に普通は比較用
にはデータレジスターを使用するが、演算用に使い切ったの
でポインタをアドレス比較とし使用する事が処理速度最速と
考えて終端目印にポインタを使用した名残です!
「int offset_up;」は、左上側にポインタを移動用のオフ
セット
「int offset_dw;」は、左下側にポインタを移動用のオフ
セット
「BYTE d1;」は、注視点左上側画素データ
「BYTE d2;」は、注視点上側画素データ
「BYTE d3;」は、注視点上側画素データ
「BYTE d4;」は、注視点左側画素データ

(4)アルゴリズム

code:アルゴリズム

    offset_up = -( inc + 1 );                       // 左上用OFFSET算出
    offset_dw = inc - 1;                            // 左下用OFFSET算出
    pend      = px + h;                             // 終点Ptrを算出
    for( ; px < pend; px++ ){                       // 水平方向に繰返
        if( *px ){                                  // 注視点があれば
            p  = px + offset_up;                    // 左上側Ptrをセット
            d1 = *p;                                // 左上側を取り出し
            d2 = *( p + 1 );                        // 上側を取り出し
            d3 = *( p + 2 );                        // 右上側を取り出し
            d4 = *( px - 1 );                       // 左側を取り出し
            if( d1 == 0 ){                          // 左上が無効なら
                if( d2 == 0 ){                      // 上が無効なら
                    if( d3 == 0 ){                  // 右上が無効で
                        if( d4 == 0 ){              // 左が無効なら
                            p = px + offset_dw;     // 左下側Ptrをセット
                            if( *( px + 1 ) == 0    // 右と左下と下と
                             && *p == 0             // 右下が無効
                             && *( p + 1 ) == 0     // (孤立点)なら
                             && *( p + 2 ) == 0 ){  //
                                *px = 0;            // 除去する
                            }else{                  // 非孤立点なら
                                *px = n;            // 番号をセット
                                n++;                // 番号アップ
                            }                       //
                        }else{                      // 左が有効なら
                            *px = d4;               // 左の値をセット
                        }                           //
                    }else{                          // 右上が有効で
                        if( d4 == 0 ){              // 左が無効なら
                            *px = d3;               // 右上の値をセット
                        }else{                      // 左が有効なら
                            if( d3 == d4 ){         // 右上=左の時は
                                *px = d3;           // 右上の値をセット
                            }else{                  // 右上≠左の時は
                                d3 = cvt[ d3 ];     // 一旦、変換表で
                                d4 = cvt[ d4 ];     // 整理
                                if( d3 < d4 ){      // 右上<左の時は
                                    *px       = d3; // 右上の値をセット
                                    cvt[ d4 ] = d3; // 変換TBL d4→d3
                                }else{              // 右上>左の時は
                                    *px       = d4; // 左の値をセット
                                    cvt[ d3 ] = d4; // 変換TBL d3→d4
                                }                   //
                            }                       //
                        }                           //
                    }                               //
                }else{                              // 上が有効なら
                    *px = d2;                       // 上の値をセット
                }                                   //
            }else{                                  // 左上が有効で
                if( d3 == 0 ){                      // 右上が無効なら
                    *px = d1;                       // 左上の値をセット
                }else{                              // 右上が有効なら
                    if( d3 == d1 ){                 // 右上=左上の時は
                        *px = d3;                   // 右上の値をセット
                    }else{                          // 右上≠左上の時は
                        d1 = cvt[ d1 ];             // 一旦、変換表で
                        d3 = cvt[ d3 ];             // 整理
                        if( d3 < d1 ){              // 右上<左上の時は
                            *px       = d3;         // 右上の値をセット
                            cvt[ d1 ] = d3;         // 変換TBL d1→d3
                        }else{                      // 右上>左上の時は
                            *px       = d1;         // 左上の値をセット
                            cvt[ d3 ] = d1;         // 変換TBL d3→d1
                        }                           //
                    }                               //
                }                                   //
            }                                       //
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

「offset_up=-(inc+1);offset_dw=inc-1;」は、左上用と
左下用の画素比較用ポインタを算出する為のオフセット値を
算出!
「pend=px+h;」は、forループ構文の判定用終点目印を
作成!
「for(;px<pend;px++){if(px){・・処理本体・・}}」は、
forループ構文「for(;px<pend;px++)で水平方向終点ま
で水平方向処理「px++」で注視点ポインタを左から右へ増加
しつつif構文「if(px)」で条件「px」と注視点が有効
画素ならば、処理本体を実行するで処理本体は、
「p=px+offset_up;」は、画素比較用ポインタを左上にセッ
ト!
「d1=p;d2=(p+1);d3=(p+2);d4=(px-1);」は、左上・
上・右上・左側の画素データを取り出す!
if分岐「if(d1==0){if(d2==0){if(d3==0){if(d4==0){
・・成立ブロック・・}」で条件「左上・上・右上・左側の
画素データが全て無効(=0)」の時、成立ブロックを実行
しその中身が、
「p=px+offset_dw;」は、画素比較用ポインタを左下にセッ
ト!
「if((px+1)==0&&p==0&&(p+1)==0&&*(p+2)==0){*px=0;
}else{*px=n;n++;}」で条件「右・左下・下・右下側の画素
データが全て無効(=0)」ならば「*px=0;」と注視点を
孤立点除去!そして条件不成立ならば「*px=n;n++;」と
注視点に新規ラベルをセットし、新規ラベル番号を増加!
if分岐「if(d1==0){if(d2==0){if(d3==0){if(d4==0){
・前述で説明・}else{・・成立ブロック・・}」での成立
ブロック中身は、「*px=d4;」と注視点に左画素データを
セット!★「if(d3==0){if(d4==0){・・}else{・・}」の
分岐処理終了★
if分岐「if(d1==0){if(d2==0){if(d3==0){・前述で説明・
}else{・・成立ブロック・・}」で条件「左上・上側の画素
データが無効(=0)で右上側が有効(≠0)」の時、成立
ブロックを実行しその中身が、
「if(d4==0){*px=d3;}else{・・不成立ブロック・・}」で
条件「左側画素データ無効(=0)」で「*px=d3;」と右上
側画素データを注視点にセットし、左側画素データ有効(
≠0)で不成立ブロックを処理、その不成立ブロックは、
「d3=cvt[d3];d4=cvt[d4];」は、右上と左画素データを
ラベル変換テーブルでリンク先に整理し、
「if(d3<d4){*px=d3;cvt[d4]=d3;}
else{*px=d4;cvt[d3]=d4;}」と条件「右上画素データが左
側画素データ未満」なら「*px=d3;cvt[d4]=d3;」と
注視点に右上側画素データをセットし、ラベル変換テーブル
の添字アクセス「cvt[d4]」の中身を右上側画素データを
セットし≪右側画素が右上側画素と番号の小さい方に整理≫
そして「else{*px=d4;cvt[d3]=d4;}」と条件「右上画素
データが左側画素データ以上」なら「*px=d4;cvt[d3]=d4;
」と注視点に左側画素データをセットし、ラベル変換
テーブルの添字アクセス「cvt[d3]」の中身を左側画素
データをセットし≪右上側画素が左側画素と番号の小さい
方に整理≫★「if(d3==0){・・}else{・・}」の
分岐処理終了★
if分岐「if(d1==0){if(d2==0){・前述で説明・}else{
*px=d2;」で条件「左上の画素データが無効(=0)で上側
が有効(≠0)」の時、成立ブロックを実行しその中身が、
「*px=d2;」と注視点に上側画素データをセット!
★「if(d1==0){if(d2==0){・前述で説明・}
else{・前述で説明・}」の分岐処理終了★
「if(d1==0){・前述で説明・}else{・ここから解説・}」と
ここから解説のブロックを解説します!
「if(d3==0){*px=d1;}else{・・不成立ブロック・・}」は
条件「d3==0」つまり右上側画素データ無効(=0)の時に
「*px=d1;」と注視点に左上側画素データをセット!
そして不成立ブロックは、
「d1=cvt[d1];d3=cvt[d3];」は、左上側・右上側画素デー
タをラベル変換テーブルでリンク先の依り小さい方に整理
「if(d3<d1){*px=d3;cvt[d1]=d3;}
else{*px=d1;cvt[d3]=d1;}」は、条件「d3<d1」つまり、
右上側画素データが左上画素データ未満の時に
「*px=d3;cvt[d1]=d3;」と注視点に右上側画素データを
セットし、ラベル変換テーブルの添字アクセス「cvt[d1]」
の中身を右上側画素データをセットしリンク先の依り
小さい方に整理、そして
「else{*px=d1;cvt[d3]=d1;}」で右上側画素データが
左上画素データ以上の時に
「*px=d1;cvt[d3]=d1;」と注視点に左上側画素データを
セットし、ラベル変換テーブルの添字アクセス「cvt[d3]」
の中身を左上側画素データをセットしリンク先の依り
小さい方に整理
「return(n);」は、次のラベル番号=ラベル数を関数辺値
とし返し終了!
☆概要解説「if分岐構文組み合わせ」☆
条件「左上・上・右上・左側の画素データが全て
無効(=0)」の成立ブロックの中では、孤立点除去条件「
右・右下・下」も全て無効の時、孤立点除去を行い!
不成立ブロックで「*px=n;n++;」と新規番号を注視点に
セットし新規ラベル番号を増加する!
そして、この組み合わせ以外は、常に隣接する画素データ
の小さい方に画素データ整理と整理した時に変換テーブル
のリンク先を依り小さい方に整理

2-13.ラベル画像作成labelingB8BaseXE(仮引数){・・}

code:

/************************************************************************/
/*****      ラベル画像作成:基本部:8連結:1回目変換:右端処理    *****/
/*****      ☆1バイト単位専用☆                                    *****/
/*****  ※ここでは、画像の(X終点座標,y)の一点を処理※            *****/
/************************************************************************/

int             Filter::labelingB8BaseXE(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         n                                   // ラベル番号
){
    BYTE        *p;                                 // 左上側Ptr
    BYTE        d1;                                 // データ:左上
    BYTE        d2;                                 // データ:上
    BYTE        d4;                                 // データ:左

    if( *px ){                                      // 注視点があれば
        p  = px - inc + 1;                          // 左上側Ptrをセット
        d1 = *p;                                    // 左上側を取り出し
        d2 = *( p + 1 );                            // 上側を取り出し
        d4 = *( px - 1 );                           // 左側を取り出し
        if( d1 == 0 ){                              // 左上が無効なら
            if( d2 == 0 ){                          // 上が無効なら
                if( d4 == 0 ){                      // 左が無効なら
                    *px = n;                        // 番号をセット
                    n++;                            // 番号アップ
                }else{                              // 左が有効なら
                    *px = d4;                       // 左の値をセット
                }                                   //
            }else{                                  // 上が有効なら
                *px = d2;                           // 上の値をセット
            }                                       //
        }else{                                      // 左上が有効で
            *px = d1;                               // 左上の値をセット
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「8」は、8連結の場合を意味します!
「Base」は、基本的な処理(孤立点除去を伴わない)を
意味します
「X」は、水平方向処理を意味します
「E」は、End、終点を意味します

(2)仮引数

code:

int             Filter::labelingB8BaseXE(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         inc,                                // 増加幅
    int         n                                   // ラベル番号
){

「BYTE* px,」は、水平(X座標)方向画素処理ポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int inc,」は、垂直方向増加幅
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        *p;                                 // 左上側Ptr
    BYTE        d1;                                 // データ:左上
    BYTE        d2;                                 // データ:上
    BYTE        d4;                                 // データ:左
画素位置

「BYTE* p;」は、比較用に右上側の位置をセットする画素
ポインタ
「BYTE d1;」は、注視点右上側画素データ
「BYTE d2;」は、注視点上側画素データ
「BYTE d4;」は、注視点左側画素データ

(4)アルゴリズム

code:

    if( *px ){                                      // 注視点があれば
        p  = px - inc + 1;                          // 左上側Ptrをセット
        d1 = *p;                                    // 左上側を取り出し
        d2 = *( p + 1 );                            // 上側を取り出し
        d4 = *( px - 1 );                           // 左側を取り出し
        if( d1 == 0 ){                              // 左上が無効なら
            if( d2 == 0 ){                          // 上が無効なら
                if( d4 == 0 ){                      // 左が無効なら
                    *px = n;                        // 番号をセット
                    n++;                            // 番号アップ
                }else{                              // 左が有効なら
                    *px = d4;                       // 左の値をセット
                }                                   //
            }else{                                  // 上が有効なら
                *px = d2;                           // 上の値をセット
            }                                       //
        }else{                                      // 左上が有効で
            *px = d1;                               // 左上の値をセット
        }                                           //
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

「if(px){・・成立ブロック・・」は、注視点が有効(
≠0)なら成立ブロックを処理、成立ブロックは
「p=px-inc+1;」は、比較用に右上側の位置をセット!
「d1=p;d2=(p+1);d4=(px-1);」は、左上・上・左の画素
★☆警告☆★
あれ、アルゴリズムと右端記載のコメントが異なる??
明らかに「d2=*(p+1);」とコメント「上側を取り出し」の
記載がオカシイ?!バグが修正されて無いと判断した!
※備考:でも大丈夫:☆大丈夫な理由☆※
ここで解説して居るラベリング処理は、画像処理ライブラリ
で使用を勧めて居ません?!同じと言う依り、モット高性能
な処理として「MeasureArea()等Measure○○○()」を使用す
る事を勧めています!大昔から有るラベリング処理のアルゴ
リズムは、処理の説明する為に残して居るだけの盲腸の様な
物と理解して下さい!因みに私が画像処理アプリケーション
を作成した時にはラベリング処理は使用して居ません!
ただし、説明する為には、残して置く必要が有ったので引き
続き、ラベリング処理の解説は行います!
因みにお勧めする処理は、エッセイ『この仕事は天職だと感
じたアルゴリズム開発』でのアルゴリズムの関数群での処理
をお勧めします!私が自身を天才と誤解するほど高性能な物
か作成出来た話をエッセイで記載して置きますので読み物と
して乞うご期待と記載して置きます!
ここまでが「大丈夫な理由」です!引き続き、この「
labelingB8BaseXE()」の説明を続けます!
★☆警告:終了☆★
「if(d1==0){if(d2==0){if(d4==0){・・この成立・・}}}」
は、※上記で示したバグ確認なのでコメントの方を正常とし
解説で条件「左上側・上側・左側の全てが無効(=0)」が
成立した場合、
「*px=n;n++;」と注視点に新規番号をセットし、新規番号を
次の番号に増加!
「if(d1==0){if(d2==0){else{・・この成立・・}}」は、
条件「左上側・上側の全てが無効(=0)で左側が有効
(≠0)」が成立した場合、
「*px=d4;」と注視点に左側をセット
「if(d1==0){else{・・この成立・・}」は、
条件「左上側が無効(=0)で上側が有効」が成立した場合
「*px=d2;」と注視点に上側をセット
「if(d1==0)の{else部・・この成立・・」は、
条件「左上側が有効(≠0)」が成立した場合、
「*px=d1;」と注視点に左側をセット
「return(n);」は、次の新規番号を関数辺値とし返し終了

2-14.ラベル画像作成labelingB8Base(仮引数){・・}

/****************************************************************************/
/*****      ラベル画像作成:基本部:8連結:1回目変換                  *****/
/*****      ☆1バイト単位専用☆                                        *****/
/*****  ※注意1※演算範囲を(1,1)~(X終点,Y終点)とするため「h-1,v-1」 *****/
/*****          ※とすべきだが、「右上」の検査があるので水平方向の処理は*****/
/*****          ※(1,1)~(X終点-1,Y終点)までとしたため「h-2,v-1」   *****/
/*****  ※注意2※「右上」の検査を右端では行わないので右端専用処理を使用*****/
/****************************************************************************/

int             Filter::labelingB8Base(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 2;                                        // 演算サイズを
    v  -= 1;                                        // 補正※注意1、注意2※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB8BaseX( ptr, cvt, inc, h, n ); // 水平方向処理※注意1※
        n = labelingB8BaseXE( ptr + h, cvt,         // 右端処理
                                        inc, n );   // ※注意2※
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

code:

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「B」は、1バイトを示し、区別の為に英大文字
「8」は、8連結の場合を意味します!
「Base」は、基本的な処理を意味します

(2)仮引数

code:

int             Filter::labelingB8Base(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // ラベル番号
){

「TypeArray* p,」は、画像情報へのポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅

「BYTE* ptr;」は、画像画素アクセスポインタ
「int h;」は、水平幅
「int v;」は、垂直幅
「int y;」は、垂直(y座標方向)ループカウンタ
「int inc;」は、垂直方向増加幅

(4)アルゴリズム

code:アルゴリズム

    h   = p->h;                                     // 画像のサイズを
    v   = p->v;                                     // 取り出し
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr + ( inc + 1 );              // 演算始点をセット
    h  -= 2;                                        // 演算サイズを
    v  -= 1;                                        // 補正※注意1、注意2※
    for( y = 1; y <= v; y++, ptr += inc ){          // 垂直方向に繰返
        n = labelingB8BaseX( ptr, cvt, inc, h, n ); // 水平方向処理※注意1※
        n = labelingB8BaseXE( ptr + h, cvt,         // 右端処理
                                        inc, n );   // ※注意2※
    }                                               //
    return( n );                                    // 次ラベル番号返す
}

「h=p->h;v=p->v;inc=p->inc;」は、水平幅・垂直幅と垂直
方向の増加幅取り出し
「ptr=(BYTE*)p->adr+(inc+1);」は、画素アクセスポインタ
を画像(1,1)位置にセット※備考:限定(0,0)なので右下
1画素分と1画素内側をセット
「h-=2;v-=1;」は、処理範囲を一回り内側にするが、関数の
ソースコード上部のコメントに記した事を読んで下さい!
「for(y=1;y<=v;y++,ptr+=inc){・・ループ本体}」は、
教科書的forループ構文「for(y=1;y<=v;y++,」で垂直(
座標方向)ループカウンタを「1」から「v」まで垂直幅増加
させ繰り返し、「ptr+=inc」と画像画素アクセスポインタを
垂直方向増加幅分移動しながらループ本体を
「n=labelingB8BaseX(ptr,cvt,inc,h,n);」と下請け
関数「n=labelingB8IsolationX(ptr,cvt,inc,h,n);」と
下請け関数「labelingB8BaseXE(ptr+h,cvt,inc,h,n);」と
右端処理して1行づつ水平方向に処理「孤立点除去ラベル
画像作成」し、
「return(n);」は、次のラベル番号≪結果ラベル個数と成る
≫を関数辺値とし返し終了

2-15.テーブルの整理ArrangeConvertTableB(仮引数){
・・}

/****************************************************************************/
/*****      ラベル画像作成用変換テーブル操作:テーブルの整理            *****/
/*****      連結した最小番号に変換できるように整理★1バイト単位専用★  *****/
/*****      返値:次のラベル番号                                        *****/
/****************************************************************************/

int             Filter::ArrangeConvertTableB(
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // 次ラベル番号
){
    BYTE        root[256];                          // ROOT変換Table
    int         i;                                  // 添字:基本
    int         a;                                  // 添字:変換前
    int         b;                                  // 添字:変換後
    int         n1;                                 // ラベル番号/個数

    for( i = n - 1; i > 0; i-- ){                   // 降順に1まで
        a = i;                                      // 基本添字→ a
        for(;;){                                    // 以下を繰り返す
            b = cvt[ a ];                           // 変換
            if( a == b ){                           // 変換完了点なら
                break;                              // 内側LOOP終了
            }                                       //
            a = b;                                  // 添字を更新
        }                                           //
        cvt[ i ] = b;                               // 対象値を更新
    }                                               //
    n1 = 1;                                         // 添字データ初期化
    for( i = 1; i < n; i++ ){                       // 1から昇順に
        a = cvt[ i ];                               // 添字を取り出し
        if( i == a ){                               // ROOTならば
            root[ i ] = n1;                         // 新番号を登録し、
            n1++;                                   // 新番号を更新
        }                                           //
    }                                               //
    for( i = n - 1; i > 0; i-- ){                   // 降順に1まで
        a        = cvt[ i ];                        // ROOT番号に変換し
        cvt[ i ] = root[ a ];                       // 新番号に変換
    }                                               //
    return( n1 );                                   // 次番号を返す
}

(1)関数名

「Arrange」は、英単語「Arrange」でカタカナ語アレンジと
言う依り直訳「整える・整理する」でラベル番号変換テーブ
ルをリンク先結合的に添字でアクセスする時に同じ最少値に
成る様に前詰め整理して新規番号を整理した最終+1に修正
する意味です!
「Convert」は、英単語「Convert」でカタカナ語コンバート
詰まり、変換テーブルで変換して最少値にリンク結合
「Table」は、テーブル、詰まり、変換テーブルを示す!
「B」は、1バイトを示し、区別の為に英大文字

(2)仮引数

code:

int             Filter::ArrangeConvertTableB(
    BYTE        cvt[],                              // LABEL変換Table
    int         n                                   // 次ラベル番号
){

「BYTE cvt[],」は、ラベル変換テーブル
「int n」は、ラベル番号/ラベル個数

(3)ローカル変数

code:

){
    BYTE        root[256];                          // ROOT変換Table
    int         i;                                  // 添字:基本
    int         a;                                  // 添字:変換前
    int         b;                                  // 添字:変換後
    int         n1;                                 // ラベル番号/個数

「BYTE root[256];」は、ラベル番号を整理する為の基底番号
を格納する変換テーブルを変換する為のローカルテーブル!
「int i;」は、昇順/降順にテーブルの添字を進行するカウ
ンタ!
「int a;」は、変換前の添字!
「int b;」は、変換後の添字!
「int n1;」は、新規ラベル番号/整理したテーブル個数で
関数終了時は次のラベル番号を意味!

(4)アルゴリズム

code:

    for( i = n - 1; i > 0; i-- ){                   // 降順に1まで
        a = i;                                      // 基本添字→ a
        for(;;){                                    // 以下を繰り返す
            b = cvt[ a ];                           // 変換
            if( a == b ){                           // 変換完了点なら
                break;                              // 内側LOOP終了
            }                                       //
            a = b;                                  // 添字を更新
        }                                           //
        cvt[ i ] = b;                               // 対象値を更新
    }                                               //
    n1 = 1;                                         // 添字データ初期化
    for( i = 1; i < n; i++ ){                       // 1から昇順に
        a = cvt[ i ];                               // 添字を取り出し
        if( i == a ){                               // ROOTならば
            root[ i ] = n1;                         // 新番号を登録し、
            n1++;                                   // 新番号を更新
        }                                           //
    }                                               //
    for( i = n - 1; i > 0; i-- ){                   // 降順に1まで
        a        = cvt[ i ];                        // ROOT番号に変換し
        cvt[ i ] = root[ a ];                       // 新番号に変換
    }                                               //
    return( n1 );                                   // 次番号を返す
}

「for(i=n-1;i>0;i--){・・ループ本体・・}」は、
forループ構文「(i=n-1;i>0;i--)」で初期化「i=n-1;」
でラベル番号の最後をセットして降順に条件「i>0;」と
「0」までループ最後「i--)」と減少方向に進行する!その
ループ本体
「a=i;for(;;){b=cvt[a];if(a==b){break;}a=b;}cvt[i]=b;」と
「a=i;」で探すラベル番号をセットし無限ループ
「for(;;){b=cvt[a];if(a==b){break;}a=b;}」で「break;」
でループ脱出する構文で、その後の「a=b;」で番号を更新
してと無限ループを繰り返す、無限ループ脱出したら、
「cvt[i]=b;」で変換テーブルの添字「i」のリンク結合
データを更新します!
「n1=1;」は、変換テーブルの番号を前詰めにする初期化
「for(i=1;i<n;i++){a=cvt[i];if(i==a){
root[i]=n1;n1++;}}」は、forループ構文
「for(i=1;i<n;i++){・・ループ中身・・}」で極教科書的
な「(i=1;i<n;i++)」と添字「i」を「n」未満の間「i++」と
増加しながらループ中身を実行でループ中身
「a=cvt[i];if(i==a){root[i]=n1;n1++;}」で
「a=cvt[i];」と変換テーブルの中身取り出し、if構文「
if(i==a){root[i]=n1;n1++;}」で条件「i==a」で成立なら
詰まり、添字と中身が同じなら、「root[i]=n1;n1++;」と
ローカルテーブル「BYTE root[256];」に「n1」と添字
データをセットし、「n1++;」とソノ添字データを増加する
ループをココのブロックは生成!
最後のブロックである
「for(i=n-1;i>0;i--){a=cvt[i];cvt[i]=root[a];}」は、
forループ構文「for(i=n-1;i>0;i--){・・ループ中身
・・}」は、「(i=n-1;i>0;i--)」と添字「i」を「n-1」から
条件「i>0;」と「i--)」で減じる方向にループし、ループの
中身「a=cvt[i];cvt[i]=root[a];」と変換テーブルを新番号
に更新し、
「return(n1);」は、関数辺値とし次の番号=個数を返し、
関数終了

2-16.変換テーブル適応ConvertLabelTableB(仮引数){・・}

/****************************************************************************/
/*****      ラベル画像作成用変換テーブル操作:変換★1バイト単位専用★  *****/
/*****      連結した最小番号に変換できるように整理                      *****/
/****************************************************************************/

void            Filter::ConvertLabelTableB(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         v                                   // 垂直幅
){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         inc;                                // 増加幅

    h   = p->h;                                     // 水平サイズと
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr;                            // 画像Ptrを取出す
    for( ; --v >= 0; ptr += inc ){                  // 垂直方向に繰返
        ConvertLabelTableBX( ptr, cvt, h );         // 水平方向の処理
    }                                               //
}

(1)関数名

「Convert」は、変換するです!
「Label」は、ラベル番号です!
「Table」は、テーブルで「LabelTable」でラベル変換
テーブルを意味します!
「B」は、1バイトを示し、区別の為に英大文字

(2)仮引数

code:

void            Filter::ConvertLabelTableB(
    TypeArray   *p,                                 // 画像情報
    BYTE        cvt[],                              // LABEL変換Table
    int         v                                   // 垂直幅
){

「TypeArray* p,」は、画像情報へのポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int v」は、画像の垂直幅

(3)ローカル変数

code:

){
    BYTE        *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         inc;                                // 増加幅

「BYTE* ptr;」は、画像画素アクセスポインタ
「int h;」は、水平幅で高速化手段
「int inc;」は、垂直方向増加幅で高速化手段

(4)アルゴリズム

code:

    h   = p->h;                                     // 水平サイズと
    inc = p->inc;                                   // 画像増加幅取出
    ptr = (BYTE*)p->adr;                            // 画像Ptrを取出す
    for( ; --v >= 0; ptr += inc ){                  // 垂直方向に繰返
        ConvertLabelTableBX( ptr, cvt, h );         // 水平方向の処理
    }                                               //
}

「h=p->h;inc=p->inc;ptr=(BYTE*)p->adr;」は、水平幅と
垂直方向増加幅をローカル変数にセットし、画像画素処理
ポインタを初期値としセット
「for(;--v>=0;ptr+=inc){
ConvertLabelTableBX(ptr,cvt,h);}」は、
forループ構文「for(;--v>=0;ptr+=inc)」で垂直方向に
垂直幅分繰り返しループし、ループ中身が終わったら、
「ptr+=inc」とポインタを垂直方向に1行進行するで中身
「ConvertLabelTableBX(ptr,cvt,h);」は、下請け関数
「ConvertLabelTableBX(・・・)」で水平方向に処理する!

2-17.変換テーブル適応ConvertLabelTableBX(仮引数){・・}

/****************************************************************************/
/*****      ラベル画像作成用変換テーブル操作:変換:x方向              *****/
/*****      連結した最小番号に変換できるように整理★1バイト単位専用★  *****/
/****************************************************************************/

void            Filter::ConvertLabelTableBX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         h                                   // 水平幅
){
    int         h4;                                 // 水平幅÷4

    h4 = h / 4;                                     // 水平幅÷4
    h -= h4 * 4;                                    // 水平幅÷4の余り
    while( --h4 >= 0 ){                             // 4単位毎に繰り返
        *px = cvt[ *px ];                           // し変換する
        px++;                                       //
        *px = cvt[ *px ];                           //
        px++;                                       //
        *px = cvt[ *px ];                           //
        px++;                                       //
        *px = cvt[ *px ];                           //
        px++;                                       //
    }                                               //
    while( --h >= 0 ){                              // 残りを繰り返し
        *px = cvt[ *px ];                           // 変換する
        px++;                                       //
    }                                               //
}

(1)関数名

「Convert」は、変換するです!
「Label」は、ラベル番号です!
「Table」は、テーブルで「LabelTable」でラベル変換
テーブルを意味します!
「B」は、1バイトを示し、区別の為に英大文字
「X」は、水平方向処理を意味します

(2)仮引数

code:

void            Filter::ConvertLabelTableBX(
    BYTE        *px,                                // x方向Ptr
    BYTE        cvt[],                              // LABEL変換Table
    int         h                                   // 水平幅
){

「BYTE* px,」は、水平(X座標)方向画素処理ポインタ
「BYTE cvt[],」は、ラベル変換テーブル
「int h」は、画像の水平幅

(3)ローカル変数

code:

){
    int         h4;                                 // 水平幅÷4

「int h4;」は、水平幅高速化手段用として4画素単位処理用

(4)アルゴリズム

code:

    h4 = h / 4;                                     // 水平幅÷4
    h -= h4 * 4;                                    // 水平幅÷4の余り
    while( --h4 >= 0 ){                             // 4単位毎に繰り返
        *px = cvt[ *px ];                           // し変換する
        px++;                                       //
        *px = cvt[ *px ];                           //
        px++;                                       //
        *px = cvt[ *px ];                           //
        px++;                                       //
        *px = cvt[ *px ];                           //
        px++;                                       //
    }                                               //
    while( --h >= 0 ){                              // 残りを繰り返し
        *px = cvt[ *px ];                           // 変換する
        px++;                                       //
    }                                               //
}

「h4=h/4;h-=h4*4;」は、変数「h4」に4単位毎の回数、
「h」は、4単位毎の回数で余る1単位回数を初期値セット
「while(--h4>=0){・・44単位毎のループ本体・・}」は、
whileループ構文「while(--h4>=0)」で4単位毎の回数
分ループ本体を繰り返しと高速化≪ループ本体実行部で無く
繰り返し条件実行を相対的に割合を低くする方法≫処理で
ループ本体
「*px=cvt[*px];px++;*px=cvt[*px];px++;
*px=cvt[*px];px++;*px=cvt[*px];px++;」と1画素単位の
処理「*px=cvt[*px];px++;」詰まり「変換テーブルで画素
を変換し、ポインタを進行」を4単位分並べた事で高速化
「while(--h>=0){*px=cvt[*px];px++;}」は、4単位の余り
の処理をココで処理!

2-18.ラベル画像作成labelingWbase(仮引数){・・・}

/************************************************************************/
/*****      ラベル画像作成                  :基本部:画像変換のみ  *****/
/*****      ※注:  ラベル番号初期値は、通常画像で1から始まり      *****/
/*****      ※注:  反転画像で2から始まります、反転画像は、        *****/
/*****      ※注:  周囲一周を1でクリアしているので新規ラベルは    *****/
/*****      ※注:  2から始まるからです。                          *****/
/************************************************************************/

int             Filter::labelingWbase(
    TypeArray   *pd,                                        // D画像情報
    int         c,                                          // 連結性
    int         sw,                                         // 真:孤立点除去
    int         n                                           // 次回のラベル番号
){
    UWORD*      cvt;                                        // LABEL変換Table
    int         size;                                       // 変換Table有効サイズ

    if( c == 4 ){                                           // 4連結なら
        size = ( pd->h + 1 ) / 2 * pd->v;                   // 左記でサイズ算出
    }else{                                                  // 8連結なら
        size = ( pd->h + 1 ) / 2 * ( pd->v + 1 ) / 2;       // 左記でサイズ算出
    }                                                       // 
    if( size > CVT_BUF ){                                   // 規定サイズOverなら
        size = CVT_BUF;                                     // 規定サイズにする
    }                                                       // 
    cvt = (UWORD*)malloc( sizeof(UWORD) * ( size + 10 ) );  // LABEL変換Tableメモリ確保
    if( cvt == 0 ){                                         // 確保失敗時は
        return( STI_MEM );                                  // 左記を返す
    }                                                       // 
    MakeConvertTable( cvt, size );                          // テーブル初期化
    if( sw ){                                               // 孤立点除去版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingW4Isolation( pd, cvt, n );          // 左記で処理
        }else{                                              // 8連結なら
            n = labelingW8Isolation( pd, cvt, n );          // 左記で処理
        }                                                   //
    }else{                                                  // 全図形有効版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingW4Base( pd, cvt, n );               // 左記で処理
        }else{                                              // 8連結なら
            n = labelingW8Base( pd, cvt, n );               // 左記で処理
        }                                                   //
    }                                                       //
    if( n < 0 ){                                            // エラー発生の場合
        free( cvt );                                        // メモリ解放
        return( n );                                        // 左記を返す
    }                                                       // 
    if( n > 1 ){                                            // 図形が存在すれば
        n = ArrangeConvertTable( cvt, n );                  // 変換Table整理
        if( n < 0 ){                                        // エラー発生の場合
            free( cvt );                                    // メモリ解放
            return( n );                                    // 左記を返す
        }                                                   // 
        ConvertLabelTable( pd, cvt, pd->v );                // LABEL番号変換
    }                                                       //
    free( cvt );                                            // メモリ解放
    return( n - 1 );                                        // ラベル数を返す
}

(1)関数名

「labeling」は、ラベル付け≪ナンバリングとも動作とし
連結した一塊を同一番号画素にして番号付け≫を行うを意味
「W」は、2バイト単位を示し、区別の為に英大文字
「base」は、基本の意味

(2)仮引数

code:

int             Filter::labelingWbase(
    TypeArray   *pd,                                        // D画像情報
    int         c,                                          // 連結性
    int         sw,                                         // 真:孤立点除去
    int         n                                           // 次回のラベル番号
){

「TypeArray*pd,」は、結果画像(D≒ディスティネー
ション画像)
「int c,」は、連結性を示す選択コードで「4」なら
4連結、「8」なら8連結です!
「int sw,」は、スィッチ「sw」が真(有効)ならば、
孤立点(面積が1の図形)は、計測シナイ事を選択!
偽(無効)ならば、孤立点も計測結果を算出!
「int rev_sw」は、計測画素反転スイッチとして「=0」
なら、黒画素(=0)を背景とし、白画素(≠0)を図形画素
とし処理、スイッチ「≠0」で白画素(≠0)を背景とし、
黒画素(=0)を図形画素として計測する
「int n」は、次回のラベル番号

(3)ローカル変数

code:

){
    UWORD       *ptr;                               // 画像Ptr
    int         h;                                  // 水平幅
    int         v;                                  // 垂直幅
    int         y;                                  // y方向カウンタ
    int         inc;                                // 増加幅
    int         lmt;                                // ラベル番号制限

「UWORD* cvt;」は、ラベル変換テーブル※備考:隣接す
るラベル番号を依り小さい番後に整理する為の連結番号テー
ブルで2バイト≪解説『エラーコード等各種単純定義』で
「typedef unsigned short UWORD;」と定義≫画素画像なので
2バイト符号無し型に格納可能な数値が65535なので
ココでポインタ変数とし用意し、アルゴリズム内でメモリ
確保します!
「int size;」は、画像の水平幅・垂直幅で理論上のラベル
番号上限を算出し

(4)関連

8連結最多ラベル数パターン

code:

#define     CVT_BUF     65536           // 変換バッファーサイズ
                                        // 32767+画像の水平幅÷2+α
                                        // 16ビットでアクセス可能最大サイズ

図は、画像の中に白い部分が無効(=0)画素で黒か
赤≪数の目印で10個置きに赤色≫が有効(≠0)と考えて
下さい!白い部分も黒・赤も1ドットサイズと思って下さ
い!
「#define CVT_BUF 65536」は、「CVT_BUF」が数値
「65536」と考えて下さい!

(5)アルゴリズム

code:

    if( c == 4 ){                                           // 4連結なら
        size = ( pd->h + 1 ) / 2 * pd->v;                   // 左記でサイズ算出
    }else{                                                  // 8連結なら
        size = ( pd->h + 1 ) / 2 * ( pd->v + 1 ) / 2;       // 左記でサイズ算出
    }                                                       // 
    if( size > CVT_BUF ){                                   // 規定サイズOverなら
        size = CVT_BUF;                                     // 規定サイズにする
    }                                                       // 
    cvt = (UWORD*)malloc( sizeof(UWORD) * ( size + 10 ) );  // LABEL変換Tableメモリ確保
    if( cvt == 0 ){                                         // 確保失敗時は
        return( STI_MEM );                                  // 左記を返す
    }                                                       // 
    MakeConvertTable( cvt, size );                          // テーブル初期化
    if( sw ){                                               // 孤立点除去版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingW4Isolation( pd, cvt, n );          // 左記で処理
        }else{                                              // 8連結なら
            n = labelingW8Isolation( pd, cvt, n );          // 左記で処理
        }                                                   //
    }else{                                                  // 全図形有効版なら
        if( c == 4 ){                                       // 4連結なら
            n = labelingW4Base( pd, cvt, n );               // 左記で処理
        }else{                                              // 8連結なら
            n = labelingW8Base( pd, cvt, n );               // 左記で処理
        }                                                   //
    }                                                       //
    if( n < 0 ){                                            // エラー発生の場合
        free( cvt );                                        // メモリ解放
        return( n );                                        // 左記を返す
    }                                                       // 
    if( n > 1 ){                                            // 図形が存在すれば
        n = ArrangeConvertTable( cvt, n );                  // 変換Table整理
        if( n < 0 ){                                        // エラー発生の場合
            free( cvt );                                    // メモリ解放
            return( n );                                    // 左記を返す
        }                                                   // 
        ConvertLabelTable( pd, cvt, pd->v );                // LABEL番号変換
    }                                                       //
    free( cvt );                                            // メモリ解放
    return( n - 1 );                                        // ラベル数を返す
}

「if(c==4){size=(pd->h+1)/2pd->v;}else{
size=(pd->h+1)/2(pd->v+1)/2;}」は、4連結の場合は、
「size=(pd->h+1)/2pd->v;」で画像の水平幅の半分と
垂直幅を掛けた物が、理論上の最大ラベルサイズと算出し、
8連結の場合は、
「size=(pd->h+1)/2(pd->v+1)/2;}」と水平幅の半分と
垂直幅の半分を掛けた物が、理論上の最大ラベルサイズと
算出!※備考※(4)関連の図で8連結の場合を示す!
4連結の場合は類推して下さい!
「if(size>CVT_BUF){size=CVT_BUF;}」は、#defineで定義
した最大値でキャップを嵌めた事を意味します!
「cvt=(UWORD*)malloc(sizeof(UWORD)*(size+10));」は、
メモリ確保としC言語標準関数「malloc()」でメモリ確保
「if(cvt==0){return(STI_MEM);}」で確保失敗時は、
エラーコード「STI_MEM」を関数辺値とし返し終了!
※備考※「UWORD cvt[CVT_BUF];」とローカル変数とし
ローカル配列を字面上文法的には定義出来ますが、覚えて
いる状態でコンパイルに使用したMCC68Kは、この
所謂、ローカル変数の配列とし64Kサイズは定義不能だ
ったのでメモリ動的確保しました!
※★注意★※メモリ動的確保もCVT_BUFでキャップを嵌めた
のは、画像サイズが大きい場合、メモリ動的確保に失敗する
可能が有るからで☆このラベリング処理では連結した図形の
幾何学的数値(長さ・面積・重心等)の算出には、工夫が
必要と考えたし、ラベル画像が「UWORD型」と2バイト
必要なのでエッセイ『高速大容量の代表的処理が、画像
処理』で記載したメモリのアクセスが処理が遅く成るので
必然的に遅く成るので工夫して近々発表する予定のエッセイ『この仕事は天職だと感じたアルゴリズム開発』で記載した
UWORD型ラベル画像を使用せずBYTE型(8ビット画素
画像)を使用したアルゴリズムが高速化(画像パターンに
依っては一桁処理速度が早く成る)を説明します!
更に、このラベリング処理の解説は、エッセイ『この仕事は天職だと感じたアルゴリズム開発』の優位性を説明する為の前振りと考えて下さい!☆
「MakeConvertTable(cvt,size);」は、下請け関数
「MakeConvertTable()」で変換テーブルをインクリメント
パターンとし作成!
「if(sw){・・孤立点除去版ブロック・・}else{・・通常版
ブロック・・}」は、条件「sw」詰まり、「孤立点除去
スィッチがON」の時は、孤立点除去版ブロック
「if(c==4){n=labelingW4Isolation(pd,cvt,n);
}else{n=labelingW8Isolation(pd,cvt,n);}」と内側条件
「c==4」で成立(詰まり、4連結の時)は、
「n=labelingW4Isolation(pd,cvt,n);」で下請け関数
「labelingW4Isolation()」で処理、
「else」で不成立(詰まり、8連結の時)は、
「n=labelingW8Isolation(pd,cvt,n);」で下請け関数
「labelingW8Isolation()」で処理、
そして通常版ブロックの場合、
「if(c==4){n=labelingW4Base(pd,cvt,n);
}else{n=labelingW8Base(pd,cvt,n);}」と内側条件
「c==4」で成立(詰まり、4連結の時)は、
「n=labelingW4Base(pd,cvt,n);」で下請け関数
「labelingW4Base()」で処理、
「else」で不成立(詰まり、8連結の時)は、
「n=labelingW8Base(pd,cvt,n);」で下請け関数
「labelingW8Base()」で処理、
「if(n<0){free(cvt);return(n);}」は、処理結果個数が
0未満は、内部でエラーが発生としエラーコードとし、
この値を関数辺値とし返し、確保したメモリを開放し、
関数終了!
「if(n>1){・・図形が存在したブロック・・}」は、
条件「n>1」でラベリング処理が成功しラベル画像作成出来
たとしてブロックを処理、そのブロックで
「n=ArrangeConvertTable(cvt,n);」は、「UWORD型」の
ラベル変換テーブルをリンク結合データ整理し、依り
小さな値に変換する処理です!
「if(n<0){free(cvt);return(n);}」は、処理結果個数が
0未満は、内部でエラーが発生としエラーコードとし、
この値を関数辺値とし返し、確保したメモリを開放し、
関数終了!
「ConvertLabelTable(pd,cvt,pd->v);」は、「UWORD型」の
画像を連結した最小番号に変換する処理です!
「free(cvt);return(n-1);」は、確保したメモリを開放し
ラベル個数を関数辺値とし返し終了
☆☆最注意☆☆
ラベル画像とラベル変換テーブルの単位が「UWORD型」の
処理関数「labelingWbase()」を解説しましたが、その中で
下請け関数として色々「UWORD型」に関する関数は説明を
省きました!これらは、正規画像処理ライブラリのソース
コード説明≪解説『解説クラスFilter○○』≫で説明
しますのでしばらくお待ちください!
今回の「ラベリング処理解説」は、次の章「3.画像処理
技術としてラベリングを勧め無い理由」の為の従来通りの
処理を解説して置く、前振りと考えて下さい!

3.画像処理技術としてラベリングを勧め無い理由

3-1.ラベリング処理の弱点

(1)処理対象画像が狭い

元々、アナログTVカメラ時代に考案されたアルゴリズムの
為に処理する画像がせいぜいVGA(640×480)
クラスの狭い範囲でしかも画像画素にノイズが多く、ノイズ
を除去すると処理出来るラベル個数が少なくても良いと考え
て「labelingBbase()」関数での説明の様に変換テーブル
「BYTE cvt[256];」で分かるようにラベル個数は≪無効が0
と考えて1~255≫と255個の連結図形しか扱えません
しかも図「8連結最多ラベル数パターン」で説明した様に
理論上、最も多い連結図形が有ると考えて
式≪ラベル個数最大=(水平幅÷2)×(垂直幅÷2)≫と
考えて正方形画像ならば、32×32のサイズの画像が最大
と成り、ラベル画像画素・変換テーブルが1バイト(
8ビット符号無し)での関数群では、今のハイビジョンクラ
スの画像≪1K(1024×1024)とか2K(2048
×2048)≫での画像では、使い難い≪使う場合は画像を
分割して後で工夫して合わせる必要が有る≫が、何故か、
詳細な関数群を説明したかは、比較的、理解し易い単純な
8ビット処理と考えたからで説明だけで実用品ではありませ
んアルゴリズムの理解だけに使用して下さい!
この弱点を補う為に「labelingWbase()」関数での説明の様
に変換テーブル「UWORD*cvt;」で分かるように「UWORD型」
2バイト(16ビット符号無し)でラベルが1~65535
と65535と式≪ラベル個数最大=(水平幅÷2)×
(垂直幅÷2)≫と考えて正方形画像ならば、512×
512のサイズの画像が最大と成りVGAサイズでは2値化
時にノイズが除去出来ていたら十分に使用可能で遇った!
しかしながら、今のハイビジョンクラスの画像≪
1K(1024×1024)とか2K(2048×2048
)≫での画像では、使い難いのは理解出来ると思います!

(2)処理速度が遅い

エッセイ『高速大容量の代表的処理が、画像処理』で説明し
た様にラベル画像が2バイトを使用して居る為にメモリアク
セスに処理時間が盗られ遅く成る?!
閑話休題、エッセイ『吉田君』で記載した営業マンに成った
吉田君が「65535個の限界を越えたラベルの処理が必要
で此れが出来無いと営業が出来無い」と泣き着いて来たので
本格的・抜本的にアルゴリズムの根本から変えた≪ラベリン
グ処理で連結画像毎にラベル画像で区別した連結画像に対し
て、マトメて幾何学的特徴(長さ・面積・重心位置等)を
処理「例えば、面積ならば、同じラベル番号画素の数算出」
を行うと言う手法が取れるが、ラベル画像を作成するで無く
連結画像を単独に一個づつ特徴を計測し計測が済んだら画素
を無効化して無いも同然とし次のラベル相当の連結画像に対
する幾何学的特徴を算出する処理を作成すると圧倒的に
高速化した事がベンチマークテストで解り、更にラベル画像
を作成する必要が無いのでドンなに大きな面積の画像サイズ
にも対応可能と解った≫⇒近々紹介するエッセイ『この仕事
は天職だと感じたアルゴリズム開発』で該当アルゴリズムの
解説を行います!と言うより、
エッセイ『この仕事は天職だと感じたアルゴリズム開発』を
発表する前振りとしてラベリング処理の解説を行いました!

文末

いいなと思ったら応援しよう!