画像情報処理論 画像処理プログラミングの基礎 1 画像クラス PNM 画像フォーマット 2 レポートについて 3 演習 : 入出力 2 値化 多値化 Hue 疑似カラー ヒストグラム作成 大学院情報システム科学専攻張暁華 1 2 C++ クラスの基礎 多重ポインターから多次元配列を作る方法 class クラス名 { /* 設計図の様なものでクラス = 新しい型 */ public: /* パブリックの場合は クラスの外から参照可能 */ メンバー変数 /* クラスが持っている変数 構造体 クラス内クラス */ クラス名 (){ /* コンストラクター :new されたときに呼ばれる. */ クラス名 ( 引数 ){ /* コンストラクターは複数あってよい */ ~ クラス名 (){ /* デコンストラクター :delete されたときに呼ばれる. */ 戻り値メソッド名 ( 引数 ){ /* メソッドを作れる */ private: /* プライベートの場合は クラスの外から参照不可 */ ; 3 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; 4 Image クラス pnm( 通称 ) 画像フォーマット 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++) delete in; for(int i=0; i<in->sx; i++) delete out; in->img[j][i] = 0; 5 一番簡単な画像フォーマットである グレースケール画像は.pgm カラー画像は.ppm でテキスト形式とバイナリー形式がある グレースケール (.pgm): 1 行名 : テキストで P2 バイナリで P5 2 行目 : 画像サイズ ( 横縦 ) 3 行目 : 画素の階調 ( 最大値 ) 8bit の場合は 255 4 行目から : 画素値スペース画素値 カラー (.ppm): 1 行名 : テキストで P3 バイナリで P6 2 行目 : 画像サイズ ( 横縦 ) 3 行目 : 画素の階調 ( 最大値 ) 8bit の場合は 255 4 行目から : R G B R G B R G B バイナリ形式では デスク上占めるスペースが少ないので なるべくバイナリ! ppmpgmclass.hがどちらも対応している! 6 1
pnm( 通称 ) 画像フォーマット 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(); 7 8 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); 同様に カラー画像は #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; でメモリ開放 9 10 同様に カラー画像は #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; でメモリ開放 便利な使い方 カラー画像は 一般に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++) delete out[k]; 11 12 2
第一回課題 1. デジカメや携帯で撮ったオリジナルの画像をレポートでは使ってください 2. ppm, pgm へ変換するには IrfanView や Gimp を使う ( 名前付け保存でファイル形式を選んで保存すればよい ) 3. bmp や jpg への変換は同様である ( 多数の画像フォーマットに対応している ) 演習 5-1: カラーからグレースケールへの変換 1. カラー画像 (ppm) を読み込んで各画素の R,G,B 成分の平均値を輝度値とするグレースケール画像 (pgm) を保存するプログラムを作成せよ ( 変換方法は別にもあるが 今回は平均値を取るとする ) 2. コマンドラインで入力画像名と出力画像名を指定するように argv を使って 入力ファイル名 出力ファイル名を取得する 3. ヒント : ここまでのプログラムを参考にしてください 4. #include <stdlib.h> #include <ppmpgmclass.h> を忘れずに! 13 14 演習 5-2: 閾値を用いた 2 値化 1. pgm 画像を読み込み 閾値以下の輝度値を 0 閾値以上の輝度値を 255 に変更した 2 値化画像 (pgm) を作成 保存するプログラムを作成せよ 演習 5-2: ヒント lena.pgm で閾値を 64 96 128 160 192 で実行した結果は以下のように様になる 2. argv, atoi を使って 入力ファイル名 出力ファイル名 閾値を指定出来る事 15 16 トーンカーブ (Tone Reproduction Curve)12 疑似カラー ( 重要 ): 演習 5-3: Hue 変換 1. pgm 画像を読み込んで Hue 疑似カラー画像へ変換するプログラムを作成せよ 2. argv を使って 入力画像ファイル名 出力画像ファイル名を指定出来ること 3. ヒント : 入力の輝度値 Hue の RGB 変換用の関数を三つ用意する 右のグラフと同様に色を変換する. 17 18 3
0 R I 128 192 128 255 I 64 G 255 I 192 演習 5-3: Hue 変換 I 128 128 I 192 I 192 I 64 64 I 192 I 192 y= ax+ b の連立方程式を解くと左の関数が導出出来る 注意点 : プログラム内で (255/64) などは浮動小数点 (255.0/64.0) とすること 演習 5-3: ヒント lena.pgm でそのまま in->img[i][j] を変換したのが左 255.0-in->img[i][j] とネガポジ反転して変換したのが右の結果になる 255 B I 128 64 128 0 I 64 64 I 128 I 128 for の二重ループで変換し保存 19 20 演習 5-4: 統計 1. pgm 画像を読み込んで輝度値の最大値 最小値 平均値 及び中央値を計算し表示するプログラムを作成せよ 演習 5-4: ヒント 中央値は自分で順並びアルゴリズムを組んでできるが 画像を Image *in とすると以下の様に standard library を使うと簡単 2. argv を使って 入力画像ファイル名を指定出来ること 3. ヒント : 中央値は 輝度値の値を大きさで sort した場合に N/2 番目の値. ただし N は画素数. #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]; で計算. 参考 : 21 22 重要 : ヒストグラム (Histogram) 画像の頻度表 ( ヒストグラム ) とは量子化の階調毎に画像中の輝度値 / カラー値が何画素あるかを数えた表 演習 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 で. 23 24 4
演習 5-5: ヒント 演習 5-5: ヒント ビンの数 N ヒストグラムの配列を long *hist 入力画像を Image *in とすると for(i=0;i<n;i++)hist[i]=0; の後に for の二重ループ (i と j) で以下のように計算 以下は lena.pgm の輝度値ヒストグラムで ビンの数は 32 ( 左 ) と 256( 右 ) の場合である 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]++; 25 26 次回は? 画像の幾何変換 画素値の補間 27 5