ラベリング処理解説
ラベリング処理解説
2024年11月27初稿
1.ラベリング概要
2値化画像変換の中で「距離変換」と言う結果画像が
多値画像に成る処理を説明しましたが、骨格化に応用した
距離変換よりも更に画像処理では多く使用されるラベリング
に関して本格的にクラス「Filter」でのソースコード
解説に先立ち解説します!
何も無い所が、無効画素「=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)関連
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個の限界を越えたラベルの処理が必要
で此れが出来無いと営業が出来無い」と泣き着いて来たので
本格的・抜本的にアルゴリズムの根本から変えた≪ラベリン
グ処理で連結画像毎にラベル画像で区別した連結画像に対し
て、マトメて幾何学的特徴(長さ・面積・重心位置等)を
処理「例えば、面積ならば、同じラベル番号画素の数算出」
を行うと言う手法が取れるが、ラベル画像を作成するで無く
連結画像を単独に一個づつ特徴を計測し計測が済んだら画素
を無効化して無いも同然とし次のラベル相当の連結画像に対
する幾何学的特徴を算出する処理を作成すると圧倒的に
高速化した事がベンチマークテストで解り、更にラベル画像
を作成する必要が無いのでドンなに大きな面積の画像サイズ
にも対応可能と解った≫⇒近々紹介するエッセイ『この仕事
は天職だと感じたアルゴリズム開発』で該当アルゴリズムの
解説を行います!と言うより、
エッセイ『この仕事は天職だと感じたアルゴリズム開発』を
発表する前振りとしてラベリング処理の解説を行いました!