欅坂46から櫻坂46に改名した事を画像処理的に考察例文ソースコード説明
欅坂46から櫻坂46に改名した事を画像処理的に考察例文
ソースコード説明
2024年12月28初稿
ここのソースコードは、エッセイ『欅坂46から櫻坂46に
改名した事を画像処理的に考察』用です!上記エッセイの
ファイル「欅BMP白黒.bmp」・
ファイル「櫻BMP白黒.bmp」を使用します!
1.単純正規化相関比較
(1)画像処理ライブラリを使う前準備
#include"ImageFunc.h" // 画像処理ライブラリ用
ImageFunc FUN; // ライブラリ関数使用定義
main()
{
「#include"ImageFunc.h"」は、画像処理ライブラリの関数
(メソッド)群を使用可能に成る定義をインクルード
「ImageFunc FUN;」は、メソッドを実際に呼び出す、
例えば、ローカル変数とし
「int h;int v;int c;long xPixPerMeter;long xPiyPerMeter;
int sti;」を用意して
「sti=FUN.ReadBitmapAttribute("欅BMP白黒.bmp",
&h,&v,&c,&xPixPerMeter,&yPixPerMeter);」の様にメソッド
を実行するソースコードを記載出来、上記は、
画像ファイル「欅BMP白黒.bmp」の画像ファイル特徴と
して「&h,」は、画像水平幅(今回は、640の筈)、
「&v」は、画像垂直幅(今回は、480の筈)、
「,&c,」は、画像画素のビット幅≪「1」なら1ビット画素
なので2色、「8」なら8ビット(1バイト)画素なので
256色(色と表現だが「0・・255」と濃淡と言うか輝度で
今回は、8の筈)を「&変数」で変数にセットする!
「main(){」は、今回の例文のメインプログラムの書き出し
(2)画像ファイル読み出しとメインプログラム
main()
{
TypeArray imgA; // 画像型変数A
TypeArray* pimgA=&imgA; // 上記へのポインタ
TypeArray imgB; // 画像型変数B
TypeArray* pimgB=&imgB; // 上記へのポインタ
int h; // 水平幅
int v; // 垂直幅
int c; // Bit幅(1,4,8,16,24,32)
long xPixPerMeter; // 水平解像度(Pix/m)
long yPixPerMeter; // 垂直解像度(Pix/m)
sti=FUN.ReadBitmapAttribute( // ファイル情報読み出し
"欅BMP白黒.bmp", // ファイル「欅BMP白黒.bmp」情報読出
&h, &v, &c, // 今回、必要な情報格納変数
&xPixPerMeter, // 今回、不要な情報だが、引数とし
&yPixPerMeter); // てダミー使用
if( sti != STI_END ){ // エラー発生なら
printf("画像ファイル「欅BMP白黒.bmp」がオカシイ\n");
exit( 0 ); // C言語標準関数でプログラム終了
}
if( h != 640 || v != 480 || c != 8 ){ // 想定したファイル仕様と異なる
printf("画像ファイル「欅BMP白黒.bmp」がオカシイ\n");
printf( "h=%d,v=%d,c=%d\n", h, v, c );
exit( 0 ); // C言語標準関数でプログラム終了
}
pimgA->MallocBYTE (h,v); // 画像ファイルサイズで画像情報セット
FUN.Clear(pimgA,0); // 一旦、画像を0クリア
FUN.ReadBitmap("欅BMP白黒.bmp", // ファイル「欅BMP白黒.bmp」を
pimgA ); // 画像とし画像「imgA」に格納
sti=FUN.ReadBitmapAttribute( // ファイル情報読み出し
"櫻BMP白黒.bmp", // ファイル「櫻BMP白黒.bmp」情報読出
&h, &v, &c, // 今回、必要な情報格納変数
&xPixPerMeter, // 今回、不要な情報だが、引数とし
&yPixPerMeter); // てダミー使用
if( sti != STI_END ){ // エラー発生なら
printf("画像ファイル「櫻BMP白黒.bmp」がオカシイ\n");
pimgA->freeMem(); // 画像メモリ解放画像「imgA」
exit( 0 ); // C言語標準関数でプログラム終了
}
if( h != 640 || v != 480 || c != 8 ){ // 想定したファイル仕様と異なる
printf("画像ファイル「櫻BMP白黒.bmp」がオカシイ\n");
pimgA->freeMem(); // 画像メモリ解放画像「imgA」
exit( 0 ); // C言語標準関数でプログラム終了
}
pimgB->MallocBYTE (h,v); // 画像ファイルサイズで画像情報セット
FUN.Clear(pimgB,0); // 一旦、画像を0クリア
FUN.ReadBitmap("櫻BMP白黒.bmp", // ファイル「櫻BMP白黒.bmp」を
pimgB ); // 画像とし画像「imgB」に格納
execute( pimgA, pimgB ); // 例文の実行部
pimgA->freeMem(); // 画像メモリ解放画像「imgA」
pimgB->freeMem(); // 画像メモリ解放画像「imgB」
}
「main(){・・本体・・}」は、ご存知、C言語標準のメイン
プログラムを記載する方法!
(2-1)ローカル変数
{
TypeArray imgA; // 画像型変数A
TypeArray* pimgA=&imgA; // 上記へのポインタ
TypeArray imgB; // 画像型変数B
TypeArray* pimgB=&imgB; // 上記へのポインタ
int h; // 水平幅
int v; // 垂直幅
int c; // Bit幅(1,4,8,16,24,32)
long xPixPerMeter; // 水平解像度(Pix/m)
long yPixPerMeter; // 垂直解像度(Pix/m)
「TypeArray imgA;」は、画像情報「imgA」の定義!
「TypeArray* pimgA=&imgA;」は、画像情報ポインタ変数
「TypeArray* pimgA」に画像情報「imgA」をセット
「TypeArray imgB;」は、画像情報「imgB」の定義!
「TypeArray* pimgB=&imgB;」は、画像情報ポインタ変数
「TypeArray* pimgB」に画像情報「imgB」をセット
「int h;」は、画像の水平幅
「int v;」は、画像の垂直幅
「int c;」は、画像ファイルの画素ビット幅
「long xPixPerMeter;」は、画像ファイルの水平解像度
「long yPixPerMeter;」は、画像ファイルの垂直解像度
(2-2)アルゴリズム
sti=FUN.ReadBitmapAttribute( // ファイル情報読み出し
"欅BMP白黒.bmp", // ファイル「欅BMP白黒.bmp」情報読出
&h, &v, &c, // 今回、必要な情報格納変数
&xPixPerMeter, // 今回、不要な情報だが、引数とし
&yPixPerMeter); // てダミー使用
if( sti != STI_END ){ // エラー発生なら
printf("画像ファイル「欅BMP白黒.bmp」がオカシイ\n");
exit( 0 ); // C言語標準関数でプログラム終了
}
if( h != 640 || v != 480 || c != 8 ){ // 想定したファイル仕様と異なる
printf("画像ファイル「欅BMP白黒.bmp」がオカシイ\n");
printf( "h=%d,v=%d,c=%d\n", h, v, c );
exit( 0 ); // C言語標準関数でプログラム終了
}
pimgA->MallocBYTE (h,v); // 画像ファイルサイズで画像情報セット
FUN.Clear(pimgA,0); // 一旦、画像を0クリア
FUN.ReadBitmap("欅BMP白黒.bmp", // ファイル「欅BMP白黒.bmp」を
pimgA ); // 画像とし画像「imgA」に格納
sti=FUN.ReadBitmapAttribute( // ファイル情報読み出し
"櫻BMP白黒.bmp", // ファイル「櫻BMP白黒.bmp」情報読出
&h, &v, &c, // 今回、必要な情報格納変数
&xPixPerMeter, // 今回、不要な情報だが、引数とし
&yPixPerMeter); // てダミー使用
if( sti != STI_END ){ // エラー発生なら
printf("画像ファイル「櫻BMP白黒.bmp」がオカシイ\n");
pimgA->freeMem(); // 画像メモリ解放画像「imgA」
exit( 0 ); // C言語標準関数でプログラム終了
}
if( h != 640 || v != 480 || c != 8 ){ // 想定したファイル仕様と異なる
printf("画像ファイル「櫻BMP白黒.bmp」がオカシイ\n");
pimgA->freeMem(); // 画像メモリ解放画像「imgA」
exit( 0 ); // C言語標準関数でプログラム終了
}
pimgB->MallocBYTE (h,v); // 画像ファイルサイズで画像情報セット
FUN.Clear(pimgB,0); // 一旦、画像を0クリア
FUN.ReadBitmap("櫻BMP白黒.bmp", // ファイル「櫻BMP白黒.bmp」を
pimgB ); // 画像とし画像「imgB」に格納
execute( pimgA, pimgB ); // 例文の実行部
pimgA->freeMem(); // 画像メモリ解放画像「imgA」
pimgB->freeMem(); // 画像メモリ解放画像「imgB」
}
「sti=FUN.ReadBitmapAttribute("欅BMP白黒.bmp",
&h,&v,&c,&xPixPerMeter,&yPixPerMeter);」は、
画像ファイル「欅BMP白黒.bmp」のファイルとしての諸元
情報≪画像としての水平幅・垂直幅・画素のビット単位・
水平垂直の解像度≫を取得する関数です!
「if( sti != STI_END ){printf(メッセージ);exit(0);}」
は、エラー発生時にエラーメッセージ≪ここでは、
「("画像ファイル「欅BMP白黒.bmp」がオカシイ」≫を
表示し、「exit(0);」でプログラム終了!
「if(h!=640||v!=480||c!=8){printf(メッセージ);
exit(0);}」は、
条件「h!=640||v!=480||c!=8」で例文用に用意した画像ファ
イル「欅BMP白黒.bmp」なら水平幅=640、垂直幅=480、
画素のビット数=8に成る筈が異なるのでエラーと判断して
「printf(メッセージ);」とエラーメッセージを出して
因みにメッセージ
「("画像ファイル「欅BMP白黒.bmp」がオカシイ」です!
「exit(0);」でプログラム終了!
「pimgA->MallocBYTE(h,v);FUN.Clear(pimgA,0);」は、
画像メモリ「pimgA」実態を動的に確保し、0クリア、
「FUN.ReadBitmap("欅BMP白黒.bmp",pimgA);」は、
画像メモリ「pimgA」に画像ファイル「欅BMP白黒.bmp」
の画像を読み出す!
「sti=FUN.ReadBitmapAttribute("櫻BMP白黒.bmp",
&h,&v,&c,&xPixPerMeter,&yPixPerMeter);」は、
画像ファイル「櫻BMP白黒.bmp」のファイルとしての諸元
情報≪画像としての水平幅・垂直幅・画素のビット単位・
水平垂直の解像度≫を取得する関数です!
「if( sti != STI_END ){printf(メッセージ);
pimgA->freeMem();exit(0);}」
は、エラー発生時にエラーメッセージ≪ここでは、
「("画像ファイル「櫻BMP白黒.bmp」がオカシイ」≫を
表示し、
「pimgA->freeMem();」は、画像メモリ解放「imgA」
「exit(0);」でプログラム終了!
「if(h!=640||v!=480||c!=8){printf(メッセージ);
pimgA->freeMem();exit(0);}」は、
条件「h!=640||v!=480||c!=8」で例文用に用意した画像ファ
イル「櫻BMP白黒.bmp」なら水平幅=640、垂直幅=480、
画素のビット数=8に成る筈が異なるのでエラーと判断して
「printf(メッセージ);」とエラーメッセージを出して
因みにメッセージ
「("画像ファイル「櫻BMP白黒.bmp」がオカシイ」です!
「pimgA->freeMem();」は、画像メモリ解放「imgA」
「exit(0);」でプログラム終了!
「pimgB->MallocBYTE(h,v);FUN.Clear(pimgB,0);」は、
画像メモリ「pimgB」実態を動的に確保し、0クリア、
「FUN.ReadBitmap("櫻BMP白黒.bmp",pimgB);」は、
画像メモリ「pimgB」に画像ファイル「櫻BMP白黒.bmp」
の画像を読み出す!
「execute( pimgA, pimgB );」は、実際の処理部
「pimgA->freeMem();」は、画像メモリ解放「imgA」
「pimgB->freeMem();」は、画像メモリ解放「imgB」
2.execute(pimgA,pimgB);実際の処理部
void execute(
TypeArray* pimgA, // テンプレート画像全体
TypeArray* pimgB // サーチ対象画像
){
TypeXY bufXY[1000]; // 座標結果格納バッファー
double buf[1000]; // 相関値結果格納バッファー
short bufix[1000]; // ソート用補助バッファー
TypeXY* pXY; // 座標結果格納バッファー実行ポインタ
double* pbuf; // 相関値結果格納バッファー実行ポインタ
BYTE* ptrA; // A画像:処理ポインタ
BYTE* ptrBX; // B画像:処理ポインタX座標方向
BYTE* ptrBY; // B画像:処理ポインタY座標方向
int incA; // A画像:増加幅
int incB; // B画像:増加幅
int x; // マッチング用部分画像X座標
int y; // マッチング用部分画像Y座標
int n; // マッチング用部分水平幅(N×MのN)
int m; // マッチング用部分垂直幅(N×MのM)
int h; // マッチング用画像水平幅
int v; // マッチング用画像垂直幅
int stepX; // マッチング用画像X座標移動ステップ
int stepY; // マッチング用画像Y座標移動ステップ
int cnt; // 計測個数カウント
double data; // 相関値≒一致率
getPatternSize( pimgA, x, y, n, m ); // テンプレート画像サイズ範囲取得
ptrA = pimgA->getPtrByte( x, y ); // A画像:テンプレート画像ポインタをセット
ptrBY = pimgB->getPtrByte( 0, 0 ); // B画像:サーチ画像ポインタを画像左上隅セット
h = pimgB->h; // B画像:サーチ画像の水平幅
v = pimgB->v; // B画像:サーチ画像の垂直幅
incA = pimgA->inc; // A画像:増加幅ローカル変数セット
incB = pimgB->inc; // B画像:増加幅ローカル変数セット
stepX = 1; // テンプレートマッチングする座標移動量を
stepY = 1; // 1画素単位≪備考:処理時間甚大だが、
// 説明用なので1に設定:後で高速化説明予定≫
pXY = bufXY; // 座標結果格納バッファー実行ポインタ
pbuf = buf; // 相関値結果格納バッファー実行ポインタ
cnt = 0; // 個数カウント初期化
for( y = 0; y < v; y += stepY ){ // 外側ループとし垂直方向処理
ptrBX = ptrBY; // 水平方向サーチ原点セット
for( x = 0; x < h; x += stepX ){ // 内側ループとし水平方向処理
data = FUN.CorrNM( ptrA, ptrBX, // n×m画素の正規相関を
incA, incB, n, m ); // 算出し
printf( "data=%e,(%d,%d)\n", // 例文なので途中結果(一致率、座標)表示
data, x, y ); //
if( abs(data) > 0.5){ // 条件「絶対値(相関値)が0.5超え」
if( cnt < 1000 ){ // 再条件「格納数が1000未満」の場合で
cnt++; // 格納数をカウントアップ
*pbuf++ = data; // バッファーに格納
pXY->x = x; // X座標格納
pXY->y = y; // Y座標格納
pXY++; // 座標結果格納バッファー実行ポインタ増加
} //
} //
ptrBX += stepX; // サーチ画像ポインタを水平方向増加
} //
ptrBY += incB * stepY; // サーチ画像ポインタを垂直方向増加
}
FUN.SortIndex( 1, buf, bufix, cnt ); // リンク用インデックス付きで相関値で降順≪
// 大⇒小≫にソーティング
FUN.SortCopy( bufXY, bufix, cnt ); // 座標結果も連動してソーティング
for( int i=0; i < cnt; i++ ){ // ソーティングした結果バッファーをインデックスで
printf( "data=%e,(%d,%d)\n", // 例文なのでソーティング結果(一致率、座標)
buf[i], bufXY[i].x, bufXY[i].y ); // 表示
} // ループ
}
(1)関数名
「execute」英単語ソノママ「実行する」です!
(2)仮引数
void execute(
TypeArray* pimgA, // テンプレート画像全体
TypeArray* pimgB // サーチ対象画像
){
「TypeArraypimgA,」は、テンプレート画像全体
「TypeArraypimgB 」は、サーチ対象画像
(3)ローカル変数
){
TypeXY bufXY[1000]; // 座標結果格納バッファー
double buf[1000]; // 相関値結果格納バッファー
short bufix[1000]; // ソート用補助バッファー
TypeXY* pXY; // 座標結果格納バッファー実行ポインタ
double* pbuf; // 相関値結果格納バッファー実行ポインタ
BYTE* ptrA; // A画像:処理ポインタ
BYTE* ptrBX; // B画像:処理ポインタX座標方向
BYTE* ptrBY; // B画像:処理ポインタY座標方向
int incA; // A画像:増加幅
int incB; // B画像:増加幅
int x; // マッチング用部分画像X座標
int y; // マッチング用部分画像Y座標
int n; // マッチング用部分水平幅(N×MのN)
int m; // マッチング用部分垂直幅(N×MのM)
int h; // マッチング用画像水平幅
int v; // マッチング用画像垂直幅
int stepX; // マッチング用画像X座標移動ステップ
int stepY; // マッチング用画像Y座標移動ステップ
int cnt; // 計測個数カウント
double data; // 相関値≒一致率
「TypeXY bufXY[1000];」は、XY座標結果バッファー
「double buf[1000];」は、相関値結果バッファー
「short bufix[1000];」は、ソーティング用補助バッファー
「TypeXY* pXY;」は、XY座標結果バッファー操作ポインタ
「double* pbuf;」は、相関値結果バッファー操作ポインタ
「BYTE* ptrA;」は、テンプレート画像処理ポインタ
「BYTE* ptrBX;」は、サーチ対象画像X座標処理ポインタ
「BYTE* ptrBY;」は、サーチ対象画像Y座標処理ポインタ
「int incA;」は、テンプレート画像の垂直方向増加幅
「int incB;」は、サーチ対象画像の垂直方向増加幅
「int x;」は、サーチ対象画像のX座標
「int y;」は、サーチ対象画像のY座標
「int n;」は、正規相関N×MのN
「int m;」は、正規相関N×MのM
「int h;」は、サーチ対象画像の水平幅
「int v;」は、サーチ対象画像の垂直幅
「int stepX;」は、サーチ対象への水平方向画素進行単位
「int stepY;」は、サーチ対象への垂直方向画素進行単位
「int cnt;」は、計測個数
「double data;」は、相関値
(4)アルゴリズム
getPatternSize( pimgA, x, y, n, m ); // テンプレート画像サイズ範囲取得
ptrA = pimgA->getPtrByte( x, y ); // A画像:テンプレート画像ポインタをセット
ptrBY = pimgB->getPtrByte( 0, 0 ); // B画像:サーチ画像ポインタを画像左上隅セット
h = pimgB->h; // B画像:サーチ画像の水平幅
v = pimgB->v; // B画像:サーチ画像の垂直幅
incA = pimgA->inc; // A画像:増加幅ローカル変数セット
incB = pimgB->inc; // B画像:増加幅ローカル変数セット
stepX = 1; // テンプレートマッチングする座標移動量を
stepY = 1; // 1画素単位≪備考:処理時間甚大だが、
// 説明用なので1に設定:後で高速化説明予定≫
pXY = bufXY; // 座標結果格納バッファー実行ポインタ
pbuf = buf; // 相関値結果格納バッファー実行ポインタ
cnt = 0; // 個数カウント初期化
for( y = 0; y < v; y += stepY ){ // 外側ループとし垂直方向処理
ptrBX = ptrBY; // 水平方向サーチ原点セット
for( x = 0; x < h; x += stepX ){ // 内側ループとし水平方向処理
data = FUN.CorrNM( ptrA, ptrBX, // n×m画素の正規相関を
incA, incB, n, m ); // 算出し
printf( "data=%e,(%d,%d)\n", // 例文なので途中結果(一致率、座標)表示
data, x, y ); //
if( abs(data) > 0.5){ // 条件「絶対値(相関値)が0.5超え」
if( cnt < 1000 ){ // 再条件「格納数が1000未満」の場合で
cnt++; // 格納数をカウントアップ
*pbuf++ = data; // バッファーに格納
pXY->x = x; // X座標格納
pXY->y = y; // Y座標格納
pXY++; // 座標結果格納バッファー実行ポインタ増加
} //
} //
ptrBX += stepX; // サーチ画像ポインタを水平方向増加
} //
ptrBY += incB * stepY; // サーチ画像ポインタを垂直方向増加
}
FUN.SortIndex( 1, buf, bufix, cnt ); // リンク用インデックス付きで相関値で降順≪
// 大⇒小≫にソーティング
FUN.SortCopy( bufXY, bufix, cnt ); // 座標結果も連動してソーティング
for( int i=0; i < cnt; i++ ){ // ソーティングした結果バッファーをインデックスで
printf( "data=%e,(%d,%d)\n", // 例文なのでソーティング結果(一致率、座標)
buf[i], bufXY[i].x, bufXY[i].y ); // 表示
} // ループ
}
「getPatternSize(pimgA,x,y,n,m);」は、下請け関数
「getPatternSize()」でパターン画像「pimgA」の
範囲「x,y,n,m」を算出
「ptrA=pimgA->getPtrByte(x,y);」は、パターン画像の
先頭(左上隅)の画像画素ポインタをセット
「ptrBY=pimgB->getPtrByte(0,0);」は、サーチ対象画像の
先頭(左上隅)の画像画素ポインタをセット
「h=pimgB->h;v=pimgB->v;」は、サーチ対象画像の
水平幅・垂直幅をローカル変数にセット
「incA=pimgA->inc;incB=pimgB->inc;」は、パターン画像と
サーチ対象画像の増加幅≪垂直方向に増加する為の幅≫を
ローカル変数にセット
「stepX=1;stepY=1;」は、パターン画像とサーチ対象画像の
マッチング≪相関演算≫する移動画素単位をセット、ここで
は1画素と最小の値をセット
「pXY=bufXY;」は、XY座標結果バッファーへのポインタを
セット
「pbuf=buf;」は、相関値結果バッファーへのポインタを
セット
「cnt=0;」は、有効相関値結果個数を初期化=0
「for(y=0;y<v;y+=stepY){・・外側ループ・・}」は、
forループ「(y=0;y<v;y+=stepY)」でY座標方向に繰り返
し初期値「y=0;」でループ条件「y<v;」Y座標が垂直幅未満
で繰り返し、増分「y+=stepY」でY座標を増加し、ループ
中身を繰り返す!その外側ループ中身は
「ptrBX=ptrBY;」は、水平方向の画像画素処理始点セット
「for(x=0;x<h;x+=stepX){・・内側ループ・・}」は、
forループ「(x=0;x<h;x+=stepX)」でX座標方向に繰り返
し初期値「x=0;」でループ条件「x<h;」X座標が水平幅未満
で繰り返し、増分「x+=stepX」でX座標を増加し、ループ
中身を繰り返す!その内側ループ中身は
「data=FUN.CorrNM(ptrA,ptrBX,incA,incB,n,m);」は、
下請け関数「FUN.CorrNM(ptrA,ptrBX,incA,incB,n,m)」で
「FUN.」と画像処理ライブラリ関数の「CorrNM()」でN×M
サイズ≪ココではN×MのN=「n」・M=「m」≫で正規相
関算出し答えの相関値を「data」にセット、
「printf("data=%e,(%d,%d)\n",data,x,y);」は、デバッグ
用に途中結果を表示
「if(abs(data)>0.5){・・外側分岐本体・・}」は、
条件「abs(data)>0.5」で相関値の絶対値≪相関値は負の数
に成る場合有り≫が「0.5」を超える場合は有効とし外側
分岐本体を処理、その本体で
「if(cnt<1000){
cnt++;*pbuf++=data;pXY->x=x;pXY->y=y;pXY++;}」は、
条件「cnt<1000」と計測個数が「1000」未満なら、
「cnt++;」で計測個数をカウントアップ、
「*pbuf++=data;」で相関値結果バッファーに相関値を
追加、
「pXY->x=x;pXY->y=y;」でXY座標を座標結果バッファーに
追加、「pXY++;」でXY座標を座標結果バッファーを指す
ポインタを増加、if分岐が外側内側と終わったら、
「ptrBX+=stepX;」で水平方向(X座標方向)ポインタ増加
forループ内側が終わったら、
「ptrBY+=incBstepY;」で垂直方向(Y座標方向)ポインタ
増加≪「incBstepY」で垂直方向への増分を算出≫
forループ外側が終わったら、
「FUN.SortIndex(1,buf,bufix,cnt);」は、相関値をキー
ソートのキーとして下請け関数「FUN.SortIndex()」で
「FUN.」と画像処理ライブラリ関数の「SortIndex()」で
「1,」と降順≪大⇒小に整列≫に「buf,」と相関値結果の値
で整列し同時に「bufix,」と連動インデックスを整列し、
そのソートする数は「cnt」でソーティングする!
「FUN.SortCopy(bufXY,bufix,cnt);」は、連動インデックス
「bufix,」を使用して座標結果バッファー「bufXY,」を
「cnt」数分整列する!
「for(int i=0;i<cnt;i++){・・表示ループ・・}」は、
forループ「for(int i=0;i<cnt;i++)」で
内部添字「int i=0;」とループカウンタを初期化し、
ループ条件「i<cnt;」と整列したバッファーをアクセスして
「printf("data=%e,(%d,%d)\n",buf[i],bufXY[i].x,
bufXY[i].y);」と相関値で降順≪大⇒小≫に成った整列デー
タをデバッグ用に表示します
2025年1月2変更
「*buf++=data;」で相関値結果バッファーに相関値を追加を
「*pbuf++=data;」で相関値結果バッファーに相関値を
に修正しました
3.getPatternSize();範囲「x,y,n,m」算出
void getPatternSize( pimgA, x, y, h, v )
TypeArray* pimgA, // テンプレート画像全体
int& x, // マッチング用部分画像X座標始点
int& y, // マッチング用部分画像Y座標始点
int& h, // マッチング用部分水平幅
int& v // マッチング用部分垂直幅
){
TypeArray BinImg; // 2値化画像
TypeArray* pBinImg=&BinImg; // 2値化画像情報へのポインタ
TypeMB bufMB[1000]; // 図形計測バッファー
TypeMB *pMB; // 上記へのポインタ
long* buf[256]; // ヒストグラム(度数分布)バッファー
int t; // 2値化しきい値
int n; // 計測図形個数
int xy; // XY座標補助
int x1; // X座標左側
int x2; // X座標右側
int y1; // Y座標上側
int y2; // Y座標下側
FUN.HistogramImage( pimgA, buf ); // 画像のヒストグラム算出
t = FUN.GetBinOotu( buf, 256 ); // 大津法で2値化しきい値作成
pBinImg->MallocByte( pimgA->h, pimgA->v); // 2値化画像の実体メモリ動的確保
FUN.Binarization( pimgA, pBinImg, t, 1 ); // 2値化(0,1)
pMB = bufMB; // 図形計測バッファーポインタセット
n = FUN.MeasureFere( 8, pBinImg, pMB, // 図形計測としフェレ計測を行う
1000 ); //
printf( "n=%d\n", n ); // デバッグ用計測図形数表示
x1 = pimgA->h; // X座標左側初期化
x2 = 0; // X座標右側初期化
y1 = pimgA->v; // Y座標上側初期化
y2 = 0; // Y座標下側初期化
for( int i = 0; i < n; i++, pMB++ ){ // 計測結果内をループして
x1 = ( x1 > pMB->x ) ? pMB->x : x1; // X座標左側更新
y1 = ( y1 > pMB->y ) ? pMB->y : y1; // Y座標上側更新
xy = pMB->x + pMB->h - 1; // フェレ計測X座標右側算出
x2 = ( x2 < xy ) ? xy : x2; // X座標右側更新
xy = pMB->y + pMB->v - 1; // フェレ計測Y座標下側算出
y2 = ( y2 < xy ) ? xy : y2; // Y座標下側更新
} //
printf("左上隅(%d,%d)⇒右下隅(%d,%d)\n", // デバッグ用計測座標表示
x1, y1, x2, y2 ); //
x = x1; // 引数返値としX座標セット
y = y1; // 引数返値としY座標セット
h = x2 - x1 + 1; // 引数返値とし水平幅セット
v = y2 - y1 + 1; // 引数返値とし垂直幅セット
pBinImg->freeMem(); // 動的確保メモリ解放
}
(1)関数名
「get」は、カタカナ語「ゲット」で取得するです!
「Pattern」は、カタカナ語「パターン」でパターン画像を
意味
「Size」は、カタカナ語「サイズ」で「PatternSize」で
パターン画像のサイズ≒範囲を算出し、
「getPatternSize」でパターン画像の範囲とサイズ取得
(2)仮引数
void getPatternSize( pimgA, x, y, h, v )
TypeArray* pimgA, // テンプレート画像全体
int& x, // マッチング用部分画像X座標始点
int& y, // マッチング用部分画像Y座標始点
int& h, // マッチング用部分水平幅
int& v // マッチング用部分垂直幅
){
「TypeArray*pimgA,」は、テンプレート画像全体
「int& x,」は、X座標始点
「int& y,」は、Y座標始点
「int& h,」は、水平幅
「int& v」は、垂直幅
(3)ローカル変数
){
TypeArray BinImg; // 2値化画像
TypeArray* pBinImg=&BinImg; // 2値化画像情報へのポインタ
TypeMB bufMB[1000]; // 図形計測バッファー
TypeMB *pMB; // 上記へのポインタ
long* buf[256]; // ヒストグラム(度数分布)バッファー
int t; // 2値化しきい値
int n; // 計測図形個数
int xy; // XY座標補助
int x1; // X座標左側
int x2; // X座標右側
int y1; // Y座標上側
int y2; // Y座標下側
「TypeArray BinImg;」は、2値化画像情報
「TypeArray* pBinImg=&BinImg;」は、上記へのポインタ
セット
「TypeMB bufMB[1000];」は、2値化画像の図形計測バッ
ファー≪備考:少し前、エッセイ『
この仕事は天職だと感じたアルゴリズム開発(続1)』で
使用した事を説明した型「TypeSf」と言う計測結果格納用
型と同じ事が出来るが項目を必要最小限に絞った
型「TypeMB」にも関数「MeasureFere()」とフェレ計測を行
うオーバーロード(多重定義)サレタ関数で使用しますが、
バッファーのサイズが小さく成る為にココでは使用しました
解説『解説クラスTypeSf』に「TypeSf」・「TypeMB」
の説明は記載しています!≫
「TypeMB* pMB;」は、バッファー「TypeMB bufMB[1000];」
のポインタアクセス用ポインタです
「long* buf[256];」は、ヒストグラム(度数分布)算出用
≪画像画素のヒストグラムなので1バイト画素=「0」から
「255」の値の画素なので配列として256個用意≫
「int t;」は、2値化しきい値≪自然多値画像=「画素が
0..255」を2値化画像=「黒(0)か白(1とか255)」に
変換≫用の値
「int n;」は、図形計測した数で図形≪連結した塊≫事の
計測した図形自体の数
「int xy;」は、XY座標を計算する途中結果
「int x1;」は、X座標左側
「int x2;」は、X座標右側
「int y1;」は、Y座標上側
「int y2;」は、Y座標下側
(4)アルゴリズム
FUN.HistogramImage( pimgA, buf ); // 画像のヒストグラム算出
t = FUN.GetBinOotu( buf, 256 ); // 大津法で2値化しきい値作成
pBinImg->MallocByte( pimgA->h, pimgA->v); // 2値化画像の実体メモリ動的確保
FUN.Binarization( pimgA, pBinImg, t, 1 ); // 2値化(0,1)
pMB = bufMB; // 図形計測バッファーポインタセット
n = FUN.MeasureFere( 8, pBinImg, pMB, // 図形計測としフェレ計測を行う
1000 ); //
printf( "n=%d\n", n ); // デバッグ用計測図形数表示
x1 = pimgA->h; // X座標左側初期化
x2 = 0; // X座標右側初期化
y1 = pimgA->v; // Y座標上側初期化
y2 = 0; // Y座標下側初期化
for( int i = 0; i < n; i++, pMB++ ){ // 計測結果内をループして
x1 = ( x1 > pMB->x ) ? pMB->x : x1; // X座標左側更新
y1 = ( y1 > pMB->y ) ? pMB->y : y1; // Y座標上側更新
xy = pMB->x + pMB->h - 1; // フェレ計測X座標右側算出
x2 = ( x2 < xy ) ? xy : x2; // X座標右側更新
xy = pMB->y + pMB->v - 1; // フェレ計測Y座標下側算出
y2 = ( y2 < xy ) ? xy : y2; // Y座標下側更新
} //
printf("左上隅(%d,%d)⇒右下隅(%d,%d)\n", // デバッグ用計測座標表示
x1, y1, x2, y2 ); //
x = x1; // 引数返値としX座標セット
y = y1; // 引数返値としY座標セット
h = x2 - x1 + 1; // 引数返値とし水平幅セット
v = y2 - y1 + 1; // 引数返値とし垂直幅セット
pBinImg->freeMem(); // 動的確保メモリ解放
}
「FUN.HistogramImage(pimgA,buf);」は、ヒストグラム算出
を画像処理ライブラリ関数「FUN.HistogramImage()」で
接頭語「FUN.」と画像処理ライブラリ関数を使う事を示し、
実引数「pimgA,buf」と画像「pimgA」のヒストグラム算出(
度数分布)し結果バッファー「buf」に格納
「t=FUN.GetBinOotu(buf,256);」は、画像処理ライブラリ
関数「GetBinOotu()」でヒストグラム結果バッファー「buf
」から大津法≪恐らく統計学の大昔の教授の名前から命名と
思う≫と言う統計的手法で2値化しきい値を算出!
「pBinImg->MallocByte(pimgA->h,pimgA->v);」は、画像処
理ライブラリ関数「MallocByte()」で動的に画像メモリ実体
を確保≪解説『解説クラスTypeArray』参考≫し
「FUN.Binarization(pimgA,pBinImg,t,1);」は、画像処理ラ
イブラリ関数「Binarization()」で多値画像「pimgA」を
2値化画像「pBinImg」にしきい値「t」未満を「0」に、
「t」以上を「1」に2値化画像変換を行う!
「pMB=bufMB;」は、図形計測バッファーポインタをセット
「n=FUN.MeasureFere(8,pBinImg,pMB,1000);」は、8連結で
2値化画像「pBinImg」の図形計測としてフェレ計測を行い
結果バッファー「pMB」に計測した図形情報を格納し、図形
の個数を「n」にセット
「printf("n=%d\n",n);」は、デバッグ用の図形個数を表示
≪備考:今回使用した「欅」の画像ファイルでは漢字のツナ
ガル画素が8連結では全てツナガル様に思うので恐らく、
実行すれば「n=1」と表示される筈です!しかしながら、私
がド貧乏人で使用出来る環境【ウィンドウズ11で動作する
ビジュアルスタジオ11でのコンパイル⇒実行を1年間無償
期間にウィンドウズXPで快適に動いていたビジュアルスタ
ジオ6.0と同じくダイアログベースで例文作成に挑んだが
ダイアログ用のプログラムを作成する糸口も脳梗塞経験者で
衰えたか継続して訓練が出来て無いので期限切れで過去に
サードパーティー無償コンパイラで画像ファイルベースでの
作成「これは、別のアカウントで発表しました」も既に無償
期間を超えて予算が無いので使用不能】と言う残念な状態で
実際にコンパイルも動かしてもいませんので多分動くとしか
言いようが有りませんので使用される読者様が確認出来る様
に「printf()」を挟んでいます!と言う事で「n=1」では、
1個の図形として図形のフェレ計測情報は一組だけですが、
以下のプログラムコードの様に複数個の個数が出た場合に
対応可能な処理を記載しました≫
「x1=pimgA->h;」は、複数個フェレ計測結果が有るとしての
最左側を計測する変数を真逆の右側を初期値セット
「x2=0;」は、最右を計測する変数を真逆の「0」を初期値
セット
「y1=pimgA->v;」は、最上側を計測する変数を真逆の下側を
初期値セット
「y2=0;」は、最下を計測する変数を真逆の右側を初期値
セット
「for(int i=0;i<n;i++,pMB++){・・座標最大最小・・}」
は、forループ「for(int i=0;i<n;i++,pMB++)」で
初期化「int i=0;」でループカウンタ初期化し、
ループ条件「i<n;」で計測結果個数分繰り返し、
増分「i++,pMB++」でループカウンタ「i」増加と計測結果
バッファー「bufMB」へのポインタ「pMB」を進行させ座標
最大最小の中身、
「x1=(x1>pMB->x)?pMB->x:x1;」でX座標最小値更新
「y1=(y1>pMB->y)?pMB->y:y1;」でY座標最小値更新
「xy=pMB->x+pMB->h-1;x2=(x2<xy)?xy:x2;」の
「xy=pMB->x+pMB->h-1;」でX座標右側を算出し、
「x2=(x2<xy)?xy:x2;」でX座標最大値更新
「xy=pMB->y+pMB->v-1;y2=(y2<xy)?xy:y2;」の
「xy=pMB->y+pMB->v-1;」でY座標上側を算出し、
「y2=(y2<xy)?xy:y2;」でY座標最大値更新
「printf("左上隅(%d,%d)⇒右下隅(%d,%d)\n",
x1,y1,x2,y2);」は、デバッグ用表示です
「x=x1;y=y1;」は、XY座標最小値(左上隅)の値を引数
辺値とし返す
「h=x2-x1+1;v=y2-y1+1;」は、パターン画像の水平垂直幅
を算出し、引数辺値とし返す
「pBinImg->freeMem();」は、内部で動的にメモリ確保した
2値化画像の実体メモリを解放≪OSに返す≫
4.考察
この画像処理ライブラリ関数を使用したソースコードを
コンパイルして実行すると「欅」と「櫻」の画像として
見たら、一致率「execute(pimgA,pimgB);実際の処理部」の
中のデバッグ用に入れた「printf()」での表示で一致率が
フォーマット「%e」なので浮動小数点形式で表示される筈
です、
しかしながら、アルゴリズム中ほどの
「if(abs(data)>0.5){・・・}」と相関値が「0.5」を超え無
いとデータとしては排除して居るので私は、図形を見た経験
から「0.5」程度で丁度いいと判断したが、動作させて無い
ので「0.1」程度に下げる必要が有るかもしれません?!
試行錯誤とし、ソースコードでの提供ですから、数値を変更
して色々試して下さい!
そして、今の画像ファイル「欅」・「櫻」とも、恐らく元々
白黒ハッキリし過ぎた画像と思います!此れでは、ハッキリ
し過ぎてパターン画像のマッチングには、少し画像をボカシ
た方が、一致率が高い気がするので、次の
「欅坂46から櫻坂46に改名した事を画像処理的に考察例
文ソースコード説明その2」では、画像をボカシてからマッ
チングを掛ける方法を説明します!
今回は、アルゴリズムの説明の為に出来るだけシンプルに
しました!
画像をボカシての方法の後は、高速化≪今回は、パターン
画像(欅)をサーチ対象画像(櫻)の一致率が高い所を探す
手法が二次元XY座標方向に虱潰し的にベターと処理時間の
掛かるが解り易いとして採用しましたが、実用では高速化と
して色々考えラレルので紹介する心算です!≫