1
画像情報処理論
大学院情報システム科学専攻 張 暁華 2画像処理プログラミングの基礎
① 画像クラス、PNM画像フォーマット ② レポートについて ③ 演習:入出力、2値化、多値化、 Hue疑似カラー、ヒストグラム作成 3C++クラスの基礎
class クラス名{ /* 設計図の様なものでクラス=新しい型*/ public: /* パブリックの場合は、クラスの外から参照可能*/ メンバー変数 /* クラスが持っている変数、構造体、クラス内クラス*/ クラス名(){ /* コンストラクター:newされたときに呼ばれる. */ } クラス名(引数){ /* コンストラクターは複数あってよい*/ } ~クラス名(){ /* デコンストラクター:delete されたときに呼ばれる. */ } 戻り値 メソッド名(引数){ /* メソッドを作れる*/ } private: /* プライベートの場合は、クラスの外から参照不可*/ }; 4多重ポインターから多次元配列を作る方法
1重ポインターから1次元配列を作る方法: double *A = new double[N];これで、A[0], A[1], …A[N-1]まで配列として使える. - 使い終わったらメモリの開放が必要:delete [] A; 2重ポインターから2次元配列を作る方法:
double **AAA = new double *[N]; for(int i=0;i<N;i++)
AAA[i] = new double[M];
これで、A[0][0], A[0][1], …A[0][M-1], A[1][0], A[1][1],…A[N-1][M-1]まで配列として使える. - 使い終わったらメモリの開放が必要 for(int i=0;i<N;i++) delete [] AAA[i]; delete [] AAA;
Imageクラス
ppmpgmClass.h: 2次元配列で一色の画像を表すImageクラス、 画像入出力に関する関数もこのヘッダに入っている。このヘッダ ファイルをインクルード必要 画像変数(オブジェクト)を宣言・メモリ確保 例えば、Image *in = new Image(); //オブジェクト生成だけ Image *out = new Image(256, 512); //256 X 512メモリ確保 画像サイズ:縦:sy、横sx: それぞれはクラスのメンバ変数 (座標(i,j)での)画素値:img[j][i]
for(int j=0; j<in->sy; j++) for(int i=0; i<in->sx; i++)
in->img[j][i] = 0; メモリ解放 delete in; delete out;
pnm(通称)画像フォーマット
一番簡単な画像フォーマットである グレースケール画像は「.pgm」、 カラー画像は「.ppm」でテキスト 形式とバイナリー形式がある グレースケール(.pgm): 1行名: テキストで「P2」、バイナリで「P5」 2行目: 画像サイズ(横 縦) 3行目:画素の階調(最大値) 8bitの場合は 255 4行目から: 画素値スペース画素値… カラー(.ppm): 1行名: テキストで「P3」、バイナリで「P6」 2行目: 画像サイズ(横 縦) 3行目:画素の階調(最大値) 8bitの場合は255 バイナリ形式では、デスク 上占めるスペースが少ない ので、なるべくバイナリ! ppmpgmClass.hがどちらも 対応している!7
pnm(通称)画像フォーマット
8
getPGM(), savePGM()
白黒画像の入出力
画像入力:void getPGM(Image *in, char *filename) 画像出力:void savePGM(Image *out, char *filename) 例えば:getPGM(in, argv[1]);
argv[1]で渡されたファイル名のpgm画像を開いてImageクラス オブジェクトinに入れる
注意:inは下記の様に画像サイズなしでnewされていないといけ ない!
Image *in = new Image();
9
getPGM(), savePGM()
白黒画像の入出力
画像入力:void getPGM(Image *in, char *filename) 画像出力:void savePGM(Image *out, char *filename) 例えば:savePGM(out, argv[2]); argv[2]で渡されたファイル名にoutの中身をpgm画像として 保存. 注意:outは下記の様に画像サイズありでnewされていないとい けない!また、outのマジックナンバーが設定され、画素データ が設定されたとする
Image *out = new Image(in->sx, in->sy);
10
getPPM(), savePPM()
同様に、カラー画像は、#include”ppmpgmClass.h”の後で、 getPPM()とsavePPM()を用いてppm画像の入出力ができる
- void getPPM(Image *R, Image *G, Image *B, char *filename) - void savePPM(Image *R, Image *G, Image *B, char *filename) getPPM()を使う場合に変数R, G, Bは、以下の様にnewされて
いる必要がある Image *R = new Image(); Image *G = new Image(); Image *B = new Image();
delete R; delete G; delete B;でメモリ開放
11
getPPM(), savePPM()
同様に、カラー画像は、#include”ppmpgmClass.h”の後で、 getPPM()とsavePPM()を用いてppm画像の入出力ができる
- void getPPM(Image *R, Image *G, Image *B, char *filename) - void savePPM(Image *R, Image *G, Image *B, char *filename) savePPM()を使う場合に変数R, G, Bは、同様に画像クラスの
オブジェクト変数を宣言し、画像サイズ付きにnewされて いる必要がある
delete R; delete G; delete B;でメモリ開放
12
getPPM(), savePPM()
便利な使い方 カラー画像は、一般にRGBで3チャンネルがることを考え、繰り返し 文を利用できるように、異なる変数名ではなく、同一名の配列を 使ったほうが都合がよいときがあるImage *in[3], *out[3]; getPPM(in[0], in[1], in[2], argv[1]); for(int k=0; k<3; k++){
out[k] = new Image(in[0]->sx, in[0]->sy); out[k]->setmagic(“P6”); // PPM(RAW)であれば }
………
savePPM(out[0], out[1], out[2], argv[2]); for(k=0; k<3; k++)
13
第一回課題
1. デジカメや携帯で撮ったオリジナルの画像をレポートで は使ってください 2. ppm, pgmへ変換するには、IrfanViewやGimpを使う (名前付け保存でファイル形式を選んで保存すればよい) 3. bmpやjpgへの変換は同様である (多数の画像フォーマットに対応している) 14演習5-1: カラーからグレースケールへの変換
1. カラー画像(ppm)を読み込んで各画素のR,G,B成分の平均 値を輝度値とするグレースケール画像(pgm)を保存するプロ グラムを作成せよ(変換方法は別にもあるが、今回は平均値 を取るとする) 2. コマンドラインで入力画像名と出力画像名を指定するように。 argvを使って、入力ファイル名、出力ファイル名を取得する 3. ヒント:ここまでのプログラムを参考にしてください。 4. #include <stdlib.h> #include <ppmpgmClass.h> を忘れずに! 15演習5-2: 閾値を用いた2値化
1. pgm画像を読み込み、閾値以下の輝度値を0、閾値以 上の輝度値を255に変更した2値化画像(pgm)を作成・ 保存するプログラムを作成せよ 2. argv, atoiを使って、入力ファイル名、出力ファイル名、 閾値を指定出来る事 16演習5-2: ヒント
lena.pgmで閾値を64、96、128、160、192で実行した結果 は以下のように様になるトーンカーブ(Tone Reproduction Curve)12
疑似カラー(重要):
演習5-3: Hue変換
1. pgm画像を読み込んでHue疑似カラー画像へ変換するプロ グラムを作成せよ 2. argvを使って、入力画像ファイル名、出力画像ファイル名を 指定出来ること 3. ヒント:入力の輝度値⇒HueのRGB変換用の関数を三つ用 意する 右のグラフと同様に色 を変換する.19
演習5-3: Hue変換
y= ax+ bの連立方程式 を解くと左の関数が導 出出来る 注意点:プログラム内 で(255/64)などは浮動 小数点(255.0/64.0)と すること forの二重ループで変 換し保存 192 255 192 128 128 192 255 128 128 0 I I I I R 192 255 192 255 255 192 64 255 64 64 255 I I I I I G
128 0 128 64 128 64 255 128 64 255 I I I I B 20演習5-3: ヒント
lena.pgmでそのままin->img[i][j]を変換したのが左、 255.0-in->img[i][j]とネガポジ反転して変換したのが右の 結果になる 21演習5-4: 統計
1. pgm画像を読み込んで輝度値の最大値、最小値、平均値、 及び中央値を計算し表示するプログラムを作成せよ 2. argvを使って、入力画像ファイル名を指定出来ること 3. ヒント:中央値は、輝度値の値を大きさでsortした場合に、 N/2番目の値. ただしNは画素数. 22演習5-4: ヒント
中央値は自分で順並びアルゴリズムを組んでできるが、画像を Image *inとすると以下の様にstandard libraryを使うと簡単 #include<algorithm> #include<vector> std::vector<double> val; forの2重ループで val.push_back(in->img[i][j]); その後に std::sort(val.begin(),val.end()); double median = val[val.size()/2]; で計算. 参考: 23重要
:ヒストグラム(Histogram)
画像の頻度表(ヒストグラム)とは量子化の階調毎に画像 中の輝度値/カラー値が何画素あるかを数えた表 24演習5-5: ヒストグラム作成
1. pgm画像を読み込んで輝度値のヒストグラムを出力する プログラムを作成せよ 2. argv, atoiを使って、入力画像ファイル名、出力ヒストグラ ムファイル名とビンの数を指定出来ること 3. ヒント1:FILE *fp = fopen(出力ファイル名,”w”); fprintf(fp,“%d %ld¥n”, ビンのID, 頻度); fclose(fp); 4. ヒント2:int N = atoi(argv[3]);long *hist = new long[N]; delete [] hist; 5. 表示はgnuplotで.
25
演習5-5: ヒント
ビンの数N、ヒストグラムの配列をlong *hist、入力画像を Image *inとすると、 for(i=0;i<N;i++)hist[i]=0;の後にforの二重ループ(iとj)で 以下のように計算double val = (in->img[i][j])/((double)(in->gray)); val *= (N-1);
int vali = ((int)(val));
if(val-((double)(vali))>=0.5) vali++; if(vali>=N) vali=N-1; hist[vali]++; 26