距離変換・骨格化
距離変換・骨格化
2024年11月6初稿
直接、アプリで使用するのは、骨格化ですが、骨格化を行う
為に距離変換が必要に成りますので距離変換と言う多値画像
が結果として得られる処理から説明します?!
2値図形の骨格化とは、概念としては、図形の背骨みたいな
物と考えて下さい!距離変換した距離画像の標高の高い尾根
を結んだ者です!
2値図形から距離変換した距離画像とは、図形の最外周を
「1」として一回り変換し、その内側を「2」の外周をぐるっ
とセットし、その内側を「3」と中に行くほど画素の値が、
大きく成る事で一番内側が一番大きな値に成る事で「距離」
とは、外周から内側に向かった外周からどれだけ離れて居る
かの数値です!丁度、地図で海面≒0で標高が海から離れる
と段々高く成る(数値が大きく成る)と考えて下さい?!
そして骨格化とは、その標高の高い尾根を結んだ物と考える
と図形の背骨の様な構造線に成ると想像して下さい!
数値「1」が入っている升が有効な画素でその一塊を距離
変換して行きます
数値「1」が最外周でソノ内側が大きな数に成って居る事が
分かるでしょう!
1.距離変換
(1)ソースコード解説DistanceImage()
code:DistanceImage(仮引数){}
/************************************************************************/
/***** 距離画像作成 :実行部*****/
/***** DISTANCE,s,d,c *****/
/************************************************************************/
int Filter::DistanceImage(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
int sti; // ステータス情報
if( c != 4 && c != 8 ){ // 連結性間違い時
return( STI_FLG ); // 左記を返す
} //
sti = distance( ps, pd, c ); // 基本部で処理
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
ClearRoundImage( pd, 0, 1 ); // 1周周囲クリア
return( END_STI ); // 正常終了
}
(1-1)関数名
「Distance」は、距離を意味する!
「Image」は、画像で画像の中で距離変換を行います!
(1-2)仮引数
code:仮引数
int Filter::DistanceImage(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
「TypeArray* ps,」は、元画像(S≒ソース画像)
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
「int c,」は、連結性を示す選択コードで「4」なら4連結
「8」なら8連結です!
(1-3)ローカル変数
code:ローカル変数
){
int sti; // ステータス情報
「int sti;」は、関数のステータス情報(エラーコード)で
す!
(1-4)アルゴリズム
code:アルゴリズム
if( c != 4 && c != 8 ){ // 連結性間違い時
return( STI_FLG ); // 左記を返す
} //
sti = distance( ps, pd, c ); // 基本部で処理
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
ClearRoundImage( pd, 0, 1 ); // 1周周囲クリア
return( END_STI ); // 正常終了
}
「if(c!=4&&c!=8){return(STI_FLG);}」は、連結性が4と
8以外ならば、エラー「STI_FLG」を関数辺値とし返し終了
「sti=distance(ps,pd,c);if(sti!=END_STI){
return(sti);}」は、下請け関数「
sti=distance(ps,pd,c);」で処理しステータスを取り出し、
正常終了以外ならば、そのエラーコードを関数辺値とし返し
終了!
「ClearRoundImage(pd,0,1);」は、外周一回り0クリア
「return(END_STI);」は、正常終了を関数辺値とし返し
終了!
(2)ソースコード解説distance()
code:distance(仮引数){}
/************************************************************************/
/***** 距離画像作成 :実行部:基本部*****/
/***** DISTANCE,s,d,c *****/
/************************************************************************/
int Filter::distance(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
TypeArray s; // S画像情報:局所
TypeArray d; // D画像情報:局所
int sti; // ステータス情報
if( ps->w != 1 || pd->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
sti = Copy( ps, pd ); // S→T
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
s = *ps; // 一旦、S画像情報
ps = &s; // を局所化
d = *pd; // 一旦、D画像情報
pd = &d; // を局所化
PreProcessor3By3( ps, pd ); // 前処理
if( c == 4 ){ // 4連結なら
distance_4( pd ); // 左記で処理
}else{ // 8連結なら
distance_8( pd ); // 左記で処理
} //
return( END_STI ); // 正常終了
}
(2-1)関数名
「distance」は、距離を意味する!
(2-2)仮引数
code:仮引数
int Filter::distance(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
「TypeArray* ps,」は、元画像(S≒ソース画像)
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
「int c,」は、連結性を示す選択コードで「4」なら4連結
「8」なら8連結です!
(2-2)ローカル変数
code:ローカル変数
){
TypeArray s; // S画像情報:局所
TypeArray d; // D画像情報:局所
int sti; // ステータス情報
「TypeArray s;」は、元画像情報のローカル変数です!
「TypeArray d;」は、結果画像情報のローカル変数です!
「int sti;」は、関数のステータス情報(エラーコード)で
す!
(2-3)アルゴリズム
code:アルゴリズム
if( ps->w != 1 || pd->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
sti = Copy( ps, pd ); // S→T
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
s = *ps; // 一旦、S画像情報
ps = &s; // を局所化
d = *pd; // 一旦、D画像情報
pd = &d; // を局所化
PreProcessor3By3( ps, pd ); // 前処理
if( c == 4 ){ // 4連結なら
distance_4( pd ); // 左記で処理
}else{ // 8連結なら
distance_8( pd ); // 左記で処理
} //
return( END_STI ); // 正常終了
}
「if(ps->w!=1||pd->w!=1){return(STI_ARY_5);}」は、
元画像(S≒ソース)も結果画像(D≒ディスティネーショ
ン)も画素単位が1バイト以外ならエラー「STI_ARY_5」を
関数辺値とし返し終了!
「sti=Copy(ps,pd);」は、結果画像に一旦コピー
「if(sti!=END_STI){return(sti);}」は、コピー関数で
エラー発生時は、エラーをステータス情報とし関数辺値と
し返し終了!
「s=*ps;ps=&s;d=*pd;pd=&d;」は、元画像結果画像情報の
ポインタ変数付替え
「PreProcessor3By3(ps,pd);」は、元画像結果画像両者の
有効な水平幅・垂直を算出し、3×3画像変数に相応しい
一回り内側に画像情報をセット!
「if(c==4){distance_4(pd);}else{distance_8(pd);}」は、
4連結なら下請け関数「distance_4(pd);」で処理し、
8連結なら下請け関数「distance_8(pd);」で処理
「return(END_STI);」は、正常終了を関数辺値とし返し
終了
(3)ソースコード解説distance_4()
code:distance_4(仮引数){}
/************************************************************************/
/***** 距離変換コマンド:ソフト実行部:4連結用 *****/
/***** DISTANCE,c,S,D *****/
/************************************************************************/
void Filter::distance_4(
TypeArray *pd // D画像情報
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_4_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_4_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
(3-1)関数名
「distance」は、距離を意味する!
「_4」は、4連結の処理を意味!
(3-2)仮引数
code:仮引数
void Filter::distance_4(
TypeArray *pd // D画像情報
){
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
(3-2)ローカル変数
code:ローカル変数
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
「BYTE* ptrd;」は、結果画像画素をアクセスするポインタ
変数!
「int h;」は、水平幅
「int v;」は、垂直幅
「int incd;」は、垂直方向増加幅
「int y;」は、垂直(Y座標)方向繰り返しカウンタ
(3-3)アルゴリズム
code:アルゴリズム終了!
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_4_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_4_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
「h=pd->h;v=pd->v;」は、水平幅・垂直幅を取り出し!
「incd=pd->inc;」は、垂直方向増加幅を取り出し!
「ptrd=(BYTE*)pd->adr;」は、画素アクセスポインタを
画像始点(左上)にセット
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「distance_4_x1(ptrd,incd,h);」で下請け
関数「distance_4_x1(・・・)」で処理
「ptrd+=incd;」で垂直方向下方にポインタを進める!
※ここまでで始点から下方に処理して行きます!
「ptrd=(BYTE*)pd->adr+(h-1)+(v-1)*incd;」は、
画素アクセスポインタを画像終点(右下)にセット!
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「distance_4_x2(ptrd,incd,h);」で下請け
関数「distance_4_x2(・・・)」で処理
「ptrd-=incd;」で垂直方向上方にポインタを進める!
※ここまでで終点(右下)から上方に処理して行きます!
★「distance_4_x1(・・・)」で左上から右下への処理、
「distance_4_x2(・・・)」で右下から左上への処理と処理
する向きが異なる事に注意して下さい!
(4)ソースコード解説distance_4_x1()
code:distance_4_x1(仮引数){}
/************************************************************************/
/***** 距離変換コマンド:4連結用:上から下に処理:1行分 *****/
/***** 1行分の処理(左から右方向に処理を行う) *****/
/************************************************************************/
void Filter::distance_4_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
int d1; // 近接データ1
int d2; // 近接データ2
int d255; // 定数値255
d255 = 255; // 定数値255
while( --h >= 0 ){ // 水平方向に繰返し
if( *p ){ // 注視点にData有り
d1 = *( p - inc ); // 上の値と
d2 = *( p - 1 ); // 左の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // 結果を出力
} //
p++; // 右方向へ進める
} //
}
(4-1)関数名
「distance」は、距離を意味する!
「_4」は、4連結の処理を意味!
「x1」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(4-2)仮引数
code:仮引数
void Filter::distance_4_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE* p,」は、画像画素へのポインタ
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(4-2)ローカル変数
code:ローカル変数
){
int d1; // 近接データ1
int d2; // 近接データ2
int d255; // 定数値255
「int d1;」は、近接画素データ1
「int d2;」は、近接画素データ2
「int d255;」は、定数値「255」※高速化技法
(4-3)アルゴリズム
code:アルゴリズム
d255 = 255; // 定数値255
while( --h >= 0 ){ // 水平方向に繰返し
if( *p ){ // 注視点にData有り
d1 = *( p - inc ); // 上の値と
d2 = *( p - 1 ); // 左の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // 結果を出力
} //
p++; // 右方向へ進める
} //
}
「d255=255;」は、定数値「255」セット※高速化技法
「while(--h>=0){・・ループ中身・・}」は、水平幅分
繰り返しループ中身を処理、そのループ中身は、
「if(p){・・分岐中身・・」で条件「p」で有効画素(
≠0)の場合は、分岐中身の
「d1=(p-inc);d2=(p-1);」で上側と左側画素データを
取り出す!
「if(d1>d2){d1=d2;}」で条件「上側値>左側値」の場合
は、近接データ1(元は上側値)を左側値の値をセット、
つまり、上側・左側の最小値セット
「d1+=1;」で近接データ1を一つ増加、
「if(d1>d255){d1=d255;}」で上限※画像画素の型名「
BYTE≒unsigned char」と符号無し1バイトで
「0・・255」を採用≪更に実用的にこれで十分と判断≫し
たので値を制限
「*p=d1;」で注視点画素の値を更新し、分岐中身ブロック
終了、
「p++;」でループ中身最後に画素を示すポインタを右方向
に進める
(5)ソースコード解説distance_4_x2()
code:distance_4_x2(仮引数){}
/************************************************************************/
/***** 距離変換コマンド:4連結用:下から上に処理:1行分 *****/
/***** 1行分の処理(右から左方向に処理を行う) *****/
/************************************************************************/
void Filter::distance_4_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
int d0; // 注視点データ
int d1; // 近接データ1
int d2; // 近接データ2
int d255; // 定数値255
d255 = 255; // 定数値255
while( --h >= 0 ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc ); // 下の値と
d2 = *( p + 1 ); // 右の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d0 ){ // 注視点と比較し
d1 = d0; // その最小を算出
} //
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // 結果を出力
} //
p--; // 左方向へ進める
} //
}
(5-1)関数名
「distance」は、距離を意味する!
「_4」は、4連結の処理を意味!
「x2」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(5-2)仮引数
code:仮引数
void Filter::distance_4_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE* p,」は、画像画素へのポインタ
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(5-2)ローカル変数
code:ローカル変数
){
int d0; // 注視点データ
int d1; // 近接データ1
int d2; // 近接データ2
int d255; // 定数値255
「int d0;」は、注視点画素データ
「int d1;」は、近接画素データ1
「int d2;」は、近接画素データ2
「int d255;」は、定数値「255」※高速化技法
(5-3)アルゴリズム
code:アルゴリズム
d255 = 255; // 定数値255
while( --h >= 0 ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc ); // 下の値と
d2 = *( p + 1 ); // 右の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d0 ){ // 注視点と比較し
d1 = d0; // その最小を算出
} //
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // 結果を出力
} //
p--; // 左方向へ進める
} //
}
「d255=255;」は、定数値「255」セット※高速化技法
「while(--h>=0){・・ループ中身・・}」は、水平幅分
繰り返しループ中身を処理、そのループ中身は、
「d0=p;」で注視点画素データとしセット※何回も比較用
に使用するので高速化技法
「if(d0){・・分岐中身・・」で条件「d0」で有効画素(
≠0)の場合は、分岐中身の
「d1=(p+inc);d2=*(p+1);」で右側と下側画素データを
取り出す!
「if(d1>d2){d1=d2;}」で条件「右側値>下側値」の場合
は、近接データ1(元は右側値)を下側値の値をセット、
つまり、右側・下側の最小値セット
「d1+=1;」で近接データ1を一つ増加、
「if(d1>d0){d1=d0;}」で条件「近接データ1>注視点」の
場合は、近接データ1を注視点の値をセット、つまり、
依り近接データ1・注視点の最小値セット
「if(d1>d255){d1=d255;}」で上限※画像画素の型名「
BYTE≒unsigned char」と符号無し1バイトで
「0・・255」を採用≪更に実用的にこれで十分と判断≫し
たので値を制限
「*p=d1;」で注視点画素の値を更新し、分岐中身ブロック
終了、
「p--;」でループ中身最後に画素を示すポインタを左方向
に進める
(6)ソースコード解説distance_8()
code:distance_8(仮引数){}
/************************************************************************/
/***** 距離変換コマンド:ソフト実行部:8連結用 *****/
/***** DISTANCE,c,S,D *****/
/************************************************************************/
void Filter::distance_8(
TypeArray *pd // D画像情報
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_8_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_8_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
(6-1)関数名
「distance」は、距離を意味する!
「_8」は、8連結の処理を意味!
(6-2)仮引数
code:仮引数
void Filter::distance_8(
TypeArray *pd // D画像情報
){
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
(6-2)ローカル変数
code:ローカル変数
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
「BYTE* ptrd;」は、結果画像画素をアクセスするポインタ
変数!
「int h;」は、水平幅
「int v;」は、垂直幅
「int incd;」は、垂直方向増加幅
「int y;」は、垂直(Y座標)方向繰り返しカウンタ
(6-3)アルゴリズム
code:アルゴリズム
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_8_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
distance_8_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
「h=pd->h;v=pd->v;」は、水平幅・垂直幅を取り出し!
「incd=pd->inc;」は、垂直方向増加幅を取り出し!
「ptrd=(BYTE*)pd->adr;」は、画素アクセスポインタを
画像始点(左上)にセット
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「distance_8_x1(ptrd,incd,h);」で下請け
関数「distance_8_x1(・・・)」で処理
「ptrd+=incd;」で垂直方向下方にポインタを進める!
※ここまでで始点から下方に処理して行きます!
「ptrd=(BYTE*)pd->adr+(h-1)+(v-1)*incd;」は、
画素アクセスポインタを画像終点(右下)にセット!
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「distance_8_x2(ptrd,incd,h);」で下請け
関数「distance_8_x2(・・・)」で処理
「ptrd-=incd;」で垂直方向上方にポインタを進める!
※ここまでで終点(右下)から上方に処理して行きます!
★「distance_8_x1(・・・)」で左上から右下への処理、
「distance_8_x2(・・・)」で右下から左上への処理と処理
する向きが異なる事に注意して下さい!
(7)ソースコード解説distance_8_x1()
code:distance_8_x1(仮引数){}
/************************************************************************/
/***** 距離変換コマンド:8連結用:上から下方向:1行分 *****/
/***** 1行分の処理(左から右方向に処理を行う) *****/
/************************************************************************/
void Filter::distance_8_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
BYTE *pend; // 終点Ptr:右側
int d1; // 近接データ1
int d2; // 近接データ2
int d3; // 近接データ3
int d4; // 近接データ4
int d255; // 定数値255
d255 = 255; // 定数値255
for( pend = p + h; p < pend; p++ ){ // 水平方向に繰返し
if( *p ){ // 注視点にData有り
d1 = *( p - inc - 1 ); // 左上の値と
d2 = *( p - inc ); // 上の値と
d3 = *( p - inc + 1 ); // 右上の値と
d4 = *( p - 1 ); // 左の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
if( d1 > d3 ){ //
d1 = d3; //
} //
if( d1 > d4 ){ //
d1 = d4; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // データ出力
} //
} //
}
(7-1)関数名
「distance」は、距離を意味する!
「_8」は、8連結の処理を意味!
「x1」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(7-2)仮引数
code:仮引数
void Filter::distance_8_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE* p,」は、画像画素へのポインタ
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(7-2)ローカル変数
code:ローカル変数
){
BYTE *pend; // 終点Ptr:右側
int d1; // 近接データ1
int d2; // 近接データ2
int d3; // 近接データ3
int d4; // 近接データ4
int d255; // 定数値255
「BYTE*pend;」は、水平方向画素ポインタ終点※目印で
何故ループ条件に使用したかは、元々ADS社画像処理装置
のCPUが68000系でコンパイラ「MCC68K」を
使用して居たためにループ条件としデータレジスターを使い
切り、アドレスレジスターに変数を割り当てる高速化技法と
して「BYTE*pend;」を終点目印に使用した名残です!
(7-3)アルゴリズム
code:アルゴリズム
d255 = 255; // 定数値255
for( pend = p + h; p < pend; p++ ){ // 水平方向に繰返し
if( *p ){ // 注視点にData有り
d1 = *( p - inc - 1 ); // 左上の値と
d2 = *( p - inc ); // 上の値と
d3 = *( p - inc + 1 ); // 右上の値と
d4 = *( p - 1 ); // 左の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
if( d1 > d3 ){ //
d1 = d3; //
} //
if( d1 > d4 ){ //
d1 = d4; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // データ出力
} //
} //
}
「d255=255;」は、定数値「255」セット※高速化技法
「for(pend=p+h;p<pend;p++){・・ループ中身・・}」は、
水平幅分繰り返し「pend=p+h;」で終点目印を作成し
「p<pend;」で終点目印まで繰り返し「p++」で処理ポインタを左から右へ移動のforループでループ中身を処理、
そのループ中身は、
「if(p){・・分岐中身・・」で条件「p」で有効画素(
≠0)の場合は、分岐中身の
「d1=(p-inc-1);d2=(p-inc);d3=(p-inc+1);d4=(p-1);
」で左上側と上側画素と右上側と左側画素データを取り出
す!
「if(d1>d2){d1=d2;}if(d1>d3){d1=d3;}if(d1>d4){
d1=d4;}」で左上側・上側・右上側・左側との最小値セット
「d1+=1;」で近接データ1を一つ増加、
「if(d1>d255){d1=d255;}」で上限※画像画素の型名「
BYTE≒unsigned char」と符号無し1バイトで
「0・・255」を採用≪更に実用的にこれで十分と判断≫し
たので値を制限
「*p=d1;」で注視点画素の値を更新し、分岐中身ブロック
終了、
「p++;」でループ中身最後に画素を示すポインタを右方向
に進める
(8)ソースコード解説distance_8_x2()
code:distance_8_x2(仮引数){}
/************************************************************************/
/***** 距離変換コマンド:8連結用:下から上方向:1行分 *****/
/***** 1行分の処理(右から左方向に処理を行う) *****/
/************************************************************************/
void Filter::distance_8_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
BYTE *pend; // 終点Ptr:左側
int d0; // 注視点データ
int d1; // 近接データ1
int d2; // 近接データ2
int d3; // 近接データ3
int d4; // 近接データ4
int d255; // 定数値255
d255 = 255; // 定数値255
for( pend = p - h; p > pend; p-- ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc - 1 ); // 左下の値と
d2 = *( p + inc ); // 下の値と
d3 = *( p + inc + 1 ); // 右下の値と
d4 = *( p + 1 ); // 右の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
if( d1 > d3 ){ //
d1 = d3; //
} //
if( d1 > d4 ){ //
d1 = d4; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d0 ){ // 注視点と比較し
d1 = d0; // その最小を算出
} //
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // 結果を出力
} //
} //
}
(8-1)関数名
「distance」は、距離を意味する!
「_8」は、8連結の処理を意味!
「x2」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(8-2)仮引数
code:仮引数
void Filter::distance_8_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE* p,」は、画像画素へのポインタ
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(8-2)ローカル変数
code:ローカル変数
){
BYTE *pend; // 終点Ptr:左側
int d0; // 注視点データ
int d1; // 近接データ1
int d2; // 近接データ2
int d3; // 近接データ3
int d4; // 近接データ4
int d255; // 定数値255
「BYTE *pend;」は、水平方向画素ポインタ終点※目印で
何故ループ条件に使用したかは、元々ADS社画像処理装置
のCPUが68000系でコンパイラ「MCC68K」を
使用して居たためにループ条件としデータレジスターを使い
切り、アドレスレジスターに変数を割り当てる高速化技法と
して「BYTE*pend;」を終点目印に使用した名残です!
「int d0;」は、注視点画素データ
「int d1;」は、近接画素データ1
「int d2;」は、近接画素データ2
「int d3;」は、近接画素データ3
「int d4;」は、近接画素データ4
「int d255;」は、定数値「255」※高速化技法
(8-3)アルゴリズム
code:アルゴリズム
d255 = 255; // 定数値255
for( pend = p - h; p > pend; p-- ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc - 1 ); // 左下の値と
d2 = *( p + inc ); // 下の値と
d3 = *( p + inc + 1 ); // 右下の値と
d4 = *( p + 1 ); // 右の値を取り出し
if( d1 > d2 ){ // 最小の値を算出し
d1 = d2; //
} //
if( d1 > d3 ){ //
d1 = d3; //
} //
if( d1 > d4 ){ //
d1 = d4; //
} //
d1 += 1; // 最小+1を算出
if( d1 > d0 ){ // 注視点と比較し
d1 = d0; // その最小を算出
} //
if( d1 > d255 ){ // 255超なら
d1 = d255; // 255に補正
} //
*p = d1; // 結果を出力
} //
} //
}
「d255=255;」は、定数値「255」セット※高速化技法
「for(pend=p-h;p>pend;p--){・・ループ中身・・}」は、
水平幅分繰り返し「pend=p+h;」で終点目印を作成し
「p>pend;」で終点目印まで繰り返し「p--」で処理ポインタ
を右から左へ移動のforループでループ中身を処理、そのループ中身は、
「d0=p;」で注視点画素データとしセット※何回も比較用
に使用するので高速化技法
「if(d0){・・分岐中身・・」で条件「d0」で有効画素(
≠0)の場合は、分岐中身の
「d1=(p-inc-1);d2=(p-inc);」で左上側と上側画素
データを取り出す!
「d3=(p-inc+1);d4=*(p-1);」で右上側と左側画素
データを取り出す!
「if(d1>d2){d1=d2;}」で条件「左上側>上側値」の場合
は、近接データ1を上側値の値でセット、
「if(d1>d3){d1=d3;}」で条件「近接データ1>右上側値」
の場合は、近接データ1に右上値の値でセット、
「if(d1>d4){d1=d4;}」で条件「近接データ1>左側値」
の場合は、近接データ1に左側値の値でセット、
つまり、左上側・上側・右上側・左側との最小値セット
「d1+=1;」で近接データ1を一つ増加、
「if(d1>d0){d1=d0;}」で条件「近接データ1>注視点」の
場合は、近接データ1を注視点の値をセット、つまり、
依り近接データ1・注視点の最小値セット
「if(d1>d255){d1=d255;}」で上限※画像画素の型名「
BYTE≒unsigned char」と符号無し1バイトで
「0・・255」を採用≪更に実用的にこれで十分と判断≫し
たので値を制限
「*p=d1;」で注視点画素の値を更新し、分岐中身ブロック
終了
2.骨格化
(1)ソースコード解説SkeletonImage()
code:SkeletonImage(仮引数){}
/************************************************************************/
/***** 骨格化 :実行部*****/
/***** SKELETON,s,d,c *****/
/************************************************************************/
int Filter::SkeletonImage(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
TypeArray t; // T画像情報:局所
void *p; // 画像ポインタ
int sti; // ステータス情報
if( c == 5 || c == 9 ){ // 2値→骨格化時
p = malloc( pd->h * pd->v ); // メモリを確保
if( p == 0 ){ // 確保失敗なら
return( STI_MEM ); // 左記を返す
} //
t.SetByte( (int)p, pd->h, pd->v ); // 画像情報作成:BYTE
c -= 1; // 5→4, 9→8
sti = DistanceImage( ps, &t, c ); // 距離変換 S→T
if( sti == END_STI ){ // 上記成功時は
sti = skeleton( &t, pd, c ); // 骨格化 T→D
} //
free( p ); // メモリを解放し
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
}else if( c == 4 || c == 8 ){ // 距離→骨格の時
sti = skeleton( ps, pd, c ); // 骨格化 T→D
}else{ // 距離→骨格の時
return( STI_FLG ); // 左記を返す
} //
ClearRoundImage( pd, 0, 1 ); // 1周周囲クリア
return( sti ); // ステータスを返す
}
(1-1)関数名
「Skeleton」は、骨格を意味する!ここでは、骨格化
「Image」は、画像で画像の中で骨格化変換を行います!
(1-2)仮引数
code:仮引数
int Filter::SkeletonImage(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
「TypeArray* ps,」は、元画像(S≒ソース画像)
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
※備考:同じ関数名でオーバーロード関数(画像がimgと
一つだけ)が有ります!
「int c,」は、連結性を示す選択コードで「4」なら4連結
「8」なら8連結です!
更に「5」の場合、一旦、2値化画像を4連結距離変換を
行った後で4連結で骨格化変換を行います!
「9」の場合、一旦、2値化画像を8連結距離変換を行った
後で8連結で骨格化変換を行います!
(1-3)ローカル変数
code:ローカル変数
){
TypeArray t; // T画像情報:局所
void *p; // 画像ポインタ
int sti; // ステータス情報
「TypeArray t;」は、連結性「c」が「5,9」指定で途中で
距離変換画像を作成する時の局所画像Tの画像情報です!
「void* p;」は、「void型ポインタ」と言う汎用ポインタ
を使用したのは、C言語からC++言語への途中切り替え
のクラスライブラリ編集中の課程でクラス「TypeArray」
でメモリ管理を確定して無かった時の名残です!
「int sti;」は、関数のステータス情報(エラーコード)で
す!
(1-4)アルゴリズム
code:アルゴリズムに進める
if( c == 5 || c == 9 ){ // 2値→骨格化時
p = malloc( pd->h * pd->v ); // メモリを確保
if( p == 0 ){ // 確保失敗なら
return( STI_MEM ); // 左記を返す
} //
t.SetByte( (int)p, pd->h, pd->v ); // 画像情報作成:BYTE
c -= 1; // 5→4, 9→8
sti = DistanceImage( ps, &t, c ); // 距離変換 S→T
if( sti == END_STI ){ // 上記成功時は
sti = skeleton( &t, pd, c ); // 骨格化 T→D
} //
free( p ); // メモリを解放し
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
}else if( c == 4 || c == 8 ){ // 距離→骨格の時
sti = skeleton( ps, pd, c ); // 骨格化 T→D
}else{ // 距離→骨格の時
return( STI_FLG ); // 左記を返す
} //
ClearRoundImage( pd, 0, 1 ); // 1周周囲クリア
return( sti ); // ステータスを返す
}
「if(c==5||c==9){・・成立分岐中身・・」は、条件「
c==5||c==9」で連結性指定が「5,9」の場合の処理で成立
分岐中身は、
「p=malloc(pd->hpd->v);if(p==0){return(STI_MEM);}」
で「malloc(pd->hpd->v);」とメモリ確保し、確保失敗し
たら、「return(STI_MEM);」でエラーコードを関数辺値と
し返し終了!
「t.SetByte((int)p,pd->h,pd->v);」は、画像情報Tに
画像画素ポインタや水平幅・垂直幅のサイズ等セット
「c-=1;」は、連結性指定が「5,9」を「4,8」に変換!
「sti=DistanceImage(ps,&t,c);」は、距離変換を行い、
「if(sti==END_STI){sti=skeleton(&t,pd,c);}」は、
距離変換が正常終了したら、下請け関数「
sti=skeleton(&t,pd,c);」で骨格化処理し、
「free(p);」は、メモリ確保「p=malloc(・・・)」の
後始末!
「if(sti!=END_STI){return(sti);}」は、エラー発生時
は、エラーコードを関数辺値とし返し終了!
「}else if(c==4||c==8){sti=skeleton(ps,pd,c);}」は、
連結性指定が「4,8」の場合の処理で下請け関数「
sti=skeleton(&t,pd,c);」で骨格化処理!
「else{return(STI_FLG);}」は連結性指定が「4,5,8,9」
以外なら、「STI_FLG」をエラーコード関数辺値とし返し
終了!
「ClearRoundImage(pd,0,1);return(sti);」は、
外周一回り0クリアし、ステータスを関数辺値とし返し
終了!
(2)ソースコード解説SkeletonImage()
code:SkeletonImage(仮引数){}
/************************************************************************/
/***** 骨格化:単一画像変換版 :実行部*****/
/***** SKELETON,img,c *****/
/************************************************************************/
int Filter::SkeletonImage(
TypeArray *img, // 画像情報
int c // 連結性
){
TypeArray w; // 作業画像:一回り内側
if( img == 0 ){ // ポインタ不正
return( STI_ARY_0 ); // 左記を返す
}else if( img->adr == 0 ){ // アドレス不正
return( STI_ARY_1 ); // 左記を返す
}else if( img->h < 3 ){ // 水平幅不正
return( STI_ARY_2 ); // 左記を返す
}else if( img->v < 3 ){ // 垂直幅不正
return( STI_ARY_3 ); // 左記を返す
}else if( img->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
w.subset( img, 1, 1, img->h - 2, img->v - 2); // 作業画像:一回り内側
if( c == 4 ){ // 4連結なら
skeleton_4( &w ); // 左記で処理
}else if( c == 8 ){ // 8連結なら
skeleton_8( &w ); // 左記で処理
}else{ // 連結性間違い時
return( STI_FLG ); // 左記を返す
} //
ClearRoundImage( img, 0, 1 ); // 1周周囲クリア
return( END_STI ); // 正常終了
}
(2-1)関数名
「Skeleton」は、骨格を意味する!ここでは、骨格化
「Image」は、画像で画像の中で骨格化変換を行います!
(2-2)仮引数
code:仮引数
int Filter::SkeletonImage(
TypeArray *img, // 画像情報
int c // 連結性
){
「TypeArray* img,」は、画像情報へのポインタ
※備考:同じ関数名でオーバーロード関数(画像がSDと
二つ)が有ります!
「int c,」は、連結性を示す選択コードで「4」なら4連結
「8」なら8連結です!
(2-3)ローカル変数
code:ローカル変数
){
TypeArray w; // 作業画像:一回り内側
「TypeArray w;」は、作業用の部分画像
(2-4)アルゴリズム
code:アルゴリズム
if( img == 0 ){ // ポインタ不正
return( STI_ARY_0 ); // 左記を返す
}else if( img->adr == 0 ){ // アドレス不正
return( STI_ARY_1 ); // 左記を返す
}else if( img->h < 3 ){ // 水平幅不正
return( STI_ARY_2 ); // 左記を返す
}else if( img->v < 3 ){ // 垂直幅不正
return( STI_ARY_3 ); // 左記を返す
}else if( img->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
w.subset( img, 1, 1, img->h - 2, img->v - 2); // 作業画像:一回り内側
if( c == 4 ){ // 4連結なら
skeleton_4( &w ); // 左記で処理
}else if( c == 8 ){ // 8連結なら
skeleton_8( &w ); // 左記で処理
}else{ // 連結性間違い時
return( STI_FLG ); // 左記を返す
} //
ClearRoundImage( img, 0, 1 ); // 1周周囲クリア
return( END_STI ); // 正常終了
}
「if(img==0){return(STI_ARY_0);}else
if(img->adr==0){return(STI_ARY_1);}else
if(img->h<3){return(STI_ARY_2);}else
if(img->v<3){return(STI_ARY_3);}else
if(img->w!=1){return(STI_ARY_5);}」は、骨格化可能な
画像か否かの検査で画像画素が存在・3×3以上の
水平幅・垂直幅か如何かで画素単位が1バイト整数か否か
を判定しエラーが有れば、状態に応じたエラーコードを
関数辺値とし返し終了!
「w.subset(img,1,1,img->h-2,img->v-2);」は、作業用の
部分画像を作成!
「if(c==4){skeleton_4(&w);}else
if(c==8){skeleton_8(&w);}」は、連結性を示す選択
コードで「4」なら下請け関数「skeleton_4(&w);」で処理
「8」なら下請け関数「skeleton_8(&w);」で処理
「else{return(STI_FLG);}」は、連結性を示す選択コード
が「4,8」で無ければ、「STI_FLG」をエラーコードとし
関数辺値とし返し終了
「ClearRoundImage(pd,0,1);return(sti);」は、
外周一回り0クリアし、ステータスを関数辺値とし返し
終了!
(3)ソースコード解説skeleton()
code:skeleton(仮引数){}
/************************************************************************/
/***** 骨格化 :基本部(距離画像→骨格化)*****/
/***** SKELETON,s,d,c *****/
/************************************************************************/
int Filter::skeleton(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
TypeArray s; // S画像情報:局所
TypeArray d; // D画像情報:局所
int sti; // ステータス情報
if( c != 4 && c != 8 ){ // 連結性間違い時
return( STI_FLG ); // 左記を返す
} //
sti = Copy( ps, pd ); // S→T
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
s = *ps; // 一旦、S画像情報
ps = &s; // を局所化
d = *pd; // 一旦、D画像情報
pd = &d; // を局所化
PreProcessor3By3( ps, pd ); // 前処理
if( c == 4 ){ // 4連結なら
skeleton_4( pd ); // 左記で処理
}else{ // 8連結なら
skeleton_8( pd ); // 左記で処理
} //
return( END_STI ); // 正常終了
}
(3-1)関数名
「skeleton」は、骨格を意味する!ここでは、骨格化
(3-2)仮引数
code:仮引数
int Filter::skeleton(
TypeArray *ps, // S画像情報
TypeArray *pd, // D画像情報
int c // 連結性
){
「TypeArray* ps,」は、元画像(S≒ソース画像)
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
「int c,」は、連結性を示す選択コードで「4」なら4連結
「8」なら8連結です!
(3-2)ローカル変数
code:ローカル変数
){
TypeArray s; // S画像情報:局所
TypeArray d; // D画像情報:局所
int sti; // ステータス情報
「TypeArray s;」は、元画像情報のローカル変数です!
「TypeArray d;」は、結果画像情報のローカル変数です!
「int sti;」は、関数のステータス情報(エラーコード)で
す!
(3-3)アルゴリズム
code:アルゴリズム
if( c != 4 && c != 8 ){ // 連結性間違い時
return( STI_FLG ); // 左記を返す
} //
sti = Copy( ps, pd ); // S→T
if( sti != END_STI ){ // エラーがある場合
return( sti ); // ステータスを返す
} //
s = *ps; // 一旦、S画像情報
ps = &s; // を局所化
d = *pd; // 一旦、D画像情報
pd = &d; // を局所化
PreProcessor3By3( ps, pd ); // 前処理
if( c == 4 ){ // 4連結なら
skeleton_4( pd ); // 左記で処理
}else{ // 8連結なら
skeleton_8( pd ); // 左記で処理
} //
return( END_STI ); // 正常終了
}
「if(c!=4&&c!=8){return(STI_FLG);}」は、連結性「c」
が「4,8」以外なら、エラーコード「STI_FLG」を関数
辺値とし返し終了
「sti=Copy(ps,pd);」は、一旦、結果画像に元画像を
コピー
「if(sti!=END_STI){return(sti);}」は、エラー発生時は
エラーコードを関数辺値とし返し終了!
「s=*ps;ps=&s;d=*pd;pd=&d;」は、元画像結果画像情報の
ポインタ変数付替え
「PreProcessor3By3(ps,pd);」は、元画像結果画像両者の
有効な水平幅・垂直を算出し、3×3画像変数に相応しい
一回り内側に画像情報をセット!
「if(c==4){skeleton_4(&w);}else
if(c==8){skeleton_8(&w);}」は、連結性を示す選択
コードで「4」なら下請け関数「skeleton_4(&w);」で処理
「8」なら下請け関数「skeleton_8(&w);」で処理
「return(END_STI);」は、で正常終了!
※備考:一旦、「sti=Copy(ps,pd);」で引数のエラー検査
が終わっている事に注意
(4)ソースコード解説skeleton_4()
code:skeleton_4(仮引数){}
/************************************************************************/
/***** 骨格化コマンド:ソフト実行部:4連結用 *****/
/***** SKELETON,c,S[,D] *****/
/************************************************************************/
void Filter::skeleton_4(
TypeArray *pd // D画像情報
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_4_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_4_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
(4-1)関数名
「skeleton」は、距離を意味する!
「_4」は、4連結の処理を意味!
(4-2)仮引数
code:仮引数
void Filter::skeleton_4(
TypeArray *pd // D画像情報
){
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
(4-2)ローカル変数
code:ローカル変数
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
「BYTE* ptrd;」は、結果画像画素をアクセスするポインタ
変数!
「int h;」は、水平幅
「int v;」は、垂直幅
「int incd;」は、垂直方向増加幅
「int y;」は、垂直(Y座標)方向繰り返しカウンタ
(4-3)アルゴリズム
code:アルゴリズム
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_4_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_4_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
「h=pd->h;v=pd->v;」は、水平幅・垂直幅を取り出し!
「incd=pd->inc;」は、垂直方向増加幅を取り出し!
「ptrd=(BYTE*)pd->adr;」は、画素アクセスポインタを
画像始点(左上)にセット
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「skeleton_4_x1(ptrd,incd,h);」で下請け
関数「skeleton_4_x1(・・・)」で処理
「ptrd+=incd;」で垂直方向下方にポインタを進める!
※ここまでで始点から下方に処理して行きます!
「ptrd=(BYTE*)pd->adr+(h-1)+(v-1)*incd;」は、
画素アクセスポインタを画像終点(右下)にセット!
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「skeleton_4_x2(ptrd,incd,h);」で下請け
関数「skeleton_4_x2(・・・)」で処理
「ptrd-=incd;」で垂直方向上方にポインタを進める!
※ここまでで終点(右下)から上方に処理して行きます!
★「skeleton_4_x1(・・・)」で左上から右下への処理、
「skeleton_4_x2(・・・)」で右下から左上への処理と処理
する向きが異なる事に注意して下さい!
(5)ソースコード解説skeleton_4_x1()
code:skeleton_4_x1(仮引数){}
/************************************************************************/
/***** 骨格化コマンド:4連結用:上から下に処理:1行分 *****/
/***** 1行分の処理(左から右方向に処理を行う) *****/
/************************************************************************/
void Filter::skeleton_4_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
while( --h >= 0 ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc ); // 下の値と
d2 = *( p + 1 ); // 右の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
p++; // 右方向へ進める
} //
}
(5-1)関数名
「skeleton」は、距離を意味する!
「_4」は、4連結の処理を意味!
「x1」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(5-2)仮引数
code:仮引数
void Filter::skeleton_4_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE* p,」は、画像画素へのポインタ
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(5-2)ローカル変数
code:ローカル変数
){
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
「int d0;」は、注視点画素データ
「int d1;」は、近接画素データ1
「int d2;」は、近接画素データ2
(5-3)アルゴリズム
code:アルゴリズム
while( --h >= 0 ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc ); // 下の値と
d2 = *( p + 1 ); // 右の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
p++; // 右方向へ進める
} //
}
「while(--h>=0){・・ループ中身・・}」は、水平幅分
繰り返しループ中身を処理、そのループ中身は、
「d0=p;」で注視点データを取り出し!
「if(d0){・・分岐成立・・}」で条件「d0」で注視点が
有効の場合に分岐成立ブロック実行するで分岐成立の
「d1=(p+inc);d2=*(p+1);」で右側と下側画素データを
取り出す!
「if(d1<d2){d1=d2;}」で右側の下側大きい方をデータ1に
「if(d0<d1){*p=0;}」で注視点の方が大きい場合は、
注視点を無効化(0をセット)、ここまでで分岐成立ブロッ
ク終了!
「p++;」で注視点ポインタを右方向に進める!
(6)ソースコード解説skeleton_4_x2()
code:skeleton_4_x2(仮引数){}
/************************************************************************/
/***** 骨格化コマンド:4連結用:下から上に処理:1行分 *****/
/***** 1行分の処理(右から左方向に処理を行う) *****/
/************************************************************************/
void Filter::skeleton_4_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
while( --h >= 0 ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p - inc ); // 上の値と
d2 = *( p - 1 ); // 左の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
p--; // 左方向へ進める
} //
}
(6-1)関数名
「skeleton」は、距離を意味する!
「_4」は、4連結の処理を意味!
「x2」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(6-2)仮引数
code:仮引数
void Filter::skeleton_4_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE* p,」は、画像画素へのポインタ
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(6-2)ローカル変数
code:ローカル変数
){
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
「int d0;」は、注視点画素データ
「int d1;」は、近接画素データ1
「int d2;」は、近接画素データ2
(6-3)アルゴリズム
code:アルゴリズム
while( --h >= 0 ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p - inc ); // 上の値と
d2 = *( p - 1 ); // 左の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
p--; // 左方向へ進める
} //
}
「while(--h>=0){・・ループ中身・・}」は、水平幅分
繰り返しループ中身を処理、そのループ中身は、
「d0=p;」で注視点データを取り出し!
「if(d0){・・分岐成立・・}」で条件「d0」で注視点が
有効の場合に分岐成立ブロック実行するで分岐成立の
「d1=(p-inc);d2=*(p-1);」で上側と側画素データを
取り出す!
「if(d1<d2){d1=d2;}」で大きい方をデータ1に
「if(d0<d1){*p=0;}」で注視点の方が大きい場合は、
注視点を無効化(0をセット)、ここまでで分岐成立ブロッ
ク終了!
「p--;」で注視点ポインタを左方向に進める!
(7)ソースコード解説skeleton_8()
code:skeleton_8(仮引数){}
/************************************************************************/
/***** 骨格化コマンド:ソフト実行部:8連結用 *****/
/***** SKELETON,c,S[,D] *****/
/************************************************************************/
void Filter::skeleton_8(
TypeArray *pd // D画像情報
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_8_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_8_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
(7-1)関数名
「skeleton」は、距離を意味する!
「_8」は、8連結の処理を意味!
(7-2)仮引数
code:仮引数
void Filter::skeleton_8(
TypeArray *pd // D画像情報
){
「TypeArray* pd,」は、結果画像(D≒ディスティネー
ション画像)
(7-2)ローカル変数
code:ローカル変数
){
BYTE *ptrd; // D画像Ptr
int h; // 水平幅
int v; // 垂直幅
int incd; // D画像:増加幅
int y; // y座標方向CNT
「BYTE* ptrd;」は、結果画像画素をアクセスするポインタ
変数!
「int h;」は、水平幅
「int v;」は、垂直幅
「int incd;」は、垂直方向増加幅
「int y;」は、垂直(Y座標)方向繰り返しカウンタ
(7-3)アルゴリズム
code:アルゴリズム
h = pd->h; // 画像のサイズを
v = pd->v; // 取り出し
incd = pd->inc; // D画像増加幅取出
ptrd = (BYTE*)pd->adr; // D画像Ptrを取出す
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_8_x1( ptrd, incd, h ); // 1行分処理
ptrd += incd; // 垂直方向増加
} //
ptrd = (BYTE*)pd->adr // 右下Ptrを算出
+ ( h - 1 ) + ( v - 1 ) * incd; //
for( y = v; --y >= 0; ){ // 垂直方向に繰返
skeleton_8_x2( ptrd, incd, h ); // 1行分処理
ptrd -= incd; // 垂直方向減少
} //
}
「h=pd->h;v=pd->v;」は、水平幅・垂直幅を取り出し!
「incd=pd->inc;」は、垂直方向増加幅を取り出し!
「ptrd=(BYTE*)pd->adr;」は、画素アクセスポインタを
画像始点(左上)にセット
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「skeleton_8_x1(ptrd,incd,h);」で下請け
関数「skeleton_8_x1(・・・)」で処理
「ptrd+=incd;」で垂直方向下方にポインタを進める!
※ここまでで始点から下方に処理して行きます!
「ptrd=(BYTE*)pd->adr+(h-1)+(v-1)*incd;」は、
画素アクセスポインタを画像終点(右下)にセット!
「for(y=v;--y>=0;){・・ループ中身・・}」は、垂直幅分
ループ中身を繰り返す!そのループ中身は、
「skeleton_8_x2(ptrd,incd,h);」で下請け
関数「skeleton_8_x2(・・・)」で処理
「ptrd-=incd;」で垂直方向上方にポインタを進める!
※ここまでで終点(右下)から上方に処理して行きます!
★「skeleton_8_x1(・・・)」で左上から右下への処理、
「skeleton_8_x2(・・・)」で右下から左上への処理と処理
する向きが異なる事に注意して下さい!
(8)ソースコード解説skeleton_8_x1()
code:skeleton_8_x1(仮引数){}
/************************************************************************/
/***** 骨格化コマンド:8連結用:上から下に処理:1行分 *****/
/***** 1行分の処理(左から右方向に処理を行う) *****/
/************************************************************************/
void Filter::skeleton_8_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
BYTE *pend; // 終点Ptr:右側
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
BYTE d3; // 近接データ3
BYTE d4; // 近接データ4
for( pend = p + h; p < pend; p++ ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc + 1 ); // 右下の値と
d2 = *( p + inc ); // 下の値と
d3 = *( p + inc - 1 ); // 左下の値と
d4 = *( p + 1 ); // 右の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d1 < d3 ){ //
d1 = d3; //
} //
if( d1 < d4 ){ //
d1 = d4; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
} //
}
(8-1)関数名
「skeleton」は、距離を意味する!
「_8」は、8連結の処理を意味!
「x1」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(8-2)仮引数
code:仮引数
void Filter::skeleton_8_x1(
BYTE *p, // y軸用Ptr:左側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE *pend;」は、水平方向画素ポインタ終点※目印で
何故ループ条件に使用したかは、元々ADS社画像処理装置
のCPUが68000系でコンパイラ「MCC68K」を
使用して居たためにループ条件としデータレジスターを使い
切り、アドレスレジスターに変数を割り当てる高速化技法と
して「BYTE*pend;」を終点目印に使用した名残です!
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(8-2)ローカル変数
code:ローカル変数
){
BYTE *pend; // 終点Ptr:右側
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
BYTE d3; // 近接データ3
BYTE d4; // 近接データ4
「BYTE *pend;」は、水平方向画素ポインタ終点※目印
「int d0;」は、注視点画素データ
「int d1;」は、近接画素データ1
「int d2;」は、近接画素データ2
「int d3;」は、近接画素データ3
「int d4;」は、近接画素データ4
(8-3)アルゴリズム
code:アルゴリズム
for( pend = p + h; p < pend; p++ ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p + inc + 1 ); // 右下の値と
d2 = *( p + inc ); // 下の値と
d3 = *( p + inc - 1 ); // 左下の値と
d4 = *( p + 1 ); // 右の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d1 < d3 ){ //
d1 = d3; //
} //
if( d1 < d4 ){ //
d1 = d4; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
} //
}
「for(pend=p+h;p<pend;p++){・・ループ中身・・}」は、
水平幅分繰り返し「pend=p+h;」で終点目印を作成し
「p<pend;」で終点目印まで繰り返し「p++」で処理ポインタを左から右へ移動のforループでループ中身を処理、
そのループ中身は、
「d0=p;」で注視点画素データとしセット※何回も比較用
に使用するので高速化技法
「if(d0){・・分岐中身・・」で条件「d0」で有効画素(
≠0)の場合は、分岐中身の
「d1=(p+inc+1);d2=(p+inc);d3=(p+inc-1);d4=*(p+1);
」で右下・下・左下・左隣のデータを取り出し!
「if(d1<d2){d1=d2;}if(d1<d3){d1=d3;}if(d1<d4){d1=d4;}
」で右下・下・左下・左隣のデータ最大値を変数「d1」に
セット
「if(d0<d1){*p=0;}」で注視点が近接データ未満ならば、
「*p=0;」で注視点画素を無効(=0)に更新し、
分岐中身ブロック終了
(9)ソースコード解説skeleton_8_x2()
code:skeleton_8_x2(仮引数){}
/************************************************************************/
/***** 骨格化コマンド:8連結用:下から上に処理:1行分 *****/
/***** 1行分の処理(右から左方向に処理を行う) *****/
/************************************************************************/
void Filter::skeleton_8_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
BYTE *pend; // 終点Ptr:左側
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
BYTE d3; // 近接データ3
BYTE d4; // 近接データ4
for( pend = p - h; p > pend; p-- ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p - inc - 1 ); // 左上の値と
d2 = *( p - inc ); // 上の値と
d3 = *( p - inc + 1 ); // 右上の値と
d4 = *( p - 1 ); // 左の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d1 < d3 ){ //
d1 = d3; //
} //
if( d1 < d4 ){ //
d1 = d4; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
} //
}
(9-1)関数名
「skeleton」は、距離を意味する!
「_8」は、8連結の処理を意味!
「x2」は、X座標方向の処理で「左⇒右」方向の処理を
意味!
(9-2)仮引数
code:仮引数
void Filter::skeleton_8_x2(
BYTE *p, // y軸用Ptr:右側
int inc, // 増加幅
int h // 水平方向大きさ
){
「BYTE* p,」は、画像画素へのポインタ
「int inc,」は、垂直方向増加幅※上下オフセット
「int h」は、水平幅
(9-2)ローカル変数
code:ローカル変数
){
BYTE *pend; // 終点Ptr:左側
BYTE d0; // 注視点データ
BYTE d1; // 近接データ1
BYTE d2; // 近接データ2
BYTE d3; // 近接データ3
BYTE d4; // 近接データ4
「BYTE *pend;」は、水平方向画素ポインタ終点※目印で
何故ループ条件に使用したかは、元々ADS社画像処理装置
のCPUが68000系でコンパイラ「MCC68K」を
使用して居たためにループ条件としデータレジスターを使い
切り、アドレスレジスターに変数を割り当てる高速化技法と
して「BYTE*pend;」を終点目印に使用した名残です!
「int d0;」は、注視点画素データ
「int d1;」は、近接画素データ1
「int d2;」は、近接画素データ2
「int d3;」は、近接画素データ3
「int d4;」は、近接画素データ4
(9-3)アルゴリズム
code:アルゴリズム
for( pend = p - h; p > pend; p-- ){ // 水平方向に繰返し
d0 = *p; // 注視点Data取出し
if( d0 ){ // Data有りなら
d1 = *( p - inc - 1 ); // 左上の値と
d2 = *( p - inc ); // 上の値と
d3 = *( p - inc + 1 ); // 右上の値と
d4 = *( p - 1 ); // 左の値を取り出し
if( d1 < d2 ){ // 最大の値を算出し
d1 = d2; //
} //
if( d1 < d3 ){ //
d1 = d3; //
} //
if( d1 < d4 ){ //
d1 = d4; //
} //
if( d0 < d1 ){ // 注視点が最大値未
*p = 0; // 満なら0にする
} //
} //
} //
}
「for(pend=p-h;p>pend;p--){・・ループ中身・・}」は、
水平幅分繰り返し「pend=p+h;」で終点目印を作成し
「p>pend;」で終点目印まで繰り返し「p--」で処理ポインタ
を右から左へ移動のforループでループ中身を処理、
そのループ中身は、
「d0=p;」で注視点画素データとしセット※何回も比較用
に使用するので高速化技法
「if(d0){・・分岐中身・・」で条件「d0」で有効画素(
≠0)の場合は、分岐中身の
「d1=(p+inc+1);d2=(p+inc);d3=(p+inc-1);d4=*(p+1);
」で右下・下・左下・左隣のデータを取り出し!
「if(d1<d2){d1=d2;}if(d1<d3){d1=d3;}if(d1<d4){d1=d4;}
」で右下・下・左下・左隣のデータ最大値を変数「d1」に
セット
「if(d0<d1){*p=0;}」で注視点が近接データ未満ならば、
「*p=0;」で注視点画素を無効(=0)に更新し、
分岐中身ブロック終了分岐中身ブロック終了