
この仕事は天職だと感じたアルゴリズム開発(続4)
この仕事は天職だと感じたアルゴリズム開発(続4)
2024年12月16日初稿
このエッセイは、
『この仕事は天職だと感じたアルゴリズム開発(続3)』の
直接の続編です!
1.図形の面積計測MeasureArea()
/************************************************************************/
/***** 図形の面積計測 :計測部(面積専用)*****/
/***** MEASURE_AREA(c,s,a,mh,mv,cx,cy) *****/
/************************************************************************/
int Filter::MeasureArea(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf buf[], // 計測結果Buffer
int lmt // Buffer格納制限
){
int sti; // ステータス情報
sti = CheckAndClearRoundImage( ps ); // 画像検査&周囲CLR
if( sti < 0 ){ // 不正が有れば
return( sti ); // Statusを返す
} //
return( measureBase_area( c, ps, buf, lmt ) ); // 面積を計測
}
(1)関数名
「Measure」は、「計測」でココでは、計測を意味
「Area」は、「面積」を意味
だから、「MeasureArea」で面積計測を実際に実行する
を意味します!
(2)仮引数
int Filter::MeasureFere(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf buf[], // 計測結果Buffer
int lmt // Buffer格納制限
){
「int c,」は、連結性≪「4」で4連結、「8」で8連結≫
「TypeArray* ps,」は、計測対象の2値化画像、
「TypeSf buf[],」は、計測結果バッファーで型「TypeSf」
は解説『解説クラスTypeSf』を参考にして下さい!
「int lmt」は、バッファーサイズ≪lmt名称は、格納制限
とし、この制限値まで計測し、実際に図形がオーバーする
場合は、計測を打ち切ります!≫
※備考※オーバーロード(多重定義)関数として仮引数の
数と型が異なる関数が存在します!
(3)ローカル変数数
){
int sti; // ステータス情報
「int sti;」は、ステータス情報(エラーコード)
(4)アルゴリズム
sti = CheckAndClearRoundImage( ps ); // 画像検査&周囲CLR
if( sti < 0 ){ // 不正が有れば
return( sti ); // Statusを返す
} //
return( measureBase_area( c, ps, buf, lmt ) ); // 面積を計測
}
「sti=CheckAndClearRoundImage(ps);」は、画像「ps」の
検査≪規定通り1バイトの2値化画像のサイズを満足して居
るか如何か≫し、合格ならば、画像の外周一回りクリアする
※備考※外周一回りクリアするので必要な画素が有る場合は
部分画像をコピーする等、工夫して使用して下さい!
「if(sti<0){return(sti);}」は、検査でエラー発生時は、
エラーコードを関数辺値とし返し終了!
「return(measureBase_area(c,ps,buf,lmt));」は、
下請け関数「measureBase_area(c,ps,buf,lmt)」で
フェレ計測の基本的な処理を行い、その関数辺値を返し終了
※備考※
この、MeasureArea()関数の方が、C言語から使用する場合は
便利な筈です!
2.面積計測:処理部measureBase_area()
/************************************************************************/
/***** 図形のフェレ計測+面積計測:処理部 *****/
/***** MEASURE_AREA(c,s,a) *****/
/***** ※注:フェレ計測と面積を計測します。モーメントはしません。 *****/
/***** ※注:結果が buf_lmt個までは、バッファーに格納しますがそれ *****/
/***** 以上ある場合は、バッファーの buf_lmt + 1 個目を更新す *****/
/***** るのみです。 *****/
/************************************************************************/
int Filter::measureBase_area(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf *buf, // 計測結果Buffer
int buf_lmt // Buffer格納制限
){
char **bufptr; // 追跡点履歴Buffer
int dir_off[ 8 ]; // 方向OffSetTable
char *px; // x方向処理Ptr
char *py; // y方向処理Ptr
int h; // 処理水平幅
int v; // 処理垂直幅
int inc; // 処理インクリ幅
int x; // x座標カウンタ
int y; // y座標カウンタ
int n; // 計測個数
int size; // 履歴Bufferサイズ
size = BORDERSIZE; // 履歴BufferSize初期値
bufptr = (char**)malloc( sizeof(char*) * size ); // 履歴Buffer取得
if( bufptr == 0 ){ // 失敗時は
return( STI_MEM ); // 左記を返し終了
} //
py = (char*)ps->adr; // 処理ADRを取出し
h = ps->h - 2; // 水平幅-2を算出
v = ps->v - 2; // 垂直幅-2を算出
inc = ps->inc; // 処理Inc幅取出
py += inc + 1; // 座標(1,1)にセット
SetDirOffset( c, dir_off, inc ); // 方向OffSetTblSet
n = 0; // 個数初期化
for( y = 1; y <= v; y++, py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
for( x = 1; x <= h; x++, px++ ){ // 水平方向繰り返し
if( *px > 0 ){ // 画素を発見したら
if( c == 4 ){ // 4連結なら
bufptr = search4fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
}else{ // 8連結なら
bufptr = search8fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
} //
if( bufptr == 0 ){ // 上記でメモリ確保失敗時は
return( STI_MEM ); // 左記を返し終了
} //
search_area( px - x + buf->x, buf, inc ); // 個別の面積計測
n++; // 個数を更新
if( n <= buf_lmt ){ // buf_lmt以下なら
buf++; // 結果バッファ更新
}else{ // Overなら
free( bufptr ); // 履歴Buffer解放し
return( n ); // 個数を返す
} //
} //
} //
} //
free( bufptr ); // 履歴Buffer解放
return( n ); // 個数を返す
}
(1)関数名
「measure」は、「計測」でココでは、計測を意味
「Base」は、「基本部」を意味
「area」は、「面積計測」を意味
だから、「measureBase_area」でフェレ計測内の面積計測を
実際に実行する基本部を意味します!
(2)仮引数
int Filter::measureBase_area(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf *buf, // 計測結果Buffer
int buf_lmt // Buffer格納制限
){
「int c,」は、連結性≪「4」で4連結、「8」で8連結≫
「TypeArray* ps,」は、計測対象の2値化画像、
「TypeSf buf[],」は、計測結果バッファーで型「TypeSf」
は解説『解説クラスTypeSf』を参考にして下さい!
「int buf_lmt」は、バッファーサイズ≪lmt名称は、格納
制限とし、この制限値まで計測し、実際に図形がオーバー
する場合は、計測を打ち切ります!≫
※備考※オーバーロード(多重定義)関数として仮引数の
数と型が異なる関数が存在します!
(3)ローカル変数
){
char **bufptr; // 追跡点履歴Buffer
int dir_off[ 8 ]; // 方向OffSetTable
char *px; // x方向処理Ptr
char *py; // y方向処理Ptr
int h; // 処理水平幅
int v; // 処理垂直幅
int inc; // 処理インクリ幅
int x; // x座標カウンタ
int y; // y座標カウンタ
int n; // 計測個数
int size; // 履歴Bufferサイズ
「char** bufptr;」は、追跡点履歴バッファー≪輪郭サーチ
を行った画素の場所「char*」型でポイントをバッファーに
格納する為のバッファー≫
「int dir_off[8];」は、方向オフセットテーブル≪輪郭
サーチ用の次の画素ポインタへのオフセットを格納した
テーブル≫
「char* px;」は、X座標方向画像画素ポインタ≪ラスター
スキャン用≫
「char* py;」は、Y座標方向画像画素ポインタ≪ラスター
スキャン用≫
「int h;」は、水平幅≪画像の水平幅より、外周一回りを
クリアして居るので内側の水平幅≫
「int v;」は、垂直幅≪画像の垂直幅より、外周一回りを
クリアして居るので内側の垂直幅≫
「int inc;」は、画像の増加幅≪画像の垂直方向への進行用
≫
「int x;」は、X座標≪画像を直交XY座標として≫
「int y;」は、Y標≪画像を直交XY座標として≫
「int n;」は、計測個数
「int size;」は、追跡点履歴バッファーのサイズ
(4)関連定義
#define BORDERSIZE 100000 // 輪郭バッファーのサイズ
「BORDERSIZE」は、「100000」を意味します!
※備考※連結した塊としての図形としては、実用性として
輪郭サーチの輪郭が連結≪4連結・8連結≫での画素数とし
ては、十分と考えましたが、初期値のサイズでもし、想定
以上に輪郭がツナガル画素が遇った場合は、サイズを更新
出来る仕組みを用意しています!
(5)アルゴリズム
size = BORDERSIZE; // 履歴BufferSize初期値
bufptr = (char**)malloc( sizeof(char*) * size ); // 履歴Buffer取得
if( bufptr == 0 ){ // 失敗時は
return( STI_MEM ); // 左記を返し終了
} //
py = (char*)ps->adr; // 処理ADRを取出し
h = ps->h - 2; // 水平幅-2を算出
v = ps->v - 2; // 垂直幅-2を算出
inc = ps->inc; // 処理Inc幅取出
py += inc + 1; // 座標(1,1)にセット
SetDirOffset( c, dir_off, inc ); // 方向OffSetTblSet
n = 0; // 個数初期化
for( y = 1; y <= v; y++, py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
for( x = 1; x <= h; x++, px++ ){ // 水平方向繰り返し
if( *px > 0 ){ // 画素を発見したら
if( c == 4 ){ // 4連結なら
bufptr = search4fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
}else{ // 8連結なら
bufptr = search8fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
} //
if( bufptr == 0 ){ // 上記でメモリ確保失敗時は
return( STI_MEM ); // 左記を返し終了
} //
search_area( px - x + buf->x, buf, inc ); // 個別の面積計測
n++; // 個数を更新
if( n <= buf_lmt ){ // buf_lmt以下なら
buf++; // 結果バッファ更新
}else{ // Overなら
free( bufptr ); // 履歴Buffer解放し
return( n ); // 個数を返す
} //
} //
} //
} //
free( bufptr ); // 履歴Buffer解放
return( n ); // 個数を返す
}
「size=BORDERSIZE;」は、輪郭サーチサイズの初期値セット
「bufptr=(char**)malloc(sizeof(char*)size);」は、輪郭
サーチして「char」と画像画素へのポインタをバッファー
に格納する為のバッファーを動的にメモリ確保
「if(bufptr==0){return(STI_MEM);}」は、メモリ確保に
失敗したらエラーコード「STI_MEM」を関数辺値とし返し
終了!
「py=(char*)ps->adr;」は、Y座標方向画像画素ポインタを
画像画素ポインタ始点(左上隅)にセット
「h=ps->h-2;v=ps->v-2;」は、画像「TypeArray* ps,」の
一回り内側の水平幅・垂直幅を「int h;int v;」にセット
「inc=ps->inc;」は、垂直方向増加幅をセット
「py+=inc+1;」は、Y座標方向画像画素ポインタをラスター
スキャンの始点≪一回り内側なのでXY座標(1,1)≫として
セット
「SetDirOffset(c,dir_off,inc);」は、方向オフセット
テーブルにオフセット値を作成する
「n=0;」は、図形の個数を初期化
「for(y=1;y<=v;y++,py+=inc){・・垂直方向・・}」は、
画像画素をラスタースキャンとして処理する垂直方向の
forループです、教科書的な「y=1;y<=v;y++,」とY座標を
「1・・v」まで繰り返し、ループ最後に「py+=inc」と
Y座標方向画像画素ポインタを垂直方向に進行し、
「・・垂直方向・・」と垂直方向の処理を行う、その処理は
「px=py;」でX座標方向画像画素ポインタをセット、
「for(x=1;x<=h;x++,px++){・・水平方向・・}」で
画像画素をラスタースキャンとして処理する水平方向の
forループです、教科書的な「x=1;x<=h;x++,」とX座標を
「1・・h」まで繰り返し、ループ最後に「px++」とX座標
方向画像画素ポインタを水平方向に進行し、
「if(*px>0){・・輪郭サーチ方向・・}」で条件「*px>0」で
有効な画素≪「0」を超える正の値≫の場合は、輪郭サーチ
方向に処理し、その処理の中で
「if(c==4){bufptr=search4fere(bufptr,px,x,y,buf,
dir_off,-1,size);}」で条件「c==4」詰まり、4連結時は、
下請け関数「bufptr=search4fere(bufptr,px,x,y,buf,
dir_off,-1,size);」と処理し、
「else{bufptr=search8fere(bufptr,px,x,y,buf,dir_off,
-1,size);}」で「else」と4連結以外≒8連結として下請け
関数「bufptr=search8fere(bufptr,px,x,y,buf,dir_off,-1,
size);}」で処理
「if(bufptr==0){return(STI_MEM);}」は、下請け関数「
search4fere()及び、search8fere()」内で輪郭サーチ用の
バッファーを大きなサイズに再メモリ確保≪一旦、新しい
バッファーを確保し、元のデータをコピーし、余裕がある
バッファーとし使用するが、メモリ確保に失敗した場合は、
空ポインタ(=0の値)で返るのでエラーと成る≫に失敗し
たら、らエラーコード「STI_MEM」を関数辺値とし返し終了
「search_area(px-x+buf->x,buf,inc);」は、下請け関数
「search_area()」で図形個別の面積計測を行います、
「px-x+buf->x」で図形のフェレ始点を算出する事に注意し
て下さい!
※備考※この「search_area(px-x+buf->x,buf,inc);」の部分
が図形のフェレ計測:処理部「measureBase_fere()」と異な
る部分です!「measureBase_fere()」は「search_area()」
を呼び出さ無いだけ処理時間が短く成るから存在します!
ここの「measureBase_area()」は、フェレ計測に加えて面積
も計測出来る事が特徴です!
「n++;」は、図形≪連結した塊≫の計測が完了したので個数
を増加する!
「if(n<=buf_lmt){buf++;}」は、条件「n<=buf_lmt」で
図形計測バッファーのサイズの制限値を超えて無い場合を
示し「buf++;」格納する場所を示すポインタを進める
「else{free(bufptr);return(n);}」は、バッファーの
サイズの制限値を超えたので「free(bufptr);」で確保した
動的にメモリをOSに返し、「return(n);」で制限値を超え
たまでの個数を関数辺値とし返し終了
そして全てのループが終了すると
「free(bufptr);」は、確保した動的にメモリをOSに返し
「return(n);」は、計測した図形の個数を関数辺値とし返し
終了
3.図形の面積計測:search_area()
/************************************************************************/
/***** 図形の面積計測:※面積選択用※ *****/
/***** フェレ径を求めた図形に対して処理します *****/
/***** ※注意※ 面積の前準備としてフェレ径を求めるのと同時に *****/
/***** 対象図形を「-2」の画素にしておく *****/
/***** ※注意※ 対象図形は、計測済みを示す「-1」にします。 *****/
/************************************************************************/
void Filter::search_area(
char *py, // フェレ始点Ptr
TypeSf *buf, // 計測結果Buffer
int inc // インクリ幅
){
char *px; // 追跡点:水平方向
int h; // 水平処理幅
int v; // 垂直処理幅
int x; // 水平方向カウンタ
int a; // 面積データ
h = buf->h; // 水平処理幅取出し
v = buf->v; // 垂直処理幅取出し
a = 0; // 面積Data初期化
for( ; --v >= 0; py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
for( x = h; --x >= 0; px++ ){ // 水平方向繰り返し
if( *px == -2 ){ // 対象画素なら
*px = -1; // 計測済みをセット
a++; // 面積カウントUp
} //
} //
} //
buf->a = a; // 面積を格納
}
(1)関数名
「search」は、カタカナ語「サーチ」でココでは、図形を
サーチする意味
「area」は、「面積計測」を意味
だから、「search_area」で図形≪4及び8連結の塊≫の
面積を計測するを意味します!
(2)仮引数
void Filter::search_area(
char *py, // フェレ始点Ptr
TypeSf *buf, // 計測結果Buffer
int inc // インクリ幅
){
「char* py,」は、フェレ計測始点の画像画素ポインタ
「TypeSf* buf,」は、計測結果バッファーを示すポインタ
「int inc」は、画像の増加幅≪画像の垂直方向への進行用
(3)ローカル変数
){
char *px; // 追跡点:水平方向
int h; // 水平処理幅
int v; // 垂直処理幅
int x; // 水平方向カウンタ
int a; // 面積データ
「char* px;」は、ラスタースキャンX座標方向追跡点
「int h;」は、水平幅≪X座標方向処理幅≫
「int v;」は、垂直幅≪Y座標方向処理幅≫
「int x,」は、X座標方向処理カウンター
「int a;」は、面積≪画素数≫
(4)アルゴリズム
h = buf->h; // 水平処理幅取出し
v = buf->v; // 垂直処理幅取出し
a = 0; // 面積Data初期化
for( ; --v >= 0; py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
for( x = h; --x >= 0; px++ ){ // 水平方向繰り返し
if( *px == -2 ){ // 対象画素なら
*px = -1; // 計測済みをセット
a++; // 面積カウントUp
} //
} //
} //
buf->a = a; // 面積を格納
}
「h=buf->h;v=buf->v;」は、図形の水平垂直幅を取り出し、
「a=0;」は、面積≪画素数≫データを初期化
「for(;--v>=0;py+=inc){・・ループ中身・・}」は、
forループ「;--v>=0;py+=inc」と初期化無しで
ループ条件「--v>=0;」と垂直幅分繰り返し、ループの終わ
りに「py+=inc」と垂直(Y座標)方向処理ポインタを垂直
方向に進めるで、ループ中身が
「px=py;」は、水平方向(X座標)追跡点を初期化し
「for(x=h;--x>=0;px++){if(*px==-2){*px=-1;a++;}}」は、
forループ「x=h;--x>=0;px++」で水平幅(X座標方向幅
)分繰り返し、ループの終わりに「px++」と追跡点を進め、
「if(*px==-2){*px=-1;a++;}」で条件「*px==-2」と追跡点
が「-2」≪search4fere()或はsearch8fere()関数で目印とし
てこれらの関数の仮引数「int clr_dt,」に「-2」を設定し
て画像画素を無効化「負の数」した意味とsearch_area()等
の継続して計測する処理の為の計測対象目印にして居る≫
ので面積計測対象と見なして「*px=-1;a++;」と
「*px=-1;」で計測処理も終了をセットし、「a++;」で面積
をカウントアップ
正方向≪上から下、左から右≫ラスタースキャンの二重ルー
プを終えると
「buf->a=a;」は、画素数を図形面積として計測結果へ格納
4.図形の面積計測MeasureMmt1()
/************************************************************************/
/***** 図形の面積計測 :計測部(面積+1次モーメント)*****/
/***** MEASURE_AREA(c,s,a,mh,mv,cx,cy) *****/
/************************************************************************/
int Filter::MeasureMmt1(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf buf[], // 計測結果Buffer
int lmt // Buffer格納制限
){
int sti; // ステータス情報
sti = CheckAndClearRoundImage( ps ); // 画像検査&周囲CLR
if( sti < 0 ){ // 不正が有れば
return( sti ); // Statusを返す
} //
return( measureBase_mmt1( c, ps, buf, lmt ) ); // 左記で計測する
}
(1)関数名
「Measure」は、「計測」でココでは、計測を意味
「Mmt」は、「モーメント」を意味
「1」は、「1次」を意味し、「Mmt1」で一次モーメントで
だから、「MeasureMmt1」で一次モーメント系計測を実際に
実行するを意味します!
(2)仮引数
int Filter::MeasureMmt1(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf buf[], // 計測結果Buffer
int lmt // Buffer格納制限
){
「int c,」は、連結性≪「4」で4連結、「8」で8連結≫
「TypeArray* ps,」は、計測対象の2値化画像、
「TypeSf buf[],」は、計測結果バッファーで型「TypeSf」
は解説『解説クラスTypeSf』を参考にして下さい!
「int lmt」は、バッファーサイズ≪lmt名称は、格納制限
とし、この制限値まで計測し、実際に図形がオーバーする
場合は、計測を打ち切ります!≫
※備考※オーバーロード(多重定義)関数として仮引数の
数と型が異なる関数が存在します!
(3)ローカル変数
){
int sti; // ステータス情報
「int sti;」は、ステータス情報(エラーコード)
(4)アルゴリズム
sti = CheckAndClearRoundImage( ps ); // 画像検査&周囲CLR
if( sti < 0 ){ // 不正が有れば
return( sti ); // Statusを返す
} //
return( measureBase_mmt1( c, ps, buf, lmt ) ); // 左記で計測する
}
「sti=CheckAndClearRoundImage(ps);」は、画像「ps」の
検査≪規定通り1バイトの2値化画像のサイズを満足して居
るか如何か≫し、合格ならば、画像の外周一回りクリアする
※備考※外周一回りクリアするので必要な画素が有る場合は
部分画像をコピーする等、工夫して使用して下さい!
「if(sti<0){return(sti);}」は、検査でエラー発生時は、
エラーコードを関数辺値とし返し終了!
「return(measureBase_area(c,ps,buf,lmt));」は、
下請け関数「measureBase_mmt1(c,ps,buf,lmt)」で
フェレ計測内モーメント系の基本的な処理を行い、
その関数辺値を返し終了
※備考※
この、MeasureMmt1()関数の方が、C言語から使用する場合は便利な筈です!
5.モーメント系計測:処理部
measureBase_mmt1()
/************************************************************************/
/***** 図形のフェレ計測+面積計測+1次モーメント:処理部 *****/
/***** MEASURE_AREA(c,s,a,mh,mv) *****/
/***** ※注:フェレ計測と面積と1次モーメントを計測します。 *****/
/***** ※注:結果が buf_lmt個までは、バッファーに格納しますがそれ *****/
/***** 以上ある場合は、バッファーの buf_lmt + 1 個目を更新す *****/
/***** るのみです。 *****/
/************************************************************************/
int Filter::measureBase_mmt1(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf *buf, // 計測結果Buffer
int buf_lmt // Buffer格納制限
){
char **bufptr; // 追跡点履歴Buffer
int dir_off[ 8 ]; // 方向OffSetTable
char *px; // x方向処理Ptr
char *py; // y方向処理Ptr
int h; // 処理水平幅
int v; // 処理垂直幅
int inc; // 処理インクリ幅
int x; // x座標カウンタ
int y; // y座標カウンタ
int n; // 計測個数
int size; // 履歴Bufferサイズ
size = BORDERSIZE; // 履歴BufferSize初期値
bufptr = (char**)malloc( sizeof(char*) * size ); // 履歴Buffer取得
if( bufptr == 0 ){ // 失敗時は
return( STI_MEM ); // 左記を返し終了
} //
py = (char*)ps->adr; // 処理ADRを取出し
h = ps->h - 2; // 水平幅-2を算出
v = ps->v - 2; // 垂直幅-2を算出
inc = ps->inc; // 処理Inc幅取出
py += inc + 1; // 座標(1,1)にセット
SetDirOffset( c, dir_off, inc ); // 方向OffSetTblSet
n = 0; // 個数初期化
for( y = 1; y <= v; y++, py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
for( x = 1; x <= h; x++, px++ ){ // 水平方向繰り返し
if( *px > 0 ){ // 画素を発見したら
if( c == 4 ){ // 4連結なら
bufptr = search4fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
}else{ // 8連結なら
bufptr = search8fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
} //
if( bufptr == 0 ){ // 上記でメモリ確保失敗時は
return( STI_MEM ); // 左記を返し終了
} //
search_mmt1( px - x + buf->x, buf, inc ); // 面積+1次Momentを計測
n++; // 個数を更新
if( n <= buf_lmt ){ // buf_lmt以下なら
buf++; // 結果バッファ更新
}else{ // Overなら
free( bufptr ); // 履歴Buffer解放し
return( n ); // 個数を返す
} //
} //
} //
} //
free( bufptr ); // 履歴Buffer解放
return( n ); // 個数を返す
}
(1)関数名
code:
「measure」は、「計測」でココでは、計測を意味
「Base」は、「基本部」を意味
「mmt」は、「モーメント」を意味
「1」は、「1次」を意味し、「Mmt1」で一次モーメントで
だから、「mmt1」で一次モーメント系計測を実際に
実行するを意味します!
だから、「measureBase_mmt1」でフェレ計測した範囲で
モーメント系計測を実際に実行する基本部を意味します!
(2)仮引数
int Filter::measureBase_mmt1(
int c, // 4/8連結
TypeArray *ps, // 画像
TypeSf *buf, // 計測結果Buffer
int buf_lmt // Buffer格納制限
){
「int c,」は、連結性≪「4」で4連結、「8」で8連結≫
「TypeArray* ps,」は、計測対象の2値化画像、
「TypeSf buf[],」は、計測結果バッファーで型「TypeSf」
は解説『解説クラスTypeSf』を参考にして下さい!
「int buf_lmt」は、バッファーサイズ≪lmt名称は、格納
制限とし、この制限値まで計測し、実際に図形がオーバー
する場合は、計測を打ち切ります!≫
※備考※オーバーロード(多重定義)関数として仮引数の
数と型が異なる関数が存在します!
(3)ローカル変数
){
char **bufptr; // 追跡点履歴Buffer
int dir_off[ 8 ]; // 方向OffSetTable
char *px; // x方向処理Ptr
char *py; // y方向処理Ptr
int h; // 処理水平幅
int v; // 処理垂直幅
int inc; // 処理インクリ幅
int x; // x座標カウンタ
int y; // y座標カウンタ
int n; // 計測個数
int size; // 履歴Bufferサイズ
「char** bufptr;」は、追跡点履歴バッファー≪輪郭サーチ
を行った画素の場所「char*」型でポイントをバッファーに
格納する為のバッファー≫
「int dir_off[8];」は、方向オフセットテーブル≪輪郭
サーチ用の次の画素ポインタへのオフセットを格納した
テーブル≫
「char* px;」は、X座標方向画像画素ポインタ≪ラスター
スキャン用≫
「char* py;」は、Y座標方向画像画素ポインタ≪ラスター
スキャン用≫
「int h;」は、水平幅≪画像の水平幅より、外周一回りを
クリアして居るので内側の水平幅≫
「int v;」は、垂直幅≪画像の垂直幅より、外周一回りを
クリアして居るので内側の垂直幅≫
「int inc;」は、画像の増加幅≪画像の垂直方向への進行用
≫
「int x;」は、X座標≪画像を直交XY座標として≫
「int y;」は、Y標≪画像を直交XY座標として≫
「int n;」は、計測個数
「int size;」は、追跡点履歴バッファーのサイズ
(4)関連定義
#define BORDERSIZE 100000 // 輪郭バッファーのサイズ
「BORDERSIZE」は、「100000」を意味します!
※備考※連結した塊としての図形としては、実用性として
輪郭サーチの輪郭が連結≪4連結・8連結≫での画素数とし
ては、十分と考えましたが、初期値のサイズでもし、想定
以上に輪郭がツナガル画素が遇った場合は、サイズを更新
出来る仕組みを用意しています!
(5)アルゴリズム
size = BORDERSIZE; // 履歴BufferSize初期値
bufptr = (char**)malloc( sizeof(char*) * size ); // 履歴Buffer取得
if( bufptr == 0 ){ // 失敗時は
return( STI_MEM ); // 左記を返し終了
} //
py = (char*)ps->adr; // 処理ADRを取出し
h = ps->h - 2; // 水平幅-2を算出
v = ps->v - 2; // 垂直幅-2を算出
inc = ps->inc; // 処理Inc幅取出
py += inc + 1; // 座標(1,1)にセット
SetDirOffset( c, dir_off, inc ); // 方向OffSetTblSet
n = 0; // 個数初期化
for( y = 1; y <= v; y++, py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
for( x = 1; x <= h; x++, px++ ){ // 水平方向繰り返し
if( *px > 0 ){ // 画素を発見したら
if( c == 4 ){ // 4連結なら
bufptr = search4fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
}else{ // 8連結なら
bufptr = search8fere( bufptr, px, x, y, // 個別のフェレ計測
buf, dir_off, -2, size ); //
} //
if( bufptr == 0 ){ // 上記でメモリ確保失敗時は
return( STI_MEM ); // 左記を返し終了
} //
search_mmt1( px - x + buf->x, buf, inc ); // 面積+1次Momentを計測
n++; // 個数を更新
if( n <= buf_lmt ){ // buf_lmt以下なら
buf++; // 結果バッファ更新
}else{ // Overなら
free( bufptr ); // 履歴Buffer解放し
return( n ); // 個数を返す
} //
} //
} //
} //
free( bufptr ); // 履歴Buffer解放
return( n ); // 個数を返す
}
「size=BORDERSIZE;」は、輪郭サーチサイズの初期値セット
「bufptr=(char**)malloc(sizeof(char*)size);」は、輪郭サーチして「char」と画像画素へのポインタをバッファーに格納
する為のバッファーを動的にメモリ確保
「if(bufptr==0){return(STI_MEM);}」は、メモリ確保に
失敗したらエラーコード「STI_MEM」を関数辺値とし返し
終了!
「py=(char*)ps->adr;」は、Y座標方向画像画素ポインタを
画像画素ポインタ始点(左上隅)にセット
「h=ps->h-2;v=ps->v-2;」は、画像「TypeArray* ps,」の
一回り内側の水平幅・垂直幅を「int h;int v;」にセット
「inc=ps->inc;」は、垂直方向増加幅をセット
「py+=inc+1;」は、Y座標方向画像画素ポインタをラスター
スキャンの始点≪一回り内側なのでXY座標(1,1)≫として
セット
「SetDirOffset(c,dir_off,inc);」は、方向オフセット
テーブルにオフセット値を作成する
「n=0;」は、図形の個数を初期化
「for(y=1;y<=v;y++,py+=inc){・・垂直方向・・}」は、
画像画素をラスタースキャンとして処理する垂直方向の
forループです、教科書的な「y=1;y<=v;y++,」とY座標を
「1・・v」まで繰り返し、ループ最後に「py+=inc」と
Y座標方向画像画素ポインタを垂直方向に進行し、
「・・垂直方向・・」と垂直方向の処理を行う、その処理は
「px=py;」でX座標方向画像画素ポインタをセット、
「for(x=1;x<=h;x++,px++){・・水平方向・・}」で
画像画素をラスタースキャンとして処理する水平方向の
forループです、教科書的な「x=1;x<=h;x++,」とX座標を
「1・・h」まで繰り返し、ループ最後に「px++」とX座標
方向画像画素ポインタを水平方向に進行し、
「if(*px>0){・・輪郭サーチ方向・・}」で条件「*px>0」で
有効な画素≪「0」を超える正の値≫の場合は、輪郭サーチ
方向に処理し、その処理の中で
「if(c==4){bufptr=search4fere(bufptr,px,x,y,buf,
dir_off,-1,size);}」で条件「c==4」詰まり、4連結時は、
下請け関数「bufptr=search4fere(bufptr,px,x,y,buf,
dir_off,-1,size);」と処理し、
「else{bufptr=search8fere(bufptr,px,x,y,buf,dir_off,
-1,size);}」で「else」と4連結以外≒8連結として下請け
関数「bufptr=search8fere(bufptr,px,x,y,buf,dir_off,-1,
size);}」で処理
「if(bufptr==0){return(STI_MEM);}」は、下請け関数「
search4fere()及び、search8fere()」内で輪郭サーチ用の
バッファーを大きなサイズに再メモリ確保≪一旦、新しい
バッファーを確保し、元のデータをコピーし、余裕がある
バッファーとし使用するが、メモリ確保に失敗した場合は、
空ポインタ(=0の値)で返るのでエラーと成る≫に失敗し
たら、らエラーコード「STI_MEM」を関数辺値とし返し終了
「search_mmt1(px-x+buf->x,buf,inc);」は、下請け関数
「search_mmt1()」で図形個別の面積計測を行います、
※備考※この「search_mmt1(px-x+buf->x,buf,inc);」の部分
が図形のフェレ計測:処理部「measureBase_fere()」と異な
る部分です!「measureBase_fere()」は「search_mmt1()」
を呼び出さ無いだけ処理時間が短く成るから存在します!
ここの「measureBase_mmt1()」は、フェレ計測に加えて
一次モーメント系の計測も計測出来る事が特徴です!
「px-x+buf->x」で図形のフェレ始点を算出する事に注意し
て下さい!
「n++;」は、図形≪連結した塊≫の計測が完了したので個数
を増加する!
「if(n<=buf_lmt){buf++;}」は、条件「n<=buf_lmt」で
図形計測バッファーのサイズの制限値を超えて無い場合を
示し「buf++;」格納する場所を示すポインタを進める
「else{free(bufptr);return(n);}」は、バッファーの
サイズの制限値を超えたので「free(bufptr);」で確保した
動的にメモリをOSに返し、「return(n);」で制限値を超え
たまでの個数を関数辺値とし返し終了
そして全てのループが終了すると
「free(bufptr);」は、確保した動的にメモリをOSに返し
「return(n);」は、計測した図形の個数を関数辺値とし返し
終了
6.図形の面積計測:search_mmt1()
/************************************************************************/
/***** 図形の面積計測+水平垂直の1次モーメントの計測 *****/
/***** フェレ径を求めた図形に対して処理します *****/
/***** ※注意※ 面積の前準備としてフェレ径を求めるのと同時に *****/
/***** 対象図形を「-2」の画素にしておく *****/
/***** ※注意※ 1次モーメントは、フェレ始点を座標(0,0)と *****/
/***** した相対値で算出します。 *****/
/***** ※注意※ 垂直モーメントは、水平方向の面積×y座標をΣ *****/
/***** ※注意※ 対象図形は、計測済みを示す「-1」にします。 *****/
/************************************************************************/
void Filter::search_mmt1(
char *py, // フェレ始点Ptr
TypeSf *buf, // 計測結果Buffer
int inc // インクリ幅
){
char *pend; // 追跡点:終点
char *px; // 追跡点:水平方向
int h; // 水平処理幅
int x; // x座標
int y; // y座標
int a; // 面積データ
int ax; // 面積Data:x方向
int mhx; // 1次水平MMTx方向
double mh; // 1次水平MomentData
double mv; // 1次垂直MomentData
h = buf->h; // 水平処理幅取出し
a = 0; // 面積Data初期化
mh = 0.0; // 水平Data初期化
mv = 0.0; // 垂直Data初期化
pend = py + buf->v * inc; // 終点を算出
for( y = 1; py < pend; y++, py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
ax = 0; // 面積Data初期化
mhx = 0; // 水平MMTData初期化
x = 1; // x座標初期化
while( *px != -2 ){ // 最初の有効画素を
px++; // 探しPtrと座標を
x++; // 進める
} //
while( *px == -2 ){ // 画素有効な間
*px++ = -1; // 計測済みセットしptrを進め
ax++; // 面積カウントUp
mhx += x; // 水平MmtData更新
x++; // x座標を進める
} //
for(;;){ // 2番以降を繰返す
for( ; x <= h; px++, x++ ){ // 水平方向で
if( *px == -2 ){ // 有効画素を探し
break; // 内側Loopを抜ける
} //
} //
if( x > h ){ // 水平終了時なら
break; // 中間Loop終了
} //
while( *px == -2 ){ // 画素有効な間
*px++ = -1; // 計測済みセットしptrを進め
ax++; // 面積カウントUp
mhx += x; // 水平MmtData更新
x++; // x座標を進める
} //
} //
a += ax; // 面積Data更新
mh += (double)mhx; // 水平Moment更新
mv += (double)( ax * y ); // 垂直Moment更新
} //
buf->a = a; // 面積を格納
buf->mh = (float)mh; // 水平Momentを格納
buf->mv = (float)mv; // 垂直Momentを格納
}
(1)関数名
「search」は、カタカナ語「サーチ」でココでは、図形を
サーチする意味
「mmt」は、「モーメント」を意味
「mmt1」は、「一次モーメント系計測」を意味
だから、「search_mmt1」で図形≪4及び8連結の塊≫の
一次モーメント系計測を計測するを意味します!
(2)仮引数
void Filter::search_mmt1(
char *py, // フェレ始点Ptr
TypeSf *buf, // 計測結果Buffer
int inc // インクリ幅
){
「char* py,」は、フェレ計測始点の画像画素ポインタ
「TypeSf* buf,」は、計測結果バッファーを示すポインタ
は解説『解説クラスTypeSf』を参考にして下さい!
「int inc」は、画像の増加幅≪画像の垂直方向への進行用
(3)ローカル変数
char *pend; // 追跡点:終点
char *px; // 追跡点:水平方向
int h; // 水平処理幅
int x; // x座標
int y; // y座標
int a; // 面積データ
int ax; // 面積Data:x方向
int mhx; // 1次水平MMTx方向
double mh; // 1次水平MomentData
double mv; // 1次垂直MomentData
「char* pend;」は、ラスタースキャンY座標方向終点≪
ソースコード開発時に使用したMC68060でコンパイラ
MCC68Kの機械コードでデータレジスターをローカル
変数として使い切ってたのでラスタースキャン外側ループの
制御にアドレスレジスターとし使用するポインタで比較制御
を考え高速化の為めに採用≫
「char* px;」は、ラスタースキャンX座標方向追跡点
「int h;」は、水平幅≪X座標方向処理幅≫
「int x,」は、X座標方向処理カウンター
「int y,」は、Y座標方向処理カウンター
「int a;」は、面積≪画素数≫
「int mhx;」は、1次水平方向モーメント値(水平方向分)
「double mh;」は、1次水平方向モーメント値
「double mv;」は、1次垂直方向モーメント値
(4)アルゴリズム
h = buf->h; // 水平処理幅取出し
a = 0; // 面積Data初期化
mh = 0.0; // 水平Data初期化
mv = 0.0; // 垂直Data初期化
pend = py + buf->v * inc; // 終点を算出
for( y = 1; py < pend; y++, py += inc ){ // 垂直処理幅分繰返
px = py; // 水平処理始点Setし
ax = 0; // 面積Data初期化
mhx = 0; // 水平MMTData初期化
x = 1; // x座標初期化
while( *px != -2 ){ // 最初の有効画素を
px++; // 探しPtrと座標を
x++; // 進める
} //
while( *px == -2 ){ // 画素有効な間
*px++ = -1; // 計測済みセットしptrを進め
ax++; // 面積カウントUp
mhx += x; // 水平MmtData更新
x++; // x座標を進める
} //
for(;;){ // 2番以降を繰返す
for( ; x <= h; px++, x++ ){ // 水平方向で
if( *px == -2 ){ // 有効画素を探し
break; // 内側Loopを抜ける
} //
} //
if( x > h ){ // 水平終了時なら
break; // 中間Loop終了
} //
while( *px == -2 ){ // 画素有効な間
*px++ = -1; // 計測済みセットしptrを進め
ax++; // 面積カウントUp
mhx += x; // 水平MmtData更新
x++; // x座標を進める
} //
} //
a += ax; // 面積Data更新
mh += (double)mhx; // 水平Moment更新
mv += (double)( ax * y ); // 垂直Moment更新
} //
buf->a = a; // 面積を格納
buf->mh = (float)mh; // 水平Momentを格納
buf->mv = (float)mv; // 垂直Momentを格納
}
「h=buf->h;」は、図形の水平幅を取り出し、
「a=0;」は、面積≪画素数≫データを初期化
「mh=0.0;mv=0.0;」は、1次水平垂直モーメント初期化
「pend=py+buf->v*inc;」は、外側ループY座標方向ラス
タースキャンのY座標方向終点を算出し、「pend」にセット
「for(y=1;py<pend;y++,py+=inc){・・ループ中身・・}」
は、forループ「y=1;py<pend;y++,py+=inc」と初期化
「y=1;」でY座標初期化(=1)、ループ条件「py<pend;」
と外側ループY座標方向ポインタと終点で条件にし、増分
「y++,py+=inc」の「y++」とY座標方向を増加し、
「py+=inc」と垂直方向(Y座標方向)ポインタを増加≪
※備考※仮条件「y<buf->v;」の方が判り易いと思われるが
ソースコード開発時に使用したMC68060でコンパイラ
MCC68Kの機械コードでデータレジスターをローカル
変数として使い切る事に成ると処理速度が遅く成るのでポイ
ンタ同士の比較「py<pend」にして高速化した為です≫
ループ条件「py<pend;」と垂直幅分繰り返し、ループの終わ
りに「y++,py+=inc」と垂直(Y座標)方向処理ポインタを
垂直方向に進めるで、外側ループ中身が
「px=py;」は、水平方向(X座標)追跡点を初期化し
「ax=0;mhx=0;」は、水平方向ローカルの面積と水平方向の
モーメント値を初期化
「x=1;」は、X座標初期化(=1)、
「while(*px!=-2){px++;x++;}」は、目印「-2」に到達する
まで「px++;x++;」、水平方向追跡点とX座標を増加
「while(*px==-2){*px++=-1;ax++;mhx+=x;x++;}」は、
目印「-2」が続く間、「*px++=-1;ax++;mhx+=x;x++;」で
「*px++=-1;」と追跡点に計測終了「-1」をセットしポイン
タ進行、
「ax++;」で水平方向ローカル面積カウントアップ、
「mhx+=x;」で水平方向ローカルにモーメント累算、
「x++;」でX座標を増加、
「for(;;){・ラスタースキャン水平(X座標)処理・}」は
無限forループ「for(;;)」でラスタースキャン水平(
X座標)方向処理を行い水平方向処理の中の「break;」で
ループから抜けるループ処理を行い、ループの中は、
「for(;x<=h;px++,x++){if(*px==-2){break;}}」で
最内側forループ「;x<=h;px++,x++」で初期化無しの
ループ条件「x<=h;」でX座標が水平幅いっぱい処理し、
増分「px++,x++」と追跡点とX座標を増加、そして繰り返し
「if(*px==-2){break;}」と最内側forループを脱出する
「break;」が条件「*px==-2」と目印「-2」で脱出※注意:
無限forループ「for(;;)」を脱出する「break;」で無い
事に留意、つまり、目印を見つけるか水平方向終わりまで
繰り返すループです!
「if(x>h){break;}」で条件「x>h」が成立時は、目印が見つ
かる事なく水平方向処理が終わった事を意味して
「break;」で無限forループ「for(;;)」を脱出します!
「while(*px==-2){px++=-1;ax++;mhx+=x;x++;}」で
目印「-2」が続く間、「px++=-1;ax++;mhx+=x;x++;」と
「px++=-1;」と追跡点に計測終了「-1」をセットしポイン
タ進行、
「ax++;」で水平方向ローカル面積カウントアップ、
「mhx+=x;」で水平方向ローカルにモーメント累算、
「x++;」でX座標を増加、
これで、ラスタースキャン水平(X座標)方向処理のループ
が終了し、「ax」にX座標方向ローカルな面積が算出し、
「mhx」にX座標方向ローカルな水平方向一次モーメント
算出!
「a+=ax;」は、全体の面積「a」にX座標方向ローカル面積を累計
「mh+=(double)mhx;」は、全体の水平方向一次モーメント
「mh」にX座標方向ローカル水平方向一次モーメントを累計
「mv+=(double)(axy);」は、「axy」でY座標個別の垂直
一次モーメントを算出で「mv+=(double)(axy);」で全体の
垂直一次モーメントを累計
※注意:内部で演算する場合は、CPUの高精細浮動小数点
演算レジスターに格納される為に「double型」のローカル
変数を使用しています!
ここまでのループで外側垂直方向(Y座標)ループを終了ま
で繰り返します!
※備考※
条件「*px==-2」と追跡点が「-2」≪search4fere()或は、
search8fere()関数で目印としてこれらの関数の
仮引数「int clr_dt,」に「-2」を設定して画像画素を
無効化「負の数」した意味とsearch_mmt1()等の継続して
計測する処理の為の計測対象目印にして居る事に注意≫
正方向≪上から下、左から右≫ラスタースキャンの
二重ループを終えると
「buf->a=a;」は、画素数を図形面積として計測結果へ格納
「buf->mh=(float)mh;」は、内部で「double型」で高精細
計測した1次水平モーメントを格納する「float型」と言う
省メモリに変換して格納
「buf->mv=(float)mv;」は、内部で「double型」で高精細
計測した1次水平モーメントを格納する「float型」と言う
省メモリに変換して格納
7.終わりに
この仕事は天職だと感じたアルゴリズム開発から、
代表的な関数として「MeasureStandard()」≒標準計測を
説明し、4連結のフェレ計測を「search4fere()」で
8連結のフェレ計測を「search8fere()」で解説し、
その方式で作成した図形個別に面積を「search_area()」で
計測し、同じく一次モーメントを「search_mmt1()」で作成
を例に、色々な処理が行って居る事を説明した心算です!
これで、オーソドックスなラベリング処理でのラベル画像
から色々幾何的な計測を行う事に比べて高速化した処理を
説明しました!実際にベンチマークテストを行って
自らが天才で無いかと悦に入った覚えが有るが、それの
説明に10日間以上掛かってシマッタ、
覚えているのは、この機構を徹夜込みで数日間の内に一気に
作成したので天才と考えました!しかしながら、恐らく、
エッセイ『働き方改革』に記した様に私の脳細胞を壊した元
と思える!読者様もくれぐれもご自愛ください!
ベンチマークテストが、想定以上に好成績に成ったのは、
エッセイ『高速大容量の代表的処理が、画像処理』でプログ
ラムが遅く成る原因をメモリアクセスと説明しましたが、
キャッシュメモリを効率的に使えないアルゴリズムを徹底的
に排除した事は、読み取って頂けたかな?