見出し画像

解説クラス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」は、
メモリ取得を行ってイナイので取得して無いメモリを開放
するとシステムが変に成る事を考えて欲しい?!

作成した画像ファイル

testFile5.bmp

★注意★
本当は、ソースコードを実行して作れば、カクカクした階段
増加で無く滑らかなグラデーション諧調に成るのですが、
私が、ド貧乏人に成り、無料コンパイラも使用期限を越えて
プログラムを動作出来る環境が有りませんので仕方ないので
描画ソフト「ペイント」で手作業で作成した物ですが、
滑らかなグラデーション諧調の画像と考えて下さい!
尚、解説『投げ銭方式ライブラリのダウンロード』での
有料(投げ銭)まで読んで頂ければ、私に開発環境を恵んで
頂く費用を出す事に成ります!出来たら、開発環境の費用を
お恵み下さい!

testFile6.bmp
testFile7.bmp
testFile8.bmp

取り敢えず、本日(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」にします!

作成した画像ファイル

testFile9.bmp
testFile10.bmp

取り敢えず、本日(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)」に
成ります!引き続き御贔屓して受講して下さい!

文末

この記事が気に入ったらサポートをしてみませんか?