解説クラスCopyClear(11)
解説クラスCopyClear(11)
2024年3月18初講(初稿)
この講義は解説『解説クラスCopyClear(10)』
の続きです!
2024年3月18初講(初稿)
(4-14-7)関数「int Convert(
TypeArray* ps,TypeArray* pd,TypeArray* lut){・・・}」の説明
int CopyClear::Convert(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
TypeArray *lut // LUT配列
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int ll; // lut配列サイズ
int hv; // 水平/垂直幅
ws = ps->w; // 単位幅取出し:S
ll = lut->h; // サイズ取出し:LUT
if( ws == 1 ){ // Sが1Byte整数時
if( ll < 256 ){ // LUTSizeが不足なら
return( STI_ARY_2 - 30 ); // 3番引数エラー
} //
}else if( ws == 2 ){ // Sが2Byte整数時
if( ll < 65536 ){ // LUTSizeが不足なら
return( STI_ARY_2 - 30 ); // 3番引数エラー
} //
}else{ // 上記以外なら
return( STI_ARY_5 - 10 ); // 1番引数エラー
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( convert_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( convert_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
(4-14-7-A)関数「int Convert()」の【関数名】説明
「Convert」は、英単語「Convert」として変換するの意味で
す!ここの画像処理では、LUT≪ルックアップテーブル≫
を使用したS(元)画像⇒LUT変換⇒D(結果)画像への
画素データをLUT≪Lookuptable≫として一時的に変換す
る技法を使用して変換する関数です!
(4-14-7-B)関数「int Convert()」の【返値】説明
int CopyClear::Convert(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
TypeArray *lut // LUT配列
){
特徴的なのは、仮引数「TypeArray* ps,」とS(元)画像の
画素単位「ps->w」が、1バイト単位の場合⇒仮引数「
TypeArray* lut」とLUTを示す1次元配列のサイズ「
lut->h」が256未満ならば、エラー「STI_ARY_2-30」を
返し、2バイト単位の場合⇒LUTを示す1次元配列の
サイズ「lut->h」が65536未満ならば、
エラー「STI_ARY_2-30」を返し、S(元)画像の画素
単位「ps->w」が、1や2で無い場合は、
エラー「STI_ARY_5-10」を返します!このエラーチェック
を通過したら、S(元)画像の画素単位「ps->w」が、
1バイト単位の場合⇒「return(convert_b(ps,pd,lut));」
でエラーチェックします、
2バイト単位の場合⇒「return(convert_w(ps,pd,lut));」
でエラーチェックします。
(4-14-7-C)関数「int Convert()」の【仮引数】説明
int CopyClear::Convert(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
TypeArray *lut // LUT配列
){
「TypeArray* ps,」は、S(元)画像を示します!
「TypeArray* pd,」は、D(結果)画像を示します!
「TypeArray* lut」は、LUTを示す1次元配列です!
(4-14-7-D)関数「int Convert()」の【アルゴリズム】説明
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int ll; // lut配列サイズ
int hv; // 水平/垂直幅
ws = ps->w; // 単位幅取出し:S
ll = lut->h; // サイズ取出し:LUT
if( ws == 1 ){ // Sが1Byte整数時
if( ll < 256 ){ // LUTSizeが不足なら
return( STI_ARY_2 - 30 ); // 3番引数エラー
} //
}else if( ws == 2 ){ // Sが2Byte整数時
if( ll < 65536 ){ // LUTSizeが不足なら
return( STI_ARY_2 - 30 ); // 3番引数エラー
} //
}else{ // 上記以外なら
return( STI_ARY_5 - 10 ); // 1番引数エラー
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( convert_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( convert_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
ローカル変数
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int ll; // lut配列サイズ
int hv; // 水平/垂直幅
「TypeArray s;」は、内部でサイズを補正したS画像
「TypeArray d;」は、内部でサイズを補正したD画像
「int ws;」は仮引数「TypeArray* ps」の単位幅を扱い易く
取り出した値です!
「int ll;」は仮引数「TypeArray* lut」の水平幅≪1次元配列
扱いなので1次元のサイズ≫を扱い易く取り出した値です!
「int hv;」は、仮引数「TypeArray* ps,TypeArray* pd」
の水平幅/垂直幅の最小値(SD両者の有効幅)算出用です
アルゴリズムコード
ws = ps->w; // 単位幅取出し:S
ll = lut->h; // サイズ取出し:LUT
if( ws == 1 ){ // Sが1Byte整数時
if( ll < 256 ){ // LUTSizeが不足なら
return( STI_ARY_2 - 30 ); // 3番引数エラー
} //
}else if( ws == 2 ){ // Sが2Byte整数時
if( ll < 65536 ){ // LUTSizeが不足なら
return( STI_ARY_2 - 30 ); // 3番引数エラー
} //
}else{ // 上記以外なら
return( STI_ARY_5 - 10 ); // 1番引数エラー
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( convert_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( convert_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
「ws=ps->w;」は、S画像単位幅を扱い易く取り出し!
「ll=lut->h;」は、変換用LUT1次元配列のサイズを
扱い易く取り出し!
「if(ws==1){・・左記条件成立1・・}else if(ws==2){
・・左記条件成立2・・}else{・・条件不成立・・}」は、
条件「ws==1」でS画像が1バイト単位の時、条件成立1を
実行で条件成立1「if(ll<256){return(STI_ARY_2-30);}」
でエラーチェックとしてLUTのサイズが256未満≪詰り
1バイト画素の値が符号無し8ビット「0~255」に成る
ので対応するテーブルのサイズが256必要≫なのでエラー
として「STI_ARY_2-30」を返し関数終了「-30」と3番目の
「TypeArray* 」型仮引数に原因が有る事を示します!
条件「ws==2」でS画像が2バイト単位の時、条件成立2を
実行で
条件成立2「if(ll<65536){return(STI_ARY_2-30);}」で
エラーチェックとしてLUTのサイズが65536未満≪
詰り2バイト画素の値が符号無し16ビット「0~
65535」に成るので対応するテーブルのサイズが、
65536必要≫なのでエラーとして「STI_ARY_2-30」を
返し関数終了!
そして「}else{」と条件不成立で
「return(STI_ARY_5-10);」とエラーチェックとしてS画像
が1バイト単位でも2バイト単位でも無い場合にエラーと
して「STI_ARY_5-10」を返し関数終了「-10」と1番目の
「TypeArray* 」型仮引数に原因が有る事を示します!
「s=*ps;ps=&s;」は、ローカル変数にS画像情報を一旦、
サイズ補正を考えてコピーして置きます!
「d=*pd;pd=&d;」もD画像情報の補正用です!
「hv=(ps->h<pd->h)?ps->h:pd->h;」は、SD両者水平幅の
最小値(有効幅)算出です!
「ps->h=hv;pd->h=hv;」は、SD両画像情報の
ローカル変数の水平幅を有効幅に補正します!
「hv=(ps->v<pd->v)?ps->v:pd->v;ps->v=hv;pd->v=hv;」
は、SD両画像情報のローカル変数の垂直幅を有効幅に補正
します!
「if(ws==1){return(convert_b(ps,pd,lut));
}else{return(convert_w(ps,pd,lut));}」は、
条件「ws==1」でS画像画素が1バイト単位の場合、
サブルーチン「convert_b()」で処理し結果の間数値を返し
条件不成立詰りS画像画素が2バイト単位の場合、
サブルーチン「convert_w()」で処理し結果の間数値を返し
関数終了!
(4-14-7-E)関数「int Convert()」の【使用例】説明
{
TypeArray PatImg; // 画像型変数
// :編か前のパターン画像
TypeArray* pPatImg=&PatImg; // 上記へのポインタ
TypeArray Wimg; // 画像型変数
//:フィル書き込み用
TypeArray* pWimg=&Wimg; // 上記へのポインタ
//:部分画像用
TypeArray* pimgA=&PatImg; // 部分画像用
TypeArray* pimgB=&PatImg; // 部分画像用
//:LUT配列用
TypeArray ALut; // LUT型変数
TypeArray* pLut=&ALut; // LUT型ポインタ変数
BYTE lut[256]; // LUT実体
long hgm[256]; // ヒストグラム算出データ配列
BYTE* p; // 画像画素へのポインタ
int i; // ループカウンタ
int x; // X座標
int y; // Y座標
int d; // 画素データ
int t; // 2値化しきい値
int sti; // ステータス情報
pPatImg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pPatImg, 0 ); // 画像メモリ「0クリア」
p = pPatImg->getPtrByte( 0, 0 ); //
for( x = 0; x < 640; x++ ){ //
d = x & 0x0ff; // 0..255の値にする
*p++ = d; // 画素として書き込む
} // 1行分パターン画像作成
pimgA->subset( pPatImg, 0, 0, 640, 1 ); // 先頭行の部分画像
for( y = 1; y < 480; y++ ){ //
pimgB->subset( pPatImg, 0, y, 640, // 2行以降の任意場所の
1 ); // 部分画像
FUN.Copy( pimgA, pimgB ); // 先頭行のパターン画像を2行目以降にコピー
} // これで横方向インクリメントパターン画像作成
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap( "testFile5.bmp", pPatImg );
//LUT変換サンプル:画素データ反転
pLut->SetByte( lut, 256, 1 ); // LUT実体を「TypeArray*」にセット
for( i = 0; i < 256; i++ ){ // 極、普通の教科書的な記載でlut[0..255]の中の
lut[i] = 255 - i; // パターンが「255..0」と8ビット内データで
} // 反転している事が分かるでしょう!
pWimg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pWimg, 0 ); // 画像メモリ「0クリア」
FUN.Convert( pPatImg, pWimg, pLut ); // 今回紹介する関数「Convert()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile6.bmp", pWimg );
// 典型的な2値化画像生成方法での処理
// サンプルパターン画像作成
FUN.Clear( pPatImg, 100 ); // 画像メモリ「画素データ100でクリア」
pimgA->subset( pPatImg, 100, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
50, 30 ); // 部分画像を用意
FUN.Clear( pimgA, 200 ); // 部分画像を画素データ200でクリア
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile7.bmp", pPatImg );
// 典型的な2値化しきい値を算出する方法
FUN.HistogramImage( pPatImg, hgm ); // 画像の画素濃度ヒストグラム産出
t = FUN.GetBinOotu( hgm, 256 ); // 大津法と言う画像処理で一般的な産出手法で
// 濃度ヒストグラム・データから、しきい値産出
//LUT変換サンプル:2値化
FUN.up_fill( 0, lut, t ); // LUT配列の「0..t」を0クリアし
FUN.up_fill( 255, &lut[t], 256 - t ); // 「t..255」迄を255で書き込む事で
// 2値化LUTパターン作成
FUN.Convert( pPatImg, pWimg, pLut ); // 今回紹介する関数「Convert()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile8.bmp", pWimg );
// 後始末
pPatImg->freeMem(); // 画像メモリ解放
pWimg->freeMem(); // 画像メモリ解放
}
使用例の変数等の定義
{
TypeArray PatImg; // 画像型変数
// :編か前のパターン画像
TypeArray* pPatImg=&PatImg; // 上記へのポインタ
TypeArray Wimg; // 画像型変数
//:フィル書き込み用
TypeArray* pWimg=&Wimg; // 上記へのポインタ
//:部分画像用
TypeArray* pimgA=&PatImg; // 部分画像用
TypeArray* pimgB=&PatImg; // 部分画像用
//:LUT配列用
TypeArray ALut; // LUT型変数
TypeArray* pLut=&ALut; // LUT型ポインタ変数
BYTE lut[256]; // LUT実体
long hgm[256]; // ヒストグラム算出データ配列
BYTE* p; // 画像画素へのポインタ
int i; // ループカウンタ
int x; // X座標
int y; // Y座標
int d; // 画素データ
int t; // 2値化しきい値
int sti; // ステータス情報
「TypeArray PatImg;」は、例文サンプル用のパターン画像
で「TypeArray」型変数として定義する事で画像処理の画像
を意味する事を理解して下さい
「TypeArray* pPatImg=&PatImg;」は、画像処理関数では、
取り扱いが「TypeArray」型変数より、「TypeArray*」型と
ポインタの形の方が取り扱いし易く設計しているので一旦、
「TypeArray* pPatImg;」とポインタ変数を定義し、初期設
定で「TypeArray* pPatImg=&PatImg;」と割り当てて居る事
を理解して下さい!
「TypeArray Wimg;」は、例文サンプル用のパターン画像を
LUT変換した結果画像を示し、「W」と有るのは結果画像
を画像ファイルに書き込む事を意味します!
「TypeArray* pWimg=&Wimg;」は、ポインタの形の方が取り
扱いし易く設計しているので一旦、「TypeArray* pWimg」と
ポインタ変数を定義し、初期設定で
「TypeArray* pWimg=&Wimg;」と割り当てて居る事を理解
して下さい!
「TypeArray* pimgA=&Patimg;」は、パターン画像作成時の
部分画像の定義用です!ここは、1行目の部分画像を表し
ます!
「TypeArray* pimgB=&Patimg;」は、パターン画像作成時の
部分画像の定義用です!ここは、2行目以降の部分画像を
表します!
「TypeArray ALut;」は、「TypeArray」型変数ですが、
画像で無く、一次元配列を示す事に注意して下さい!
「TypeArray* pLut=&ALut;」でコノ画像処理関数では、
取り扱いが「TypeArray」型変数より、「TypeArray*」型と
ポインタの形の方が取り扱いし易く設計しているので一旦、
「TypeArray* pLut=&ALut;」とポインタ変数を定義し、
初期設定で「TypeArray* pLut=&ALut;」と割り当てて居る
事を理解して下さい!
「BYTE lut[256];」は、「BYTE型=unsigned char型」で
定義したC言語で教科書的に表現している配列データです!
「long hgm[256];」は、画像画素の濃度(画素の値)ヒス
トグラムデータを格納する配列で「long型」と4バイト整数
型の配列です!画像処理の中でカメラで撮影した自然画像か
ら、処理する為の2値化画像を得る為の2値化しきい値を
作成する処理で使用頻度が高いので特別に独立したクラス「
Histogram」を用意して居ます!ライブラリ関数の
元に成ったADS社の画像処理装置IP-4には、特別に
一枚の基板でオプションボードとしてリアルタイムでヒス
トグラム産出する機能を備えて居ました?!その為にも
特別に独立したクラスとして作成したのが歴史的経緯です
が、2値化する為のしきい値を得るのが主な目的ですので
ここで説明する方式は、アッチコッチで使用する方式なので
使用方法は、覚えてね!
「BYTE* p;」は、画素単位で操作する為のポインタ変数
「int i;」は、ループカウンタ(インデックス)
「int x;」は、パターン画像作成時のX座標変数
「int y;」は、パターン画像作成時のY座標変数
「int d;」は、画素データ変数
「int t;」は、2値化しきい値
「int sti;」は、エラーコード
パターン画像作成
pPatImg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pPatImg, 0 ); // 画像メモリ「0クリア」
p = getPtrByte( 0, 0 ); //
for( x = 0; x < 640; x++ ){ //
d = x & 0x0ff; // 0..255の値にする
*p++ = d; // 画素として書き込む
} // 1行分パターン画像作成
pimgA->subset( pPatImg, 0, 0, 640, 1 ); // 先頭行の部分画像
for( y = 1; y < 480; y++ ){ //
pimgB->subset( pPatImg, 0, y, 640, // 2行以降の任意場所の
1 ); // 部分画像
FUN.Copy( pimgA, pimgB ); // 先頭行のパターン画像を2行目以降にコピー
} // これで横方向インクリメントパターン画像作成
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap( "testFile5.bmp", pPatImg );
「pPatImg->MallocBYTE(640,480);」は、「TypeArray」
クラスのメソッド(メンバー関数)「MallocBYTE()」で
動的に画像メモリを取得!
「FUN.Clear(pPatImg,0);」は、画像画素全体を0クリア
「p=pPatImg->getPtrByte(0,0);」は、画像メモリの
座標(0,0)の画素ポインタ値を取得し変数「p」格納
「for(x=0;x<640;x++){d=x&0x0ff;*p++ = d;}」は、極、
教科書的≪実行速度的には改良の余地が有るが分かり易い
筈≫なのでコードを読んで頂ければ分かる様にインクリメ
ント≪ここでC言語のビット毎演算(『&演算子』使用)に
不慣れな読者様も居ると考えて解説【動作的には変数「x」
を256で除算した余りが「x&0x0ff」と成ります】、更に
「0x0ff」とヘキサデシマル記法で数値を記載したのは、
直接C言語文法では2進数表記が出来無いが「0x0ff」=
2進数表記「11111111」と8ビット=1バイト全てのビット
が「1」に成った事を私の様なデジタル電子回路技術者上が
りのプログラマーには分かり易いからです!≫
「pimgA->subset(pPatImg,0,0,640,1);」は、先頭行≪画像
の範囲が「(0,0)..(639,0)」の部分画像を意味≫
「for(y=1;y<480;y++){・・ループ中身・・」は、極、
教科書的な説明記述でY座標を示す変数「y」を「1..479」
と繰り返しループ中身
「pimgB->subset(pPatImg,0,y,640,1);
FUN.Copy(pimgA,pimgB);」を繰り返します!ループ中身
「pimgB->subset(pPatImg,0,y,640,1);」は、部分画像とし
範囲が「(0,y)..(639,y)」の部分画像を意味≪Y座標
を変数「y」の値に変更した行を示す≫し、その部分画像へ
「FUN.Copy(pimgA,pimgB);」と先頭行の画像画素パターンを
コピーする事で横(X座標)方向インクリメント・パターン
画像を作成します!
「FUN.WriteBitmap("testFile5.bmp",pPatImg);」は、
ファイル名「"testFile5.bmp"」でBMP形式の画像ファイ
ル作成!
LUT変換サンプル:画素データ反転
//LUT変換サンプル:画素データ反転
pLut->SetByte( lut, 256, 1 ); // LUT実体を「TypeArray*」にセット
for( i = 0; i < 256; i++ ){ // 極、普通の教科書的な記載でlut[0..255]の中の
lut[i] = 255 - i; // パターンが「255..0」と8ビット内データで
} // 反転している事が分かるでしょう!
pWimg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pWimg, 0 ); // 画像メモリ「0クリア」
FUN.Convert( pPatImg, pWimg, pLut ); // 今回紹介する関数「Convert()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile6.bmp", pWimg );
「pLut->SetByte(lut,256,1);」は、「TypeArray」クラスの
メソッド(メンバー関数)で「TypeArray型」のポインタ
変数「pLut」に「BYTE lut[256];」配列を割り当てる!
「for(i=0;i<256;i++){lut[i]=255-i;}」は、教科書的≪
実行速度的には改良の余地が有るが分かり易い筈≫なので
コードを読んで頂ければ分かる様にデクリメントパターン≪
降順に255..0と配列「BYTE lut[256];」の中身を生成≫に成
る事は理解して下さい!補足≪「255-i」で「0⇒255、
1⇒254、2⇒253・・・・254⇒1、255⇒0」に成る≫
「pWimg->MallocBYTE(640,480);FUN.Clear(pWimg,0);」は、
「TypeArray」クラスのメソッド(メンバー関数)
「MallocBYTE()」で動的に画像メモリを取得!
「FUN.Clear(pWimg,0);」は、画像画素全体を0クリアと
初期設定する事で次のLUT変換実行前に0クリアして居る
事を説明の為に示します!
「FUN.Convert(pPatImg,pWimg,pLut);」は、この関数
「Convert()」の説明がココの例文です!そして最初の
LUT変換例として先ほど作成したLUT(pLut)で
元の横(X座標)方向インクリメント・パターン画像を
ここのLUT(pLut)で変換し、結果画像を「pWimg」で示
す結果画像を作成します!初期設定で0クリアして居るので
LUT変換を実行したら、0クリア画像と異なる事が分かる
でしょう!と言う事で分かる為に
「FUN.WriteBitmap("testFile6.bmp",pWimg);」は、
LUT変換結果画像「pWimg」を
ファイル名「"testFile6.bmp"」でBMP形式の画像ファイ
ル作成!
典型的な2値化画像生成方法での処理:
サンプルパターン画像作成
// 典型的な2値化画像生成方法での処理
// サンプルパターン画像作成
FUN.Clear( pPatImg, 100 ); // 画像メモリ「画素データ100でクリア」
pimgA->subset( pPatImg, 100, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
50, 30 ); // 部分画像を用意
FUN.Clear( pimgA, 200 ); // 部分画像を画素データ200でクリア
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile7.bmp", pPatImg );
「FUN.Clear(pPatImg,100);」は、画像画素全体を画素の
数値「100」≪黒(0)より明るく白(255)より暗い輝度
画素≫でクリア!
「pimgA->subset(pPatImg,100,200,50,30);」は、パターン
画像の部分画像とし座標範囲「(100,200)・・(149,29)」の
部分画像とし「pimgA」をセットします!そして
「FUN.Clear(pimgA,200);」は、その部分画像「pimgA」画素
全体を画素の数値「200」≪pPatImgの輝度(100)より明る
く白(255)より暗い輝度画素≫でクリア!
「FUN.WriteBitmap("testFile7.bmp",pPatImg);」は、
ファイル名「"testFile7.bmp"」で今回のサンプル画像
パターンをBMP形式の画像ファイル作成!
典型的な2値化しきい値を算出する方法
// 典型的な2値化しきい値を算出する方法
FUN.HistogramImage( pPatImg, hgm ); // 画像の画素濃度ヒストグラム産出
t = FUN.GetBinOotu( hgm, 256 ); // 大津法と言う画像処理で一般的な産出手法で
// 濃度ヒストグラム・データから、しきい値産出
ここでは、画像認識処理典型的な方式≪ビデオカメラで撮影
した画像【★説明を簡単にする為に画素がモノクロ(白黒)
輝度濃淡画像を撮影した事で説明★】を1バイト(8ビット
)画素の画像メモリに格納後⇒異物混入検査とし2値化処理
を行う典型的な手順を説明≫として濃度ヒストグラムを画像
から算出し、その統計学的な方式≪ヒストグラム=度数分布
グラフを意味します「量的データの分布の様子を見るのに用
いられます。データをいくつかの階級に分け、度数分布表を
作成してからグラフを作成」と統計学で使用される手法です
が、これで分布が低い(画素の値が小さい)塊と高い(画素
の値が大きい)塊に分けられると言う依り、二つに分けるの
に都合の良い「しきい値」を作成して、その「しきい値」で
高低に分ける事です!≫⇒ADS社の画像処理装置IP-4
は、使用頻度が高いと考えてハードウェアでリアルタイム(
アナログ放送カメラ速度で1画面撮影時間≒1/30秒)で
「しきい値」を算出する機構を特別にオプションボードとし
販売し、その制御プログラムを私は作成して居ましたので、
画像処理ライブラリにも独立した
クラス「Histogram」を分離して用意したと別の
解説クラスHistogram(××)』で解説して行きま
すので少し、後の説明順に成るので「典型的な使用方法」だ
け紹介します!
「FUN.HistogramImage(pPatImg,hgm);」は、ヒストグラム
算出関数「HistogramImage()」で画像「pPatImg」を処理し
結果データを「long hgm[256];」とサイズ「256」の配列に
格納≪1バイト(8ビット)画素が対象なので画素の値が
「0..255」と256種類しか度数分布データで無いからで、
更に「long型」でデータ配列を使うのは、例えば、大きな
画像4K「4096×4096画素数=16777216」
と大きな数に成るので頻度なので最大の値は画像のサイズに
匹敵するから大きなサイズが格納出来る事が必要なので
「long型」を使用≫、そして算出したヒストグラムデータ
から「大昔の画像処理ライブラリ(フォートランで記述した
画像処理定番のスパイダーと呼ばれるソースコードから定番
処理の大津法『恐らく開発した学者・技術者の名前と思える
』)」の大津法が、私の応用として各種の工場等の検査装置
に使用でも定番中の定番として上手く行った2値化技法なの
でコノ大津法を紹介?!
「t=FUN.GetBinOotu(hgm,256);」は、大津法で2値化
しきい値をヒストグラムデータ「hgm」から算出します!
LUT変換サンプル:2値化
//LUT変換サンプル:2値化
FUN.up_fill( 0, lut, t ); // LUT配列の「0..t」を0クリアし
FUN.up_fill( 255, &lut[t], 256 - t ); // 「t..255」迄を255で書き込む事で
// 2値化LUTパターン作成
FUN.Convert( pPatImg, pWimg, pLut ); // 今回紹介する関数「Convert()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile8.bmp", pWimg );
「FUN.up_fill(0,lut,t);」は、2値化しきい値「t」までの
値でLUT変換用のLUT「BYTE lut[256];」の下位を
値「0」にして
「FUN.up_fill(255,&lut[t],256-t);」は、上位を値「255」
にLUT2値化変換パターンを作成し、
「FUN.Convert(pPatImg,pWimg,pLut);」は、2値化変換を
行います!そして
「FUN.WriteBitmap("testFile8.bmp",pWimg);」は、
LUT変換結果画像「pWimg」を
ファイル名「"testFile8.bmp"」でBMP形式の画像ファイ
ル作成!
後始末
// 後始末
pPatImg->freeMem(); // 画像メモリ解放
pWimg->freeMem(); // 画像メモリ解放
「pPatImg->freeMem(); 」は、パターン画像動的メモリ取得
したのでメモリ解放
「pWimg->freeMem();」は、LUT変換後の結果画像動的
メモリ取得したのでメモリ解放
★注意★
上記例文で注意して欲しいのは、LUT1次元配列「plut」
には「->freeMem()」でメモリを解放シナイ事です!
お判りと思いますが、「pLut->SetBYTE ()」と「lut[]」の
実メモリをセットしているのでメモリ取得は「pPatImg」と
「pWimg」の画像メモリは行い!一次元配列「pLut」は、
メモリ取得を行ってイナイので取得して無いメモリを開放
するとシステムが変に成る事を考えて欲しい?!
作成した画像ファイル
★注意★
本当は、ソースコードを実行して作れば、カクカクした階段
増加で無く滑らかなグラデーション諧調に成るのですが、
私が、ド貧乏人に成り、無料コンパイラも使用期限を越えて
プログラムを動作出来る環境が有りませんので仕方ないので
描画ソフト「ペイント」で手作業で作成した物ですが、
滑らかなグラデーション諧調の画像と考えて下さい!
尚、解説『投げ銭方式ライブラリのダウンロード』での
有料(投げ銭)まで読んで頂ければ、私に開発環境を恵んで
頂く費用を出す事に成ります!出来たら、開発環境の費用を
お恵み下さい!
取り敢えず、本日(3月18)の講義は、ココまでにし
ます!何だか、疲れたのだ!受講されている方も、お疲れと
思いますのでココまでとします!
まだ、Noteサーバーが快適に動く分量ですから、
引き続き、この文章に続きは、載せます!
2024年3月19続講
(4-14-8)関数「int ConvertByte(TypeArray* ps,
TypeArray* pd,BYTE *lut){・・・}」の説明
int CopyClear::ConvertByte(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
BYTE *lut // LUTポインタ
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int hv; // 水平/垂直幅
ws = ps->w; // 単位幅取出し:S
if( ws < 1 || ws > 2 ){ // Sが1か2Byte以
return( STI_ARY_5 - 10 ); // 外は左記を返す
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( cvt_byte_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( cvt_byte_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
(4-14-8-A)関数「int ConvertByte()」の【関数名】説明
「Convert」は、英単語「Convert」として変換するの意味で
す!ここの画像処理では、LUT≪ルックアップテーブル≫
を使用したS(元)画像⇒LUT変換⇒D(結果)画像への
画素データをLUT≪Lookuptable≫として一時的に変換す
る技法を使用して変換する関数です!
「BYTE 」は、勿論、「BYTE型」と画素の単位が1バイト
無符号を示し、結果画像が「BYTE型」に成る変換関数です!
(4-14-8-B)関数「int ConvertByte()」の【返値】説明
int CopyClear::ConvertByte(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
BYTE *lut // LUTポインタ
){
この関数自体でエラーチェックするのは、
仮引数「TypeArray* ps,」詰り、S画像画素単位が、
1バイトか2バイト単位で無ければ、
エラー「STI_ARY_5-10」と最初の「TypeArray* 」に問題が
有った事を示し、後は、サブルーチンの
「cvt_byte_b(ps,pd,lut)」及び「cvt_byte_w(ps,pd,lut)」
で値を返します!
(4-14-8-C)関数「int ConvertByte()」の【仮引数】説明
int CopyClear::ConvertByte(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
BYTE *lut // LUTポインタ
){
「TypeArray* ps,」は、S(元)画像情報へのポインタを
意味します!
「TypeArray* pd,」は、D(結果)画像情報へのポインタを
意味します!
「BYTE *lut」は、LUTの実体へのポインタとして、この
関数では「BYTE *」型、詰り符号無し1バイト単位です!
★注意★
LUTのサイズは、指定しませんが、暗黙の了解として
S(元)画像が、1バイト単位画素なら「256」バイト
用意しているし、2バイト単位画素なら「画素データ最大値
」まで用意して居る事が前提条件です!
(4-14-8-D)関数「int ConvertByte()」の【アルゴリズム】説明
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int hv; // 水平/垂直幅
ws = ps->w; // 単位幅取出し:S
if( ws < 1 || ws > 2 ){ // Sが1か2Byte以
return( STI_ARY_5 - 10 ); // 外は左記を返す
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( cvt_byte_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( cvt_byte_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
ローカル変数
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int hv; // 水平/垂直幅
「TypeArray s;」は、有効範囲サイズ補正用のS画像情報
「TypeArray d;」は、有効範囲サイズ補正用のD画像情報
「int ws;」は、S画像の画素単位を扱い易くした変数
「int hv;」は、仮引数「TypeArray* ps,TypeArray* pd」
の水平幅/垂直幅の最小値(SD両者の有効幅)算出用です
アルゴリズムコード
ws = ps->w; // 単位幅取出し:S
if( ws < 1 || ws > 2 ){ // Sが1か2Byte以
return( STI_ARY_5 - 10 ); // 外は左記を返す
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( cvt_byte_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( cvt_byte_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
「ws=ps->w;」は、S画像単位幅を扱い易く取り出し!
「if(ws<1||ws>2){return(STI_ARY_5-10);}」は、
条件「(ws<1||ws>2)」、詰り、S画像画素単位が、
1バイトか2バイト単位で無ければ、エラーとして
「STI_ARY_5-10」と最初の仮引数「TypeArray* ps,」が
エラーを起こしたと間数値を返し関数終了!
「s=*ps;ps=&s;d=*pd;pd=&d;」は、SD両者の画像情報を
ローカル変数に一旦、移し、有効幅に補正する準備!
「hv=(ps->h<pd->h)?ps->h:pd->h;」は、水平幅の有効幅(
SD両者の最小値)を算出ト!
「ps->h=hv;pd->h=hv;」は、SD両者の水平幅を補正!
「hv=(ps->v<pd->v)?ps->v:pd->v;ps->v=hv;pd->v=hv;」
は、SD両者の垂直幅を算出し両者を補正!
「if(ws==1){・・成立・・}else{・・非成立}」は、
条件「(ws==1)」詰り、S画像が1バイト単位ならば、
成立「return(cvt_byte_b(ps,pd,lut));」とサブルーチン
「cvt_byte_b(ps,pd,lut)」で処理し、関数返値もサブルー
チンの値を返し関数終了!
「}else{」と非成立「return(cvt_byte_w(ps,pd,lut));}」
でサブルーチン「cvt_byte_w(ps,pd,lut)」で処理し、
関数返値もサブルーチンの値を返し関数終了!
(4-14-8-E)関数「int ConvertByte(」の【使用例】説明
{
TypeArray PatImg; // 画像型変数
// :編か前のパターン画像
TypeArray* pPatImg=&PatImg; // 上記へのポインタ
TypeArray Wimg; // 画像型変数
//:フィル書き込み用
TypeArray* pWimg=&Wimg; // 上記へのポインタ
//:部分画像用
TypeArray* pimgA=&PatImg; // 部分画像用
TypeArray* pimgB=&PatImg; // 部分画像用
BYTE lut[256]; // LUT実体
long hgm[256]; // ヒストグラム算出データ配列
BYTE* p; // 画像画素へのポインタ
int i; // ループカウンタ
int j; // ループカウンタ
int x; // X座標
int y; // Y座標
int d; // 画素データ
int t; // 2値化しきい値
int sti; // ステータス情報
pPatImg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pPatImg, 0 ); // 画像メモリ「0クリア」
p = pPatImg->getPtrByte( 0, 0 ); //
for( x = 0; x < 640; x++ ){ //
d = x & 0x0ff; // 0..255の値にする
*p++ = d; // 画素として書き込む
} // 1行分パターン画像作成
pimgA->subset( pPatImg, 0, 0, 640, 1 ); // 先頭行の部分画像
for( y = 1; y < 480; y++ ){ //
pimgB->subset( pPatImg, 0, y, 640, // 2行以降の任意場所の
1 ); // 部分画像
FUN.Copy( pimgA, pimgB ); // 先頭行のパターン画像を2行目以降にコピー
} // これで横方向インクリメントパターン画像作成
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap( "testFile5.bmp", pPatImg );
//LUT変換サンプル:画素データ反転
for( i = 0; i < 256; i++ ){ // 極、普通の教科書的な記載でlut[0..255]の中の
lut[i] = 255 - i; // パターンが「255..0」と8ビット内データで
} // 反転している事が分かるでしょう!
pWimg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pWimg, 0 ); // 画像メモリ「0クリア」
FUN.ConvertByte( pPatImg, pWimg, lut ); // 今回紹介する関数「ConvertByte()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile6.bmp", pWimg );
// 典型的な2値化画像生成方法での処理
// サンプルパターン画像作成
FUN.Clear( pPatImg, 100 ); // 画像メモリ「画素データ100でクリア」
pimgA->subset( pPatImg, 100, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
50, 30 ); // 部分画像を用意
FUN.Clear( pimgA, 200 ); // 部分画像を画素データ200でクリア
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile7.bmp", pPatImg );
// 典型的な2値化しきい値を算出する方法
FUN.HistogramImage( pPatImg, hgm ); // 画像の画素濃度ヒストグラム産出
t = FUN.GetBinOotu( hgm, 256 ); // 大津法と言う画像処理で一般的な産出手法で
// 濃度ヒストグラム・データから、しきい値産出
//LUT変換サンプル:2値化
FUN.up_fill( 0, lut, t ); // LUT配列の「0..t」を0クリアし
FUN.up_fill( 255, &lut[t], 256 - t ); // 「t..255」迄を255で書き込む事で
// 2値化LUTパターン作成
FUN.ConvertByte( pPatImg, pWimg, lut ); // 今回紹介する関数「ConvertByte()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile8.bmp", pWimg );
// 後始末
pPatImg->freeMem(); // 画像メモリ解放
pWimg->freeMem(); // 画像メモリ解放
}
★注意★
上記例文で注意して欲しいのは、LUT一次元配列「lut[]」
と直接、C言語表現での実テーブルを使用して居る事です!
場合に依っては、関数「int Convert()」を使用した場合
より効率的に成る為にコノ関数が用意された事が分かる
でしょう!
☆備考☆
関数「int Convert()」・「int ConvertByte()」も実際に
サブルーチンの奥では同じ関数で実際の処理は行い!
関数を呼び出す形式が必要に応じて選べる様にしています!
(4-14-9)関数「int ConvertWord(
TypeArray* ps,TypeArray* pd,
short *lut){・・・}」の説明
int CopyClear::ConvertWord(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
short *lut // LUTポインタ
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int hv; // 水平/垂直幅
ws = ps->w; // 単位幅取出し:S
if( ws < 1 || ws > 2 ){ // Sが1か2Byte以
return( STI_ARY_5 - 10 ); // 外は左記を返す
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( cvt_word_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( cvt_word_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
(4-14-9-A)関数「int ConvertWord()」の【関数名】説明
「Convert」は、英単語「Convert」として変換するの意味で
す!ここの画像処理では、LUT≪ルックアップテーブル≫
を使用したS(元)画像⇒LUT変換⇒D(結果)画像への
画素データをLUT≪Lookuptable≫として一時的に変換す
る技法を使用して変換する関数です!
「Word」は、勿論、「short型」と画素の単位が2バイトを
示し、結果画素が「2バイト」に成る変換関数です!
(4-14-9-B)関数「int ConvertWord()」の【返値】説明
int CopyClear::ConvertWord(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
short *lut // LUTポインタ
){
この関数自体でエラーチェックするのは、
仮引数「TypeArray* ps,」詰り、S画像画素単位が、
1バイトか2バイト単位で無ければ、
エラー「STI_ARY_5-10」と最初の「TypeArray* 」に問題が
有った事を示し、後は、サブルーチンの
「cvt_word_b(ps,pd,lut)」及び「cvt_word_w(ps,pd,lut)」
で値を返します!
(4-14-9-C)関数「int ConvertWord()」の【仮引数】説明
int CopyClear::ConvertWord(
TypeArray *ps, // S配列
TypeArray *pd, // D配列
short *lut // LUTポインタ
){
「TypeArray* ps,」は、S(元)画像情報へのポインタを
意味します!
「TypeArray* pd,」は、D(結果)画像情報へのポインタを
意味します!
「short *lut」は、LUTの実体へのポインタとして、この
関数では「short *」型、詰り2バイト単位です!
(4-14-9-D)関数「int ConvertWord()」の【アルゴリズム】説明
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int hv; // 水平/垂直幅
ws = ps->w; // 単位幅取出し:S
if( ws < 1 || ws > 2 ){ // Sが1か2Byte以
return( STI_ARY_5 - 10 ); // 外は左記を返す
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( cvt_word_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( cvt_word_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
ローカル変数
){
TypeArray s; // S:サイズ補正後
TypeArray d; // D:サイズ補正後
int ws; // S配列の単位幅
int hv; // 水平/垂直幅
「TypeArray s;」は、有効範囲サイズ補正用のS画像情報
「TypeArray d;」は、有効範囲サイズ補正用のD画像情報
「int ws;」は、S画像の画素単位を扱い易くした変数
「int hv;」は、仮引数「TypeArray* ps,TypeArray* pd」
の水平幅/垂直幅の最小値(SD両者の有効幅)算出用です
アルゴリズムコード
ws = ps->w; // 単位幅取出し:S
if( ws < 1 || ws > 2 ){ // Sが1か2Byte以
return( STI_ARY_5 - 10 ); // 外は左記を返す
} //
s = *ps; // S配列を補正する
ps = &s; // ため情報をコピー
d = *pd; // D配列を補正する
pd = &d; // ため情報をコピー
hv = ( ps->h < pd->h ) ? ps->h : pd->h; // 水平幅を最小で
ps->h = hv; // 補正
pd->h = hv; //
hv = ( ps->v < pd->v ) ? ps->v : pd->v; // 垂直幅を最小で
ps->v = hv; // 補正
pd->v = hv; //
if( ws == 1 ){ // Sが1Byte整数時
return( cvt_word_b( ps, pd, lut ) ); // 左記で LUT変換
}else{ // Sが2Byte整数時
return( cvt_word_w( ps, pd, lut ) ); // 左記で LUT変換
} //
}
「ws=ps->w;」は、S画像単位幅を扱い易く取り出し!
「if(ws<1||ws>2){return(STI_ARY_5-10);}」は、
条件「(ws<1||ws>2)」、詰り、S画像画素単位が、
1バイトか2バイト単位で無ければ、エラーとして
「STI_ARY_5-10」と最初の仮引数「TypeArray* ps,」が
エラーを起こしたと間数値を返し関数終了!
「s=*ps;ps=&s;d=*pd;pd=&d;」は、SD両者の画像情報を
ローカル変数に一旦、移し、有効幅に補正する準備!
「hv=(ps->h<pd->h)?ps->h:pd->h;」は、水平幅の有効幅(
SD両者の最小値)を算出ト!
「ps->h=hv;pd->h=hv;」は、SD両者の水平幅を補正!
「hv=(ps->v<pd->v)?ps->v:pd->v;ps->v=hv;pd->v=hv;」
は、SD両者の垂直幅を算出し両者を補正!
「if(ws==1){・・成立・・}else{・・非成立}」は、
条件「(ws==1)」詰り、S画像が1バイト単位ならば、
成立「return(cvt_word_b(ps,pd,lut));」とサブルーチン
「cvt_word_b(ps,pd,lut)」で処理し、関数返値もサブルー
チンの値を返し関数終了!
「}else{」と非成立「return(cvt_word_w(ps,pd,lut));}」
でサブルーチン「cvt_word_w(ps,pd,lut)」で処理し、
関数返値もサブルーチンの値を返し関数終了!
(4-14-9-E)関数「int ConvertWord(」の【使用例】説明
{
TypeArray PatImg; // 画像型変数
// :編か前のパターン画像
TypeArray* pPatImg=&PatImg; // 上記へのポインタ
TypeArray Wimg; // 画像型変数
//:フィル書き込み用
TypeArray* pWimg=&Wimg; // 上記へのポインタ
//:部分画像用
TypeArray* pimgA=&PatImg; // 部分画像用
TypeArray* pimgB=&PatImg; // 部分画像用
short lut[256]; // LUT実体
long hgm[256]; // ヒストグラム算出データ配列
short* p; // 画像画素へのポインタ
int i; // ループカウンタ
int j; // ループカウンタ
int x; // X座標
int y; // Y座標
int d; // 画素データ
int t; // 2値化しきい値
int sti; // ステータス情報
pPatImg->MallocShort( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pPatImg, 0 ); // 画像メモリ「0クリア」
p = pPatImg->getPtrShort( 0, 0 ); //
for( x = 0; x < 640; x++ ){ //
d = x; // 0..639の値にする
*p++ = d; // 画素として書き込む
} // 1行分パターン画像作成
pimgA->subset( pPatImg, 0, 0, 640, 1 ); // 先頭行の部分画像
for( y = 1; y < 480; y++ ){ //
pimgB->subset( pPatImg, 0, y, 640, // 2行以降の任意場所の
1 ); // 部分画像
FUN.Copy( pimgA, pimgB ); // 先頭行のパターン画像を2行目以降にコピー
} // これで横方向インクリメントパターン画像作成
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap( "testFile5.bmp", pPatImg );
//LUT変換サンプル:画素データ反転
for( i = 0; i < 256; i++ ){ // 極、普通の教科書的な記載でlut[0..255]の中の
lut[i] = 255 - i; // パターンが「255..0」と8ビット内データで
} // 反転している事が分かるでしょう!
pWimg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pWimg, 0 ); // 画像メモリ「0クリア」
FUN.Convertshort( pPatImg, pWimg, lut );// 今回紹介する関数「Convertshort()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile6.bmp", pWimg );
// 典型的な2値化画像生成方法での処理
// サンプルパターン画像作成
FUN.Clear( pPatImg, 100 ); // 画像メモリ「画素データ100でクリア」
pimgA->subset( pPatImg, 100, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
50, 30 ); // 部分画像を用意
FUN.Clear( pimgA, 200 ); // 部分画像を画素データ200でクリア
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile7.bmp", pPatImg );
// 典型的な2値化しきい値を算出する方法
FUN.HistogramImage( pPatImg, hgm ); // 画像の画素濃度ヒストグラム産出
t = FUN.GetBinOotu( hgm, 256 ); // 大津法と言う画像処理で一般的な産出手法で
// 濃度ヒストグラム・データから、しきい値産出
//LUT変換サンプル:2値化
FUN.up_fill( 0, lut, t ); // LUT配列の「0..t」を0クリアし
FUN.up_fill( 255, &lut[t], 256 - t ); // 「t..255」迄を255で書き込む事で
// 2値化LUTパターン作成
FUN.Convertshort( pPatImg, pWimg, lut );// 今回紹介する関数「Convertshort()」でLUT変換
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile8.bmp", pWimg );
// 後始末
pPatImg->freeMem(); // 画像メモリ解放
pWimg->freeMem(); // 画像メモリ解放
}
★注意★
上記例文で注意して欲しいのは、LUT一次元配列「lut」
と直接、C言語表現での実テーブルを使用して居る事です!
場合に依っては、関数「int Convert()」を使用した場合
より効率的に成る為にコノ関数が用意された事が分かる
でしょう!
☆備考☆
関数「int Convert()」・「int ConvertWord()」も実際に
サブルーチンの奥では同じ関数で実際の処理は行い!
関数を呼び出す形式が必要に応じて選べる様にしています!
★補足★
通常、良く使用される撮像用のデジタルカメラの輝度濃度
データは、1バイト(8ビット=0:黒⇒255:白)ですが、
特殊用途で更にダイナミックレンジが広い特殊カメラ≪安定
撮影サセル為にペルチェ素子+水冷冷却システムと多少高価
なカメラも存在し、使用経験が有るものは英国アンドリュー
のカメラで画素データが2バイト(10ビット=0:黒⇒
1023:白)も存在します≫、コノ様なカメラの映像も加工
出来る為に元画像が、8ビットを越えた例として例文サンプ
ル作成しました!
取り敢えず、本日(3月19)の講義は、ココまでにし
ます!何だか、疲れたのだ!受講されている方もお疲れと
思いますのでココまでとします!
まだ、Noteサーバーが快適に動く分量ですから、
引き続き、この文章に続きは、載せます!
2024年3月20続講
(4-14-10)関数「int ConvertLine(
TypeArray* ps,TypeArray* pd,
BYTE tbl[],int mode){・・・}」の説明
int CopyClear::ConvertLine(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
BYTE tbl[], // テーブル「1..255」
int mode // 0→水平方向,1→垂直方向
){
BYTE* lut; // LUTバッファー
TypeArray subS; // 部分画像S
TypeArray subD; // 部分画像D
int h; // 水平幅
int v; // 垂直幅
int k; // 割合(255に対して)
int x; // X座標
int x0; // X座標:始点
int x1; // X座標:終点
int y; // Y座標
int y0; // Y座標:始点
int y1; // Y座標:終点
int sti; // ステータス情報
sti = CheckImageByte( ps ); // 元画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
sti = CheckImageByte( pd ); // 結果画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
h = ( ps->h < pd->h ) ? ps->h : pd->h; // 有効水平幅算出
v = ( ps->v < pd->v ) ? ps->v : pd->v; // 有効垂直幅算出
if( mode == 0 ){ // 水平方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * h ); // LUTを256*水平幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, h ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineX( ps, pd, lut ); // 水平方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
}else{ // 垂直方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * v ); // LUTを256*垂直幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, v ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineY( ps, pd, lut ); // 垂直方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
} // ※大規模LUT確保失敗時の処理※
lut = (BYTE*)malloc( sizeof(BYTE) * 256 ); // LUTを256で確保
if( lut == 0 ){ // 確保失敗時は
return( STI_MEM ); // 左記を返す
} //
if( mode == 0 ){ // 水平方向なら
x0 = 0; // X座標:始点セット
k = tbl[x0]; // 先頭の割合取り出し
for( x = 1; x < h; x++ ){ // X座標を繰り返し
if( k != tbl[x] ){ // 同一割合で無くなれば
x1 = x - 1; // X座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, x0, 0, x1 - x0 + 1, v ); // 部分画像Sセット
subD.subset( pd, x0, 0, x1 - x0 + 1, v ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
x0 = x; // X座標:始点更新
k = tbl[x0]; // 割合更新
} //
} //
}else{ // 垂直方向なら
y0 = 0; // Y座標:始点セット
k = tbl[y0]; // 先頭の割合取り出し
for( y = 1; y < v; y++ ){ // Y座標を繰り返し
if( k != tbl[y] ){ // 同一割合で無くなれば
y1 = y - 1; // Y座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, 0, y0, h, y1 - y0 + 1 ); // 部分画像Sセット
subD.subset( pd, 0, y0, h, y1 - y0 + 1 ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
y0 = y; // Y座標:始点更新
k = tbl[y0]; // 割合更新
} //
} //
} //
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですので
ライブラリの外から使用可能です!
(4-14-10-A)関数「int ConvertLine()」の【関数名】説明
「Convert」は、英単語「Convert」として変換するの意味で
す!ここの画像処理では、LUT≪ルックアップテーブル≫
を使用したS(元)画像⇒LUT変換⇒D(結果)画像への
画素データをLUT≪Lookuptable≫として一時的に変換す
る技法を使用して変換する関数ですが、
「Line」は、ココでは、線形(一次元的)に変換⇒元の撮影
画像が照明の関係で傾いている場合に補正を加える処理です
☆備考☆
この関数が何故、存在するかを解説して居る時に思い出した
撮影条件や、カメラの特性で画像の濃淡に機材固有のズレが
生じてアルゴリズムを工夫して補正する必要が遇ったからと
特殊用途で一般的な処理では有りません!
しかしながら、画像処理では撮影機材の問題は付き物なので
過去に如何に工夫したかが分かる例文として解説して行く事
にします!
★備考★
この関数には、オーバーロード(多重定義)として同じ
関数名で仮引数が異なる関数が存在します!
(4-14-10-B)関数「int ConvertLine()」の【返値】説明
int CopyClear::ConvertLine(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
BYTE tbl[], // テーブル「1..255」
int mode // 0→水平方向,1→垂直方向
){
仮引数「TypeArray* ps,TypeArray* pd,」と
S(元)画像D(結果)画像共、
検査関数「CheckImageBYTE ();」で検査で1バイト単位で
有るか無いかを検査し、この関数は、SD両者が1バイト
単位である事を検査し、エラーが有れば返し終了します!
関数の実行時エラーとしてメモリー確保「lut=(BYTE *)
malloc(sizeof(BYTE )*256);」で変数「lut」が空
ポインタに成ったらエラー「STI_MEM」を返し終了します!
正常に最後まで実行し、正常終了「END_STI」を返し
終了します!
(4-14-10-C)関数「int ConvertLine()」の【仮引数】説明
int CopyClear::ConvertLine(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
BYTE tbl[], // テーブル「1..255」
int mode // 0→水平方向,1→垂直方向
){
「TypeArray* ps,」は、S(元)画像を示す画像情報!
「TypeArray* pd,」は、D(結果)画像を示す画像情報!
「BYTE tbl[],」は、変換用のテーブルです!サイズは、
コメントに記載して居る「1..255」では、255と誤解
するかも知れませんが256です!
「int mode」は、モードで「mode=0」で水平方向変換処理
「mode=1」で垂直方向変換処理を示します!
(4-14-10-D)関数「int ConvertLine()」の【アルゴリズム】説明
){
BYTE* lut; // LUTバッファー
TypeArray subS; // 部分画像S
TypeArray subD; // 部分画像D
int h; // 水平幅
int v; // 垂直幅
int k; // 割合(255に対して)
int x; // X座標
int x0; // X座標:始点
int x1; // X座標:終点
int y; // Y座標
int y0; // Y座標:始点
int y1; // Y座標:終点
int sti; // ステータス情報
sti = CheckImageByte( ps ); // 元画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
sti = CheckImageByte( pd ); // 結果画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
h = ( ps->h < pd->h ) ? ps->h : pd->h; // 有効水平幅算出
v = ( ps->v < pd->v ) ? ps->v : pd->v; // 有効垂直幅算出
if( mode == 0 ){ // 水平方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * h ); // LUTを256*水平幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, h ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineX( ps, pd, lut ); // 水平方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
}else{ // 垂直方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * v ); // LUTを256*垂直幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, v ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineY( ps, pd, lut ); // 垂直方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
} // ※大規模LUT確保失敗時の処理※
lut = (BYTE*)malloc( sizeof(BYTE) * 256 ); // LUTを256で確保
if( lut == 0 ){ // 確保失敗時は
return( STI_MEM ); // 左記を返す
} //
if( mode == 0 ){ // 水平方向なら
x0 = 0; // X座標:始点セット
k = tbl[x0]; // 先頭の割合取り出し
for( x = 1; x < h; x++ ){ // X座標を繰り返し
if( k != tbl[x] ){ // 同一割合で無くなれば
x1 = x - 1; // X座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, x0, 0, x1 - x0 + 1, v ); // 部分画像Sセット
subD.subset( pd, x0, 0, x1 - x0 + 1, v ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
x0 = x; // X座標:始点更新
k = tbl[x0]; // 割合更新
} //
} //
}else{ // 垂直方向なら
y0 = 0; // Y座標:始点セット
k = tbl[y0]; // 先頭の割合取り出し
for( y = 1; y < v; y++ ){ // Y座標を繰り返し
if( k != tbl[y] ){ // 同一割合で無くなれば
y1 = y - 1; // Y座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, 0, y0, h, y1 - y0 + 1 ); // 部分画像Sセット
subD.subset( pd, 0, y0, h, y1 - y0 + 1 ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
y0 = y; // Y座標:始点更新
k = tbl[y0]; // 割合更新
} //
} //
} //
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
}
ローカル変数
){
BYTE* lut; // LUTバッファー
TypeArray subS; // 部分画像S
TypeArray subD; // 部分画像D
int h; // 水平幅
int v; // 垂直幅
int k; // 割合(255に対して)
int x; // X座標
int x0; // X座標:始点
int x1; // X座標:終点
int y; // Y座標
int y0; // Y座標:始点
int y1; // Y座標:終点
int sti; // ステータス情報
「BYTE *lut;」は、内部で使用するLUTです!
「TypeArraysubS;」は、S画像の部分画像用
「TypeArraysubD;」は、D画像の部分画像用
「int h;」は、内部でサイズ調整に使用した水平幅
「int v;」は、内部でサイズ調整に使用した垂直幅
「int k;」は、仮引数「BYTE tbl[],」に格納している
係数を内部で扱い易い様に取り出した変数です!
「int x;」は、X座標の値!
「int x0;」は、X座標の範囲で始点!
「int x1;」は、X座標の範囲で終点!
「int y;」は、Y座標の値!
「int y0;」はY座標の範囲で始点!、
「int y1;」はY座標の範囲で終点!、
「int sti;」は、関数の返値の一次置き場!
アルゴリズムコード
sti = CheckImageByte( ps ); // 元画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
sti = CheckImageByte( pd ); // 結果画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
h = ( ps->h < pd->h ) ? ps->h : pd->h; // 有効水平幅算出
v = ( ps->v < pd->v ) ? ps->v : pd->v; // 有効垂直幅算出
if( mode == 0 ){ // 水平方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * h ); // LUTを256*水平幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, h ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineX( ps, pd, lut ); // 水平方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
}else{ // 垂直方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * v ); // LUTを256*垂直幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, v ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineY( ps, pd, lut ); // 垂直方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
} // ※大規模LUT確保失敗時の処理※
lut = (BYTE*)malloc( sizeof(BYTE) * 256 ); // LUTを256で確保
if( lut == 0 ){ // 確保失敗時は
return( STI_MEM ); // 左記を返す
} //
if( mode == 0 ){ // 水平方向なら
x0 = 0; // X座標:始点セット
k = tbl[x0]; // 先頭の割合取り出し
for( x = 1; x < h; x++ ){ // X座標を繰り返し
if( k != tbl[x] ){ // 同一割合で無くなれば
x1 = x - 1; // X座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, x0, 0, x1 - x0 + 1, v ); // 部分画像Sセット
subD.subset( pd, x0, 0, x1 - x0 + 1, v ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
x0 = x; // X座標:始点更新
k = tbl[x0]; // 割合更新
} //
} //
}else{ // 垂直方向なら
y0 = 0; // Y座標:始点セット
k = tbl[y0]; // 先頭の割合取り出し
for( y = 1; y < v; y++ ){ // Y座標を繰り返し
if( k != tbl[y] ){ // 同一割合で無くなれば
y1 = y - 1; // Y座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, 0, y0, h, y1 - y0 + 1 ); // 部分画像Sセット
subD.subset( pd, 0, y0, h, y1 - y0 + 1 ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
y0 = y; // Y座標:始点更新
k = tbl[y0]; // 割合更新
} //
} //
} //
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
}
「sti=CheckImageBYTE (ps);if(sti!=END_STI){
return(sti);}」は、仮引数検査関数
「sti=CheckImageBYTE (ps);」でS(元)画像が1バイト
単位画像か如何か検査し、判定「if(sti!=END_STI)」で
異なれば「return(sti);」とエラーを返し関数終了!
「sti=CheckImageBYTE (pd);if(sti!=END_STI){
return(sti);}」は、D(結果)画像が1バイト検査!
「h=(ps->h<pd->h)?ps->h:pd->h;」は、SD両者の水平幅
での有効幅(最小幅)算出!
「v=(ps->v<pd->v)?ps->v:pd->v;」は、SD両者の垂直幅
での有効幅(最小幅)算出!
「if(mode==0){・・成立中身・・}else{
・・否成立中身・・}」は、条件「(mode==0)」と
仮引数「int mode」が、0の場合、成立中身ブロックを
処理し、それ以外の場合、否成立中身ブロックを処理します
先ず、成立中身ブロックは、
「lut=(BYTE *)malloc(sizeof(BYTE )256h);」とメモリ
確保します!確保するサイズは「sizeof(BYTE )256h」と
「sizeof(BYTE )」でBYTE型(勿論、unsignedchar)なので
1に成るのですが★注意★未知のCPU&処理系にも対応
する為にワザワザ「sizeof(型名)」の記述を採用しました!
そして「256h」と水平幅×256をメモリ取得サイズと
しています!水平幅分1バイト単位のLUTが存在するとの
意味です!そして「if(lut!=0){・・メモリ取得成功時」は、
「sti=makeCvtLineLUT(lut,tbl,h);」で下請けのサブルー
チン「makeCvtLineLUT()」でLUTパターン作成処理し、
「if(sti!=END_STI){free(lut);return(sti);」と
処理失敗「sti!=END_STI」ならば「free(lut);」で取得
したメモリを後始末とし解放し、エラーコード「sti」を
返し関数終了!★注意★エラー時のメモリ解放は必須です!
「convertLineX(ps,pd,lut);free(lut);
return(END_STI);」は、サブルーチン「convertLineX()」で
先ほど作成したLUTを使用してX座標方向の傾き校正処理
を行います!そして「free(lut);」で取得したメモリを
後始末とし解放し、関数正常終了!★注意★勿論、正常終了
でもメモリ解放は必須です!
次に、否成立中身ブロックは、
「lut=(BYTE *)malloc(sizeof(BYTE )256v);」とメモリ
確保します!
そして「256v」と垂直幅×256をメモリ取得サイズと
しています!
垂直幅分1バイト単位のLUTが存在するとの意味です!
そして「if(lut!=0){・・メモリ取得成功時」は、
「sti=makeCvtLineLUT(lut,tbl,v);」で下請けのサブルー
チン「makeCvtLineLUT()」でLUTパターン作成処理し、
「if(sti!=END_STI){free(lut);return(sti);」と
処理失敗「sti!=END_STI」ならば「free(lut);」で取得
したメモリを後始末とし解放し、エラーコード「sti」を
返し関数終了!
「convertLineY(ps,pd,lut);free(lut);
return(END_STI);」は、サブルーチン「convertLineY()」で
先ほど作成したLUTを使用してY座標方向の傾き校正処理
を行います!そして「free(lut);」で取得したメモリを
後始末とし解放し、関数正常終了!
★注意★ココまでで関数の大まかな通常処理の説明は終わり
ですが、「lut=(BYTE *)malloc(sizeof(BYTE )256h);」
及び「lut=(BYTE *)malloc(sizeof(BYTE )256v);」の
メモリ確保
で「if(lut!=0){」と確保成功した場合の処理しか説明して
無い事に注意して下さい!
勿論、大概のシステムでは成功する筈ですのでコレから
説明する部分は、メモリをケチって居るシステムの為の
処理です!詰り、
上記「malloc()」が失敗した場合の処理です!
「lut=(BYTE *)malloc(sizeof(BYTE )*256);」は、
1単位分変換用のLUTをメモリ確保した事を示します!
「if(lut==0){return(STI_MEM);}」は、このサイズでも
確保失敗したらエラー「STI_MEM」を返し関数終了!
そして再び、仮引数「int mode」が、0の場合、成立中身
ブロックを処理し、それ以外の場合、否成立中身ブロックを
処理します
先ず、成立中身ブロックは、
「x0=0;k=tbl[x0];」でX座標始点初期化と係数の取り出し
です!
「for(x=1;x<h;x++){・・ループ中身・・}」は、for
ループ構文で初期化「x=1;」でX座標を1からスタートし
条件「x<h;」で「h-1」まで「x++」と一つずつ増やして
ブロックをループします!そのループ中身ブロックで
「if(k!=tbl[x]){・・内側成立中身・・」と条件「
k!=tbl[x]」≪現係数の値がテーブルの係数と異なる場合に
処理する=同じならば変換する意味が無い≫の場合に成立
中身を実行します!その内側成立中身は、
「x1=x-1;」は、X座標終点を算出!
「makeCvtLineLUTSingle(lut,k);」で基本単位のLUTを
作成して置きます!
「subS.subset(ps,x0,0,x1-x0+1,v);
subD.subset(pd,x0,0,x1-x0+1,v);」でSD両者の処理する
部分画像を「subS・subD」に取り出します!
「ConvertByte(&subS,&subD,lut);」でサブルーチンとし
て関数「ConvertByte();」で部分画像を処理します!
「x0=x;k=tbl[x0];」で次のループの準備としてX座標始点
と係数の取り出しです!
次に、否成立中身ブロックは、
「y0=0;k=tbl[y0];」でY座標始点初期化と係数の取り出し
です!
「for(y=1;y<v;y++){・・ループ中身・・}」は、
forループ構文で初期化「y=1;」でY座標を1から
スタートし条件「y<v;」で「v-1」まで「y++」と一つずつ
増やしてブロックをループします!
そのループ中身ブロックで
「if(k!=tbl[y]){・・内側成立中身・・」と条件「
k!=tbl[y]」≪現係数の値がテーブルの係数と異なる場合に
処理する=同じならば変換する意味が無い≫の場合に成立
中身を実行します!その内側成立中身は、
「y1=y-1;」は、Y座標終点を算出!
「makeCvtLineLUTSingle(lut,k);」で基本単位のLUTを
作成して置きます!
「subS.subset(ps,0,y0,h,y1-y0+1);
subD.subset(pd,0,y0,h,y1-y0+1);」でSD両者の処理する
部分画像を「subS・subD」に取り出します!
「ConvertByte(&subS,&subD,lut);」でサブルーチンとし
て関数「ConvertByte();」で部分画像を処理します!
「y0=y;k=tbl[y0];」で次のループの準備としてY座標始点
と係数の取り出しです!
最後に「free(lut);return(END_STI);」と確保した
メモリーを解放し正常終了!
(4-14-10-D)関数「int ConvertLine()」の【備考】
この関数の使用例は、敢て記載しません!
この関数を製作した時を思い出したのだが、良く思い出せ
無いのでアルゴリズムコードから推測して、撮影条件が悪い
≪照明が傾いて居るとか、撮像機器に問題が有る≫場合の
画像補正を行って居ると推測したのでアルゴリズムコードと
して補正するとは、コンナ物ですと示す事を説明するだけと
します!
(4-14-11)関数「int ConvertLine(
TypeArray* ps,TypeArray* pd,
UWORD tbl[],int mode){・・・}」の説明
int CopyClear::ConvertLine(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
UWORD tbl[], // テーブル「1..1023」
int mode // 0→水平方向,1→垂直方向
){
BYTE* lut; // LUTバッファー
TypeArray subS; // 部分画像S
TypeArray subD; // 部分画像D
int h; // 水平幅
int v; // 垂直幅
int k; // 割合(255に対して)
int x; // X座標
int x0; // X座標:始点
int x1; // X座標:終点
int y; // Y座標
int y0; // Y座標:始点
int y1; // Y座標:終点
int sti; // ステータス情報
sti = CheckImageByte( ps ); // 元画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
sti = CheckImageByte( pd ); // 結果画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
h = ( ps->h < pd->h ) ? ps->h : pd->h; // 有効水平幅算出
v = ( ps->v < pd->v ) ? ps->v : pd->v; // 有効垂直幅算出
if( mode == 0 ){ // 水平方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * h ); // LUTを256*水平幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, h ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineX( ps, pd, lut ); // 水平方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
}else{ // 垂直方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * v ); // LUTを256*垂直幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, v ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineY( ps, pd, lut ); // 垂直方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
} // ※大規模LUT確保失敗時の処理※
lut = (BYTE*)malloc( sizeof(BYTE) * 256 ); // LUTを256で確保
if( lut == 0 ){ // 確保失敗時は
return( STI_MEM ); // 左記を返す
} //
if( mode == 0 ){ // 水平方向なら
x0 = 0; // X座標:始点セット
k = tbl[x0]; // 先頭の割合取り出し
for( x = 1; x < h; x++ ){ // X座標を繰り返し
if( k != tbl[x] ){ // 同一割合で無くなれば
x1 = x - 1; // X座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, x0, 0, x1 - x0 + 1, v ); // 部分画像Sセット
subD.subset( pd, x0, 0, x1 - x0 + 1, v ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
x0 = x; // X座標:始点更新
k = tbl[x0]; // 割合更新
} //
} //
}else{ // 垂直方向なら
y0 = 0; // Y座標:始点セット
k = tbl[y0]; // 先頭の割合取り出し
for( y = 1; y < v; y++ ){ // Y座標を繰り返し
if( k != tbl[y] ){ // 同一割合で無くなれば
y1 = y - 1; // Y座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, 0, y0, h, y1 - y0 + 1 ); // 部分画像Sセット
subD.subset( pd, 0, y0, h, y1 - y0 + 1 ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
y0 = y; // Y座標:始点更新
k = tbl[y0]; // 割合更新
} //
} //
} //
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
(4-14-11-A)関数「int ConvertLine()」の【関数名】説明
「Convert」は、英単語「Convert」として変換するの意味で
す!ここの画像処理では、LUT≪ルックアップテーブル≫
を使用したS(元)画像⇒LUT変換⇒D(結果)画像への
画素データをLUT≪Lookuptable≫として一時的に変換す
る技法を使用して変換する関数ですが、
「Line」は、ココでは、線形(一次元的)に変換⇒元の撮影
画像が照明の関係で傾いている場合に補正を加える処理です
☆備考☆
この関数が何故、存在するかを解説して居る時に思い出した
撮影条件や、カメラの特性で画像の濃淡に機材固有のズレが
生じてアルゴリズムを工夫して補正する必要が遇ったからと
特殊用途で一般的な処理では有りません!
しかしながら、画像処理では撮影機材の問題は付き物なので
過去に如何に工夫したかが分かる例文として解説して行く事
★備考★
この関数には、オーバーロード(多重定義)として同じ
関数名で仮引数が異なる関数が存在します!
(4-14-11-B)関数「int ConvertLine()」の【返値】説明
int CopyClear::ConvertLine(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
UWORD tbl[], // テーブル「1..1023」
int mode // 0→水平方向,1→垂直方向
){
仮引数「TypeArray* ps,TypeArray* pd,」とS(元)画像
D(結果)画像共、検査関数「CheckImageBYTE ();」で検査
で1バイト単位で有るか無いかを検査し、この関数は、SD
両者が1バイト単位である事を検査し、エラーが有れば返し
終了します!
関数の実行時エラーとしてメモリー確保「lut=(BYTE *)
malloc(sizeof(BYTE )*256);」で変数「lut」が空
ポインタに成ったらエラー「STI_MEM」を返し終了します!
正常に最後まで実行し、正常終了「END_STI」を返し
終了します!
(4-14-11-C)関数「int ConvertLine()」の【仮引数】説明
int CopyClear::ConvertLine(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
UWORD tbl[], // テーブル「1..1023」
int mode // 0→水平方向,1→垂直方向
){
「TypeArray* ps,」は、S(元)画像を示す画像情報!
「TypeArray* pd,」は、D(結果)画像を示す画像情報!
「UWORD tbl[],」は、変換用のテーブルです!サイズは、
コメントに記載して居る「1..1023」では、1023と誤解
するかも知れませんが1024です!
★注意★ココの型が、「UWORD」と異なるのが、
「4-14-10」で説明した関数との違いです!
「int mode」は、モードで「mode=0」で水平方向変換処理
「mode=1」で垂直方向変換処理を示します!
(4-14-11-D)関数「int ConvertLine()」の【アルゴリズム】説明
){
BYTE* lut; // LUTバッファー
TypeArray subS; // 部分画像S
TypeArray subD; // 部分画像D
int h; // 水平幅
int v; // 垂直幅
int k; // 割合(255に対して)
int x; // X座標
int x0; // X座標:始点
int x1; // X座標:終点
int y; // Y座標
int y0; // Y座標:始点
int y1; // Y座標:終点
int sti; // ステータス情報
sti = CheckImageByte( ps ); // 元画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
sti = CheckImageByte( pd ); // 結果画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
h = ( ps->h < pd->h ) ? ps->h : pd->h; // 有効水平幅算出
v = ( ps->v < pd->v ) ? ps->v : pd->v; // 有効垂直幅算出
if( mode == 0 ){ // 水平方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * h ); // LUTを256*水平幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, h ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineX( ps, pd, lut ); // 水平方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
}else{ // 垂直方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * v ); // LUTを256*垂直幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, v ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineY( ps, pd, lut ); // 垂直方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
} // ※大規模LUT確保失敗時の処理※
lut = (BYTE*)malloc( sizeof(BYTE) * 256 ); // LUTを256で確保
if( lut == 0 ){ // 確保失敗時は
return( STI_MEM ); // 左記を返す
} //
if( mode == 0 ){ // 水平方向なら
x0 = 0; // X座標:始点セット
k = tbl[x0]; // 先頭の割合取り出し
for( x = 1; x < h; x++ ){ // X座標を繰り返し
if( k != tbl[x] ){ // 同一割合で無くなれば
x1 = x - 1; // X座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, x0, 0, x1 - x0 + 1, v ); // 部分画像Sセット
subD.subset( pd, x0, 0, x1 - x0 + 1, v ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
x0 = x; // X座標:始点更新
k = tbl[x0]; // 割合更新
} //
} //
}else{ // 垂直方向なら
y0 = 0; // Y座標:始点セット
k = tbl[y0]; // 先頭の割合取り出し
for( y = 1; y < v; y++ ){ // Y座標を繰り返し
if( k != tbl[y] ){ // 同一割合で無くなれば
y1 = y - 1; // Y座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, 0, y0, h, y1 - y0 + 1 ); // 部分画像Sセット
subD.subset( pd, 0, y0, h, y1 - y0 + 1 ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
y0 = y; // Y座標:始点更新
k = tbl[y0]; // 割合更新
} //
} //
} //
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
}
ローカル変数
){
BYTE* lut; // LUTバッファー
TypeArray subS; // 部分画像S
TypeArray subD; // 部分画像D
int h; // 水平幅
int v; // 垂直幅
int k; // 割合(255に対して)
int x; // X座標
int x0; // X座標:始点
int x1; // X座標:終点
int y; // Y座標
int y0; // Y座標:始点
int y1; // Y座標:終点
int sti; // ステータス情報
「BYTE *lut;」は、内部で使用するLUTです!
「TypeArraysubS;」は、S画像の部分画像用
「TypeArraysubD;」は、D画像の部分画像用
「int h;」は、内部でサイズ調整に使用した水平幅
「int v;」は、内部でサイズ調整に使用した垂直幅
「int k;」は、仮引数「BYTE tbl[],」に格納している
係数を内部で扱い易い様に取り出した変数です!
「int x;」は、X座標の値!
「int x0;」は、X座標の範囲で始点!
「int x1;」は、X座標の範囲で終点!
「int y;」は、Y座標の値!
「int y0;」はY座標の範囲で始点!、
「int y1;」はY座標の範囲で終点!、
「int sti;」は、関数の返値の一次置き場!
アルゴリズムコード
sti = CheckImageByte( ps ); // 元画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
sti = CheckImageByte( pd ); // 結果画像検査
if( sti != END_STI ){ // 不正が有れば
return( sti ); // エラーステータスを返す
} //
h = ( ps->h < pd->h ) ? ps->h : pd->h; // 有効水平幅算出
v = ( ps->v < pd->v ) ? ps->v : pd->v; // 有効垂直幅算出
if( mode == 0 ){ // 水平方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * h ); // LUTを256*水平幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, h ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineX( ps, pd, lut ); // 水平方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
}else{ // 垂直方向なら
lut = (BYTE*)malloc( sizeof(BYTE) * 256 * v ); // LUTを256*垂直幅で確保
if( lut != 0 ){ // 確保成功時は
sti = makeCvtLineLUT( lut, tbl, v ); // 左記でLUT作成
if( sti != END_STI ){ // 不正が有れば
free( lut ); // LUTを解放
return( sti ); // エラーステータスを返す
} //
convertLineY( ps, pd, lut ); // 垂直方向LUT変換
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
} //
} // ※大規模LUT確保失敗時の処理※
lut = (BYTE*)malloc( sizeof(BYTE) * 256 ); // LUTを256で確保
if( lut == 0 ){ // 確保失敗時は
return( STI_MEM ); // 左記を返す
} //
if( mode == 0 ){ // 水平方向なら
x0 = 0; // X座標:始点セット
k = tbl[x0]; // 先頭の割合取り出し
for( x = 1; x < h; x++ ){ // X座標を繰り返し
if( k != tbl[x] ){ // 同一割合で無くなれば
x1 = x - 1; // X座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, x0, 0, x1 - x0 + 1, v ); // 部分画像Sセット
subD.subset( pd, x0, 0, x1 - x0 + 1, v ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
x0 = x; // X座標:始点更新
k = tbl[x0]; // 割合更新
} //
} //
}else{ // 垂直方向なら
y0 = 0; // Y座標:始点セット
k = tbl[y0]; // 先頭の割合取り出し
for( y = 1; y < v; y++ ){ // Y座標を繰り返し
if( k != tbl[y] ){ // 同一割合で無くなれば
y1 = y - 1; // Y座標:終点セット
makeCvtLineLUTSingle( lut, k ); // 左記でLUT作成
subS.subset( ps, 0, y0, h, y1 - y0 + 1 ); // 部分画像Sセット
subD.subset( pd, 0, y0, h, y1 - y0 + 1 ); // 部分画像Dセット
ConvertByte( &subS, &subD, lut ); // LUT変換
y0 = y; // Y座標:始点更新
k = tbl[y0]; // 割合更新
} //
} //
} //
free( lut ); // LUTを解放
return( END_STI ); // 正常終了
}
「sti=CheckImageBYTE (ps);if(sti!=END_STI){
return(sti);}」は、
仮引数検査関数「sti=CheckImageBYTE (ps);」で
S(元)画像が1バイト単位画像か如何か検査し、
判定「if(sti!=END_STI)」で異なれば「return(sti);」と
エラーを返し関数終了!
「sti=CheckImageBYTE (pd);if(sti!=END_STI){
return(sti);}」は、
D(結果)画像が1バイト検査!
「h=(ps->h<pd->h)?ps->h:pd->h;」は、SD両者の水平幅
での有効幅(最小幅)算出!
「v=(ps->v<pd->v)?ps->v:pd->v;」は、SD両者の垂直幅
での有効幅(最小幅)算出!
「if(mode==0){・・成立中身・・}else{
・・否成立中身・・}」は、条件「(mode==0)」と
仮引数「int mode」が、0の場合、成立中身ブロックを
処理し、それ以外の場合、否成立中身ブロックを処理します
先ず、成立中身ブロックは、
「lut=(BYTE *)malloc(sizeof(BYTE )256h);」とメモリ
確保します!確保するサイズは「sizeof(UWORD)*256*h」と
「sizeof(UWORD)」でUWORD型(勿論、unsignedchar)なので
1に成るのですが
★注意★未知のCPU&処理系にも対応する為にワザワザ
「sizeof(型名)」の記述を採用しました!
そして「256*h」と水平幅×256をメモリ取得サイズと
しています!水平幅分1バイト単位のLUTが存在するとの
意味です!そして「if(lut!=0){・・メモリ取得成功時」は、
「sti=makeCvtLineLUT(lut,tbl,h);」で下請けのサブルー
チン「makeCvtLineLUT()」でLUTパターン作成処理し、
★注意★ココでサブルーチンとして使用している関数は、
「4-14-10-D」で説明した関数と同じ名前ですが
仮引数「UWORDtbl[],」とUWORD型と型が異なる多重定義
(オーバーロード)関数と物が違う事に留意して下さい!
「if(sti!=END_STI){free(lut);return(sti);」と
処理失敗「sti!=END_STI」ならば「free(lut);」で取得
したメモリを後始末とし解放し、エラーコード「sti」を
返し関数終了!★注意★エラー時のメモリ解放は必須です!
「convertLineX(ps,pd,lut);free(lut);
return(END_STI);」は、サブルーチン「convertLineX()」で
先ほど作成したLUTを使用してX座標方向の傾き校正処理
を行います!そして「free(lut);」で取得したメモリを
後始末とし解放し、関数正常終了!★注意★勿論、正常終了
でもメモリ解放は必須です!
次に、否成立中身ブロックは、
「lut=(BYTE *)malloc(sizeof(BYTE )*256*v);」とメモリ
確保します!
そして「256v」と垂直幅×256をメモリ取得サイズと
しています!垂直幅分1バイト単位のLUTが存在するとの
意味です!
そして「if(lut!=0){・・メモリ取得成功時」は、
「sti=makeCvtLineLUT(lut,tbl,v);」で下請けの
サブルーチン「makeCvtLineLUT()」でLUTパターン作成
処理し、
★注意★ココでサブルーチンとして使用している関数は、
「4-14-10-D」で説明した関数と同じ名前ですが
仮引数「UWORD tbl[],」とUWORD型と型が異なる多重定義
(オーバーロード)関数と物が違う事に留意して下さい!
「if(sti!=END_STI){free(lut);return(sti);」と
処理失敗「sti!=END_STI」ならば「free(lut);」で取得
したメモリを後始末とし解放し、エラーコード「sti」を
返し関数終了!
「convertLineY(ps,pd,lut);free(lut);
return(END_STI);」は、
サブルーチン「convertLineY()」で先ほど作成したLUTを
使用してY座標方向の傾き校正処理を行います!
そして「free(lut);」で取得したメモリを後始末とし
解放し、関数正常終了!
★注意★ココまでで関数の大まかな通常処理の説明は終わり
ですが、「lut=(BYTE *)malloc(sizeof(BYTE)*256*h);」
及び
「lut=(BYTE *)malloc(sizeof(BYTE)*256*v);」のメモリ
確保で「if(lut!=0){」と確保成功した場合の処理しか
説明して無い事に注意して下さい!
勿論、大概のシステムでは成功する筈ですのでコレから
説明する部分は、メモリをケチって居るシステムの為の処理
です!
詰り、上記「malloc()」が失敗した場合の処理です!
「lut=(BYTE *)malloc(sizeof(BYTE)*256);」は、
1単位分変換用のLUTをメモリ確保した事を示します!
「if(lut==0){return(STI_MEM);}」は、このサイズでも確保
失敗したらエラー「STI_MEM」を返し関数終了!
そして再び、仮引数「int mode」が、0の場合、成立中身
ブロックを処理し、それ以外の場合、否成立中身ブロックを
処理します!
先ず、成立中身ブロックは、
「x0=0;k=tbl[x0];」でX座標始点初期化と係数の取り出し
です!
「for(x=1;x<h;x++){・・ループ中身・・}」は、
forループ構文で初期化「x=1;」でX座標を1から
スタートし条件「x<h;」で「h-1」まで「x++」と一つずつ
増やしてブロックをループします!
そのループ中身ブロックで
「if(k!=tbl[x]){・・内側成立中身・・」と条件「
k!=tbl[x]」≪現係数の値がテーブルの係数と異なる場合に
処理する=同じならば変換する意味が無い≫の場合に成立
中身を実行します!その内側成立中身は、
「x1=x-1;」は、X座標終点を算出!
「makeCvtLineLUTSingle(lut,k);」で基本単位のLUTを
作成して置きます!
「subS.subset(ps,x0,0,x1-x0+1,v);
subD.subset(pd,x0,0,x1-x0+1,v);」でSD両者の処理する
部分画像を「subS・subD」に取り出します!
「ConvertByte(&subS,&subD,lut);」でサブルーチンとし
て関数「ConvertByte();」で部分画像を処理します!
「x0=x;k=tbl[x0];」で次のループの準備としてX座標始点
と係数の取り出しです!
次に、否成立中身ブロックは、
「y0=0;k=tbl[y0];」でY座標始点初期化と係数の取り出し
です!
「for(y=1;y<v;y++){・・ループ中身・・}」は、for
ループ構文で初期化「y=1;」でY座標を1からスタートし
条件「y<v;」で「v-1」まで「y++」と一つずつ増やして
ブロックをループします!そのループ中身ブロックで
「if(k!=tbl[y]){・・内側成立中身・・」と条件「
k!=tbl[y]」≪現係数の値がテーブルの係数と異なる場合に
処理する=同じならば変換する意味が無い≫の場合に成立
中身を実行します!その内側成立中身は、
「y1=y-1;」は、Y座標終点を算出!
「makeCvtLineLUTSingle(lut,k);」で基本単位のLUTを
作成して置きます!
「subS.subset(ps,0,y0,h,y1-y0+1);
subD.subset(pd,0,y0,h,y1-y0+1);」でSD両者の処理する
部分画像を「subS・subD」に取り出します!
「ConvertByte(&subS,&subD,lut);」でサブルーチンとし
て関数「ConvertByte();」で部分画像を処理します!
「y0=y;k=tbl[y0];」で次のループの準備としてY座標始点
と係数の取り出し
です!
最後に「free(lut);return(END_STI);」と確保した
メモリーを解放し正常終了!
(4-14-11-D)関数「int ConvertLine()」の【備考】
この関数の使用例は、敢て記載しません!
この関数を製作した時を思い出したのだが、良く思い出せ
無いのでアルゴリズムコードから推測して、撮影条件が悪い
≪照明が傾いて居るとか、撮像機器に問題が有る≫場合の
画像補正を行って居ると推測したのでアルゴリズムコードと
して補正するとは、コンナ物ですと示す事を説明するだけと
します!
※特に注意※
ココの「4-14-11」と一つ前の「4-14-10」
で説明した関数「int ConvertLine()」は、ココの字面だけ
の解釈では、役に立つ関数とは、現時点(2024年3月)
では思えません!
と作者が、思い出せ無かった事を記載して置きます!
まっC言語のアルゴリズムコードの見本ぐらいに利用して
下さい!
(4-14-12)関数「int Binarization(
TypeArray* ps,TypeArray* pd,
int t,int data=255,int low=0){・・・}」の説明
int CopyClear::Binarization(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
int t, // しきい値 0..255
int data, // 出力値:高位(省略時=255)
int low // 出力値:低位(省略時=0)
){
BYTE lut[ 256 ]; // LUTバッファー
up_fill_void( low, lut, t ); // LUT[0..t] =low
up_fill_void( data, &lut[ t ], 256 - t ); // LUT[t..255]=data
return( ConvertByte( ps, pd, lut ) ); // LUT変換で2値化
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
(4-14-12-A)関数「Binarization()」の【関数名】説明
「Binarization」は、英単語でと言うか、画像処理の専門
用語で「二値化」≪多値画像(通常の撮像カメラ単色では、
8ビット画素画像・特殊用途の高性能カメラ単色では、
10ビットとか画素の値が有りますが、メモリの都合で
16ビット画素画像)等と扱いますが、
其れを白画素(8ビット画像の場合、数値「1」で表すか、
「255」と表示して明るい白に成る事を選ぶかは、
アプリの都合です)が、黒画素(この場合、数値「0」と
数値としても表示上のアプリ都合でも漆黒を意味する黒
「数値0」)と表現します!黒(0)で白(≠0)と成ると
考えて下さい!
この二値画素画像の考え方は基本ですので画像処理を扱う
場合の基本のキですから!良く理解して下さい!≫
そしてBinarization()関数は、多値画像を二値画像に変換す
る関数で有ると理解して下さい!
(4-14-12-B)関数「int Binarization()」の【返値】説明
int CopyClear::Binarization(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
int t, // しきい値 0..255
int data, // 出力値:高位(省略時=255)
int low // 出力値:低位(省略時=0)
){
「return(ConvertByte(ps,pd,lut));」とサブルーチンの
関数「ConvertByte()」の実行時の返値を返します!
(4-14-12-C)関数「Binarization()」の【仮引数】説明
int CopyClear::Binarization(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列
int t, // しきい値 0..255
int data, // 出力値:高位(省略時=255)
int low // 出力値:低位(省略時=0)
){
「TypeArray* ps,」は、S(元)画像情報です!
「TypeArray* pd,」は、D(結果)画像情報です!
「int t,」は、閾値(しきい値)と二値の切れ目です!
「int data,」は、白画素の値です!★注意★実引数の
省略時=255と8ビット画像最大値にして居ます!
勿論、論理的演算では、「TRUE=真」として扱います!
「int low」は、黒画素の値です!★注意★実引数の
省略時=0と自然数最小値で表示上漆黒にして居ます!
勿論、論理的演算では、「FALSE=偽」として扱います!
(4-14-12-D)関数「Binarization()」の【アルゴリズム】説明
){
BYTE lut[ 256 ]; // LUTバッファー
up_fill_void( low, lut, t ); // LUT[0..t] =low
up_fill_void( data, &lut[ t ], 256 - t ); // LUT[t..255]=data
return( ConvertByte( ps, pd, lut ) ); // LUT変換で2値化
}
ローカル変数
){
BYTE lut[ 256 ]; // LUTバッファー
「BYTE lut[256];」は、内部で使用する変換用LUTです!
アルゴリズムコード
up_fill_void( low, lut, t ); // LUT[0..t] =low
up_fill_void( data, &lut[ t ], 256 - t ); // LUT[t..255]=data
return( ConvertByte( ps, pd, lut ) ); // LUT変換で2値化
}
「up_fill_void (low,lut,t);」は、LUT配列の
[0]から[t-1]の範囲と仮引数「int t,」閾値(しきい値)
未満の場所を仮引数「int low」と黒画素の値をセット!
「up_fill_void (data,&lut[t],256-t);」は、LUT配列の
[t]から[256-t]と仮引数「int t,」閾値(しきい値)
以上の場所を仮引数「int data,」と白画素の値をセット!
★注意★ココまでで二値化パターン作成用LUTが出来まし
た!そしてS(元)画像が8ビット画像画素≪0..255範囲≫
「return(ConvertByte(ps,pd,lut));」は、サブルーチン
とし関数「ConvertByte()」で二値化LUTで
仮引数「TypeArray* ps,」8ビット画素画像としてLUT
変換し、仮引数「TypeArray* pd,」に結果画像が二値画素
画像に成る事を理解して下さい!★注意★更にコノ関数は
使用頻度の多い≪多値画像から二値画像に変換し二値画像
処理を行う標準的な前段処理に使用します!≫
(4-14-12-E)関数「Binarization()」の【使用例】説明
{
TypeArray PatImg; // 画像型変数
// :編か前のパターン画像
TypeArray* pPatImg=&PatImg; // 上記へのポインタ
TypeArray Wimg; // 画像型変数
//:フィル書き込み用
TypeArray* pWimg=&Wimg; // 上記へのポインタ
//:部分画像用
TypeArray* pimgA=&PatImg; // 部分画像用
TypeArray* pimgB=&PatImg; // 部分画像用
long hgm[256]; // ヒストグラム算出データ配列
int t; // 2値化しきい値
int sti; // ステータス情報
pPatImg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pPatImg, 0 ); // 画像メモリ「0クリア」
pWimg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pWimg, 0 ); // 画像メモリ「0クリア」
// 典型的な2値化画像生成方法での処理
// サンプルパターン画像作成
FUN.Clear( pPatImg, 100 ); // 画像メモリ「画素データ100でクリア」
pimgA->subset( pPatImg, 100, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
50, 30 ); // 部分画像を用意
FUN.Clear( pimgA, 200 ); // 部分画像を画素データ200でクリア
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile7.bmp", pPatImg );
// 典型的な2値化しきい値を算出する方法
FUN.HistogramImage( pPatImg, hgm ); // 画像の画素濃度ヒストグラム産出
t = FUN.GetBinOotu( hgm, 256 ); // 大津法と言う画像処理で一般的な産出手法で
// 濃度ヒストグラム・データから、しきい値産出
FUN.Binarization( pPatImg, pWimg, t ); // 今回紹介する関数「Binarization()」で2値化
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile8.bmp", pWimg );
// 後始末
pPatImg->freeMem(); // 画像メモリ解放
pWimg->freeMem(); // 画像メモリ解放
}
★注意★
上記例文で注意して欲しいのは、LUTは、用意して無い事
です!関数「Binarization()」の中でLUTをローカルに
使用してLUT変換を内部で行って居る事です!
更に、「long hgm[256];」を用意して多値画像「PatImg」の
ヒストグラム(画素度数分布)作成し、そのヒストグラム
データから、閾値(しきい値)を作成して
関数「Binarization()」で二値画像に変換する事を
記載して居る事を理解して下さい!この二値画像に変換
する方法は、標準的な方法なので覚えて下さい!
取り敢えず、本日(3月20)の講義は、ココまでにし
ます!何だか、疲れたのだ!受講されている方も
お疲れと思いますのでココまでとします!
まだ、Noteサーバーが快適に動く分量ですから、
引き続き、この文章に続きは、載せます!
2024年3月22続講
(4-14-13)関数「int BinarizationMyself(
TypeArray* p,int data);{・・・}」の説明
int CopyClear::BinarizationMyself(
TypeArray *p, // 配列(画像)情報
int data // 出力値:≠0時
){
BYTE *ptr; // 画像ポインタ
int h; // 水平幅
int v; // 垂直幅
int inc; // 増加幅
if( p->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
ptr = (BYTE*)p->adr; // 画像Ptrを取り出し
inc = p->inc; // 増加幅を取り出し
h = p->h; // 水平/垂直幅を
v = p->v; // 取り出し
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationMyselfBase( ptr, h, data ); // 水平方向処理
ptr += inc; // 垂直方向に進める
} //
return( END_STI ); // 正常終了
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
※関数内のコメントに対する備考※「:C関数専用:」との
記載は、PIPLと言うコマンド言語からはアクセス出来る
様に設計して無いとの意味でC/C++で使用する場合は、
普通に使用出来る事を意味します!
(4-14-13-A)関数「BinarizationMyself()」の【関数名】説明
「Binarization」は、二値化を意味し、
「Myself」は、英単語「Myself」の意味通り「自分自身」
です⇒仮引数「TypeArray* p,」と画像情報が一つしか
無い事に注目して下さい!自分自身とは、S(元)画像と
D(結果)画像が同じ画像で画像自体が二値化する事に成り
ます!
(4-14-13-B)関数「int BinarizationMyself()」の【返値】説明
int CopyClear::BinarizationMyself(
TypeArray *p, // 配列(画像)情報
int data // 出力値:≠0時
){
この関数内で明示的に検査しているのは、
仮引数「TypeArray* p,」が1バイト単位か否かで1バイト
で無ければエラーコード「STI_ARY_5」を返します!
最後まで実行時は、正常終了「END_STI」を返します!
(4-14-13-C)関数「BinarizationMyself()」の【仮引数】説明
int CopyClear::BinarizationMyself(
TypeArray *p, // 配列(画像)情報
int data // 出力値:≠0時
){
「TypeArray* p,」は、画像情報です!S(元)画像も
D(結果)画像が同じ画像情報です!
「int data」は、白画素の値です!
(4-14-13-D)関数「BinarizationMyself()」の【アルゴリズム】説明
){
BYTE *ptr; // 画像ポインタ
int h; // 水平幅
int v; // 垂直幅
int inc; // 増加幅
if( p->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
ptr = (BYTE*)p->adr; // 画像Ptrを取り出し
inc = p->inc; // 増加幅を取り出し
h = p->h; // 水平/垂直幅を
v = p->v; // 取り出し
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationMyselfBase( ptr, h, data ); // 水平方向処理
ptr += inc; // 垂直方向に進める
} //
return( END_STI ); // 正常終了
}
ローカル変数
){
BYTE *ptr; // 画像ポインタ
int h; // 水平幅
int v; // 垂直幅
int inc; // 増加幅
「BYTE *ptr;」は、1バイト単位画素への実アクセス
ポインタです!このポインタへの操作を行います!
「int h;int v;int inc;」は、は、内部で使用し易い(
高速化)の為に一旦、内部の単純な変数に画像情報の水平・
垂直・増加幅を代入した物です!
アルゴリズムコード
if( p->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
ptr = (BYTE*)p->adr; // 画像Ptrを取り出し
inc = p->inc; // 増加幅を取り出し
h = p->h; // 水平/垂直幅を
v = p->v; // 取り出し
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationMyselfBase( ptr, h, data ); // 水平方向処理
ptr += inc; // 垂直方向に進める
} //
return( END_STI ); // 正常終了
}
「if(p->w!=1){return(STI_ARY_5);}」は、
仮引数「TypeArray* p,」が1バイト単位で無い場合は
エラーコード「STI_ARY_5」を返し関数終了!
「ptr=(BYTE *)p->adr;」は、画素操作ポインタをセット!
「inc=p->inc;h=p->h;v=p->v;」は、内部で使用し易い(
高速化)の為に一旦、単純な変数に画像情報の水平・垂直・
増加幅を代入した物です!
「while(--v>=0){・・ループ本体・・}」は、
ループ条件「--v>=0」で垂直幅の数分「ループ本体」を
ループ(繰り返す)処理です★備考★「while(--v>=0」の
様に「--v>=0」とデクリメント演算子で変数値が変化して
も影響が無い応用には、経験上最適なCPU機械コードが
生成出来るので、この画像処理ライブラリは、この記法を
多用して居ます!学校の教科書的には余り教えて無い(恐ら
く変数の値が変わる事を説明する必要が有るので説明がヤヤ
コシク成る事を嫌ったと推測)と思いますが、兎も角、高速
に動かすには、この記法がベストと考えて採用しています!
ループ条件「--v>=0」で垂直幅の数分繰り返すと言う事は、
水平方向の処理を垂直幅の数分繰り返すとの意味で
ループ本体「BinarizationMyselfBase(ptr,h,data);
ptr+=inc;」は、水平方向の処理としてサブルーチン
関数「BinarizationMyselfBase(ptr,h,data);」で処理し、
「ptr+=inc;」でポインタを増加幅分進めるとの意味で
次の処理行へ移動との意味です!★備考★この様に外側
ループで垂直方向を繰り返し「{}」とブロック内で水平
方向の処理を行うと言う方法は、この画像処理ライブラリで
多用しているアルゴリズムコード記載法です!
★注意★順番的に後で解説する事に成りますが、サブルーチ
ン関数「BinarizationMyselfBase()」は、水平方向の処理
で「画素≠0」の場合に仮引数「int data」に画素を置き換え
る関数です!
「return(END_STI);」は、正常終了を返値として返し
関数終了
(4-14-13-E)関数「BinarizationMyself()」の【使用例】説明
{
TypeArray PatImg; // 画像型変数
// パターン画像
TypeArray* pPatImg=&PatImg; // 上記へのポインタ
//:部分画像用
TypeArray* pimgA=&PatImg; // 部分画像用
int sti; // ステータス情報
pPatImg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pPatImg, 0 ); // 画像メモリ「0クリア」
// 典型的な2値化画像生成方法での処理
// サンプルパターン画像作成
FUN.Clear( pPatImg, 100 ); // 画像メモリ「画素データ100でクリア」
pimgA->subset( pPatImg, 100, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
50, 30 ); // 部分画像を用意
FUN.Clear( pimgA, 200 ); // 部分画像を画素データ200でクリア
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile7.bmp", pPatImg );
FUN.BinarizationMyself( pPatImg, 255 ); // 今回紹介する関数「BinarizationMyself()」で2値化
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile8.bmp", pPatImg );
// 後始末
pPatImg->freeMem(); // 画像メモリ解放
}
★注意★
上記例文で注意して欲しいのは、LUTは、用意して無い事
です!関数「BinarizationMyself()」の中でLUTをローカル
に使用してLUT変換を内部で行って居る事です!
更に、黒画素「=0」・白画素を「≠0」と見なして結果の
白画素を「255」にします!
(4-14-14)関数「int BinarizationNonZeo(
TypeArray* ps,TypeArray* pd,int data){・・・}」の説明
int CopyClear::BinarizationNonZeo(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列(画像)
int data // 出力値:≠0時
){
BYTE *ptrs; // S画像ポインタ
BYTE *ptrd; // D画像ポインタ
int h; // 水平幅
int v; // 垂直幅
int inc_s; // S増加幅
int inc_d; // D増加幅
if( ps->w != 1 || pd->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
ptrs = (BYTE*)ps->adr; // SPtrを取り出し
ptrd = (BYTE*)pd->adr; // DPtrを取り出し
inc_s = ps->inc; // S増加幅を取出
inc_d = pd->inc; // D増加幅を取出
h = ps->h; // 水平/垂直幅を
v = ps->v; // 取り出し
if( h > pd->h ){ // 水平幅を小の方に
h = pd->h; // 補正
} //
if( v > pd->v ){ // 垂直幅を小の方に
v = pd->v; // 補正
} //
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationNonZeoBase( ptrs, ptrd, h, data ); // 水平方向処理
ptrs += inc_s; // 垂直方向に進める
ptrd += inc_d; // 垂直方向に進める
} //
return( END_STI ); // 正常終了
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
※関数内のコメントに対する備考※「:C関数専用:」との
記載は、PIPLと言うコマンド言語からはアクセス出来る
様に設計して無いとの意味でC/C++で使用する場合は、
普通に使用出来る事を意味します!
(4-14-14-A)関数「BinarizationNonZeo()」の【関数名】説明
「Binarization」は、二値化を意味し、
「NonZeo」は、「≠0」を意味します!関数の動作としたら
「4-14-13」で説明した
関数「BinarizationMyself()」の仮引数が自分自身で無く、
SD両画像情報独立に指定出来る事が異なります!
(4-14-14-B)関数「int BinarizationNonZeo()」の【返値】説明
int CopyClear::BinarizationNonZeo(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列(画像)
int data // 出力値:≠0時
){
この関数内で明示的に検査しているのは、
仮引数「TypeArray* ps,TypeArray* pd,」が、
1バイト単位か否かで1バイトで無ければ
エラーコード「STI_ARY_5」を返します!
最後まで実行時は、正常終了「END_STI」を返します!
(4-14-14-C)関数「BinarizationNonZeo()」の【仮引数】説明
int CopyClear::BinarizationNonZeo(
TypeArray *ps, // S配列(画像)
TypeArray *pd, // D配列(画像)
int data // 出力値:≠0時
){
「TypeArray* ps,」は、S(元)画像情報
「TypeArray* pd,」は、D(結果)画像情報
「int data」は、白画素の値です!
(4-14-14-D)関数「BinarizationNonZeo()」の【アルゴリズム】説明
){
BYTE *ptrs; // S画像ポインタ
BYTE *ptrd; // D画像ポインタ
int h; // 水平幅
int v; // 垂直幅
int inc_s; // S増加幅
int inc_d; // D増加幅
if( ps->w != 1 || pd->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
ptrs = (BYTE*)ps->adr; // SPtrを取り出し
ptrd = (BYTE*)pd->adr; // DPtrを取り出し
inc_s = ps->inc; // S増加幅を取出
inc_d = pd->inc; // D増加幅を取出
h = ps->h; // 水平/垂直幅を
v = ps->v; // 取り出し
if( h > pd->h ){ // 水平幅を小の方に
h = pd->h; // 補正
} //
if( v > pd->v ){ // 垂直幅を小の方に
v = pd->v; // 補正
} //
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationNonZeoBase( ptrs, ptrd, h, data ); // 水平方向処理
ptrs += inc_s; // 垂直方向に進める
ptrd += inc_d; // 垂直方向に進める
} //
return( END_STI ); // 正常終了
}
ローカル変数
){
BYTE *ptrs; // S画像ポインタ
BYTE *ptrd; // D画像ポインタ
int h; // 水平幅
int v; // 垂直幅
int inc_s; // S増加幅
int inc_d; // D増加幅
「BYTE *ptrs;」は、S(元)画像の実操作ポインタです!
「BYTE *ptrd;」は、D(結果)画像の実操作ポインタ
です!
「int h;」は、SD両画像の有効(最小)水平幅です!
「int v;」は、SD両画像の有効(最小)垂直幅です!
「int inc_s;」は、S(元)画像の増加幅!
「int inc_d;」は、D(結果)画像の増加幅!
「int h;int v;int inc_s;int inc_d;」は、は、内部で
使用するサイズに調整と使用し易い(高速化)の為に一旦、
内部の単純な変数に画像情報の水平・垂直・増加幅を代入
した物です!
アルゴリズムコード
if( ps->w != 1 || pd->w != 1 ){ // 画素単位=1以外
return( STI_ARY_5 ); // 左記を返す
} //
ptrs = (BYTE*)ps->adr; // SPtrを取り出し
ptrd = (BYTE*)pd->adr; // DPtrを取り出し
inc_s = ps->inc; // S増加幅を取出
inc_d = pd->inc; // D増加幅を取出
h = ps->h; // 水平/垂直幅を
v = ps->v; // 取り出し
if( h > pd->h ){ // 水平幅を小の方に
h = pd->h; // 補正
} //
if( v > pd->v ){ // 垂直幅を小の方に
v = pd->v; // 補正
} //
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationNonZeoBase( ptrs, ptrd, h, data ); // 水平方向処理
ptrs += inc_s; // 垂直方向に進める
ptrd += inc_d; // 垂直方向に進める
} //
return( END_STI ); // 正常終了
}
「if(ps->w!=1||pd->w!=1){return(STI_ARY_5);}」は、
SD両画像共画素単位が1バイトで無ければ、エラーコード
「STI_ARY_5」を返し関数終了!
「ptrs=(BYTE )ps->adr;ptrd=(BYTE )pd->adr;」は、
SD両画像共、実操作ポインタを仮引数「TypeArray ps,
TypeArray pd,」から、取り出し変数「BYTE *ptrs;
BYTE *ptrd;」にセット!
「inc_s=ps->inc;inc_d=pd->inc;」は、SD両画像の
増加幅を高速化する為にローカル変数にセット!
「h=ps->h;v=ps->v;」は、一旦、S画像の水平幅垂直幅を
取り出し、
「if(h>pd->h){h=pd->h;}」でD画像の水平幅との最小値を
有効幅として「h」にセット!
「if(v>pd->v){v=pd->v;}」でD画像の垂直幅との最小値を
有効幅として「v」にセット!
「while(--v>=0){・・ループ本体・・}」は、
ループ条件「--v>=0」で垂直幅の数分「ループ本体」を
ループ(繰り返す)処理です★備考★「while(--v>=0」の
様に「--v>=0」とデクリメント演算子で変数値が変化して
も影響が無い応用には、経験上最適なCPU機械コードが
生成出来るので、この画像処理ライブラリは、この記法を
多用して居ます!学校の教科書的には余り教えて無い(恐ら
く変数の値が変わる事を説明する必要が有るので説明がヤヤ
コシク成る事を嫌ったと推測)と思いますが、兎も角、高速
に動かすには、この記法がベストと考えて採用しています!
ループ条件「--v>=0」で垂直幅の数分繰り返すと言う事は、
水平方向の処理を垂直幅の数分繰り返すとの意味で
ループ本体「BinarizationNonZeoBase(
ptrs,ptrd,h,data);ptrs+=inc_s;ptrd+=inc_d;」は、
水平方向の処理としてサブルーチン
関数「BinarizationNonZeoBase(ptrs,ptrd,h,data);」で
処理し、「ptrs+=inc_s;ptrd+=inc_d;」でポインタを
増加幅分進めるとの意味で次の処理行へ移動との意味です!
勿論、S画像は「+=inc_s」・D画像は「+=inc_d」とそれ
ぞれ独立に増加します!
★備考★この様に外側ループで垂直方向を繰り返し「{}」と
ブロック内で水平方向の処理を行うと言う方法は、この画像
処理ライブラリで多用しているアルゴリズムコード記載法で
す!「ptrs+=inc_s;ptrd+=inc_d;」は、次の処理行へ移動と
の意味です!
★備考★この様に外側ループで垂直方向を繰り返し「{}」と
ブロック内で水平方向の処理を行うと言う方法は、
この画像処理ライブラリで多用しているアルゴリズムコード
記載法です!
★注意★順番的に後で解説する事に成りますが、
サブルーチン関数「BinarizationNonZeoBase()」は、
水平方向の処理で「画素≠0」の場合に
仮引数「int data」に画素を置き換える関数です!
「return(END_STI);」は、正常終了を返値として返し
関数終了
(4-14-14-E)関数「BinarizationNonZeo()」の【使用例】説明
{
TypeArray PatImg; // 画像型変数
// :編か前のパターン画像
TypeArray* pPatImg=&PatImg; // 上記へのポインタ
TypeArray Wimg; // 画像型変数
//:フィル書き込み用
TypeArray* pWimg=&Wimg; // 上記へのポインタ
//:部分画像用
TypeArray* pimgA=&PatImg; // 部分画像用
int sti; // ステータス情報
pPatImg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pPatImg, 0 ); // 画像メモリ「0クリア」
pWimg->MallocBYTE( 640, 480 ); // サイズ640×480画像メモリ取得
FUN.Clear( pWimg, 0 ); // 画像メモリ「0クリア」
// 典型的な2値化画像生成方法での処理
// サンプルパターン画像作成
pimgA->subset( pPatImg, 100, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
50, 30 ); // 部分画像を用意
FUN.Clear( pimgA, 100 ); // 部分画像を画素データ200でクリア
pimgA->subset( pPatImg, 200, 200, // 座標範囲(100,200)⇒(149,229)の範囲の
30, 50 ); // 部分画像を用意
FUN.Clear( pimgA, 200 ); // 部分画像を画素データ200でクリア
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile9.bmp", pPatImg );
FUN.BinarizationNonZeo( pPatImg, pWimg, t );// 今回紹介する関数「BinarizationNonZeo()」で
// 2値化
//作成した画像をBMP形式ファイルとして作成
FUN.WriteBitmap("testFile10.bmp", pWimg );
// 後始末
pPatImg->freeMem(); // 画像メモリ解放
pWimg->freeMem(); // 画像メモリ解放
}
★注意★
上記例文で注意して欲しいのは、LUTは、用意して無い事
です!関数「BinarizationNonZeo()」の中でLUTをローカ
ルに使用してLUT変換を内部で行って居る事です!
更に、黒画素「=0」・白画素を「≠0」と見なして結果の
白画素を「255」にします!
作成した画像ファイル
取り敢えず、本日(3月22)の講義は、ココまでにし
ます!何だか、!受講されている方もお疲れと思いますので
ココまでとします!
まだ、Noteサーバーが快適に動く分量ですから、
引き続き、この文章に続きは、載せます!
2024年3月25続講
(4-14-15)関数「void BinarizationXY(
TypeArray* ps,TypeArray* pd,
int x,int y,int h,int v,int t,
int data=255,int low=0);{・・・}」の説明
void CopyClear::BinarizationXY(
TypeArray* ps, // S配列(画像)
TypeArray* pd, // D配列(画像)
int x, // X座標
int y, // Y座標
int h, // 水平幅
int v, // 垂直幅
int t, // しきい値 0..255
int data, // 出力値:高位(省略時255)
int low // 出力値:低位(省略時0)
){
BYTE* ptrs; // S画像ポインタ
BYTE* ptrd; // D画像ポインタ
int incS; // S増加幅
int incD; // D増加幅
incS = ps->inc; // S増加幅を取出
incD = pd->inc; // D増加幅を取出
ptrs = (BYTE*)ps->adr + x + y * incS; // SPtrを取り出し
ptrd = (BYTE*)pd->adr + x + y * incD; // DPtrを取り出し
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationXYBase( ptrs, ptrd, h, // 水平方向処理
t, data, low ); //
ptrs += incS; // 垂直方向に進める
ptrd += incD; // 垂直方向に進める
} //
}
☆備考☆この関数はファイル「CopyClear030.cpp」に存在!
★注意★この関数は「public:」属性ですのでライブラリの
外から使用可能です!
※関数内のコメントに対する備考※「:C関数専用:」との
記載は、PIPLと言うコマンド言語からはアクセス出来る
様に設計して無いとの意味でC/C++で使用する場合は、
普通に使用出来る事を意味します!
(4-14-15-A)関数「BinarizationXY()」の【関数名】説明
void CopyClear::BinarizationXY(
TypeArray* ps, // S配列(画像)
TypeArray* pd, // D配列(画像)
int x, // X座標
int y, // Y座標
int h, // 水平幅
int v, // 垂直幅
int t, // しきい値 0..255
int data, // 出力値:高位(省略時255)
int low // 出力値:低位(省略時0)
){
「Binarization」は、二値化を意味し、
「XY」は、画像の部分画像を指定して部分画像を処理する
ことを意味します!
(4-14-15-B)関数「void BinarizationXY()」の【返値】説明
void CopyClear::BinarizationXY(
TypeArray* ps, // S配列(画像)
TypeArray* pd, // D配列(画像)
int x, // X座標
int y, // Y座標
int h, // 水平幅
int v, // 垂直幅
int t, // しきい値 0..255
int data, // 出力値:高位(省略時255)
int low // 出力値:低位(省略時0)
){
返値を返さない関数です!
★注意★詰り、実引数の検査を実行時に行いませんので
使用する時は、正しい実引数を記載する必要が有ります!
(4-14-15-C)関数「BinarizationXY()」の【仮引数】説明
void CopyClear::BinarizationXY(
TypeArray* ps, // S配列(画像)
TypeArray* pd, // D配列(画像)
int x, // X座標
int y, // Y座標
int h, // 水平幅
int v, // 垂直幅
int t, // しきい値 0..255
int data, // 出力値:高位(省略時255)
int low // 出力値:低位(省略時0)
){
「TypeArray* ps,」は、S(元)画像情報
「TypeArray* pd,」は、D(結果)画像情報
「int x,」は、処理範囲の始点X座標です
「int y,」は、処理範囲の始点Y座標です
「int h,」は、水平幅=水平方向の処理サイズです
「int v,」は、垂直幅=垂直方向の処理サイズです
「int t,」は、閾値(しきい値)と二値の切れ目です!
「int data,」は、白画素の値です!★注意★実引数の
省略時=255と8ビット画像最大値にして居ます!
「int low=0」は、黒画素の値です!★注意★実引数の
省略時=0と自然数最小値で表示上漆黒にして居ます!
勿論、論理的演算では、「FALSE=偽」として扱います!
(4-14-15-D)関数「BinarizationXY()」の【アルゴリズム】説明
){
BYTE* ptrs; // S画像ポインタ
BYTE* ptrd; // D画像ポインタ
int incS; // S増加幅
int incD; // D増加幅
incS = ps->inc; // S増加幅を取出
incD = pd->inc; // D増加幅を取出
ptrs = (BYTE*)ps->adr + x + y * incS; // SPtrを取り出し
ptrd = (BYTE*)pd->adr + x + y * incD; // DPtrを取り出し
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationXYBase( ptrs, ptrd, h, // 水平方向処理
t, data, low ); //
ptrs += incS; // 垂直方向に進める
ptrd += incD; // 垂直方向に進める
} //
}
ローカル変数
){
BYTE* ptrs; // S画像ポインタ
BYTE* ptrd; // D画像ポインタ
int incS; // S増加幅
int incD; // D増加幅
「BYTE *ptrs;」は、S(元)画像の実操作ポインタです!
「BYTE *ptrd;」は、D(結果)画像の実操作ポインタ
です!
「int incs;」は、S(元)画像の増加幅!
「int incd;」は、D(結果)画像の増加幅!
「int incs;int incd;」は、は、内部で使用し易い(高速化
)の為に内部の単純な変数に画像情報の増加幅を代入した物
です!
アルゴリズムコード
incS = ps->inc; // S増加幅を取出
incD = pd->inc; // D増加幅を取出
ptrs = (BYTE*)ps->adr + x + y * incS; // SPtrを取り出し
ptrd = (BYTE*)pd->adr + x + y * incD; // DPtrを取り出し
while( --v >= 0 ){ // 垂直方向に繰返し
BinarizationXYBase( ptrs, ptrd, h, // 水平方向処理
t, data, low ); //
ptrs += incS; // 垂直方向に進める
ptrd += incD; // 垂直方向に進める
} //
}
「incS=ps->inc;incD=pd->inc;」は、SD両画像の増加幅
を内部変数にセット!
「ptrs=(BYTE )ps->adr+x+yincS;」は、S画像の実操作
ポインタの初期値として処理始点を計算してセット!
「ptrd=(BYTE )pd->adr+x+yincD;」は、D画像の実操作
ポインタの初期値として処理始点を計算してセット!
「while(--v>=0){・・ループ本体・・}」は、
ループ条件「--v>=0」で垂直幅の数分「ループ本体」を
ループ(繰り返す)処理です
★備考★「while(--v>=0」の様に「--v>=0」とデクリメント
演算子で変数値が変化しても影響が無い応用には、
経験上最適なCPU機械コードが生成出来るので、
この画像処理ライブラリは、この記法を多用して居ます!
学校の教科書的には余り教えて無い(恐らく変数の値が変わ
る事を説明する必要が有るので説明がヤヤコシク成る事を
嫌ったと推測)と思いますが、兎も角、高速に動かすには、
この記法がベストと考えて採用しています!
ループ条件「--v>=0」で垂直幅の数分繰り返すと言う事は、
水平方向の処理を垂直幅の数分繰り返すとの意味でループ
本体は、「BinarizationXYBase(ptrs,ptrd,h,t,data,low);
」とサブルーチン「BinarizationXYBase()」で水平方向の
処理を行い、「ptrs+=incS;ptrd+=incD;」でポインタを
増加幅分進めるとの意味で次の処理行へ移動との意味です!
勿論、S画像は「+=incS」・D画像は「+=incD」と
それぞれ独立に増加します!
★備考★この様に外側ループで垂直方向を繰り返し「{}」と
ブロック内で水平方向の処理を行うと言う方法は、この画像
処理ライブラリで多用しているアルゴリズムコード記載法で
す!「ptrs+=incS;ptrd+=incD;」は、次の処理行へ移動と
の意味です!
★備考★この様に外側ループで垂直方向を繰り返し「{}」と
ブロック内で水平方向の処理を行うと言う方法は、
この画像処理ライブラリで多用しているアルゴリズムコード
記載法です!
★注意★順番的に後で解説する事に成りますが、
サブルーチン関数「BinarizationXYBase()」は、水平方向の
処理です!
取り敢えず、本日(3月25)の講義は、ココまでにし
ます!何だか、疲れたのだ!受講されている方もお疲れと
思いますのでココまでとします!
そして、この後は、単なるLUT変換系で無くカラー画像
に関係する関数を説明するので新しくして!
続きは、「解説クラスCopyClear(12)」に
成ります!引き続き御贔屓して受講して下さい!