応用プログラミング第 8 回 ~ プログラミングの応用画像処理入門 1~ 本日の内容 1. 画像処理入門 ~ 画像を知る ~ 1. CCD カメラの仕組み 2. グレースケール画像 3. カラー画像 4. 画像ファイルのフォーマット 5. 画像の入出力 2. 課題 3 電気通信大学電子工学専攻 Intelligent Electronic Systems roup 長井隆行 FILE 構造体とは?( 前回の補足 ) FILE 構造体 操作するファイルの情報を格納する構造体 ファイルの情報とは現在読み書きしているファイルのアドレス ( 位置 ) など 直接この構造体にアクセスすることはあまりない 普通はfopenやrintfなどで指定するだけ 以下は参考までにFILE 構造体の例 ( コンパイラによって異なる ) いよいよ画像 白黒濃淡画像 ( グレースケール画像 ) typedef struct { unsigned char *curp; /* Current active pointer */ unsigned char *buffer; /* Data transfer buffer */ int level; /* fill/empty level of buffer */ int bsize; /* uffer size */ unsigned short istemp; /* Temporary file indicator */ unsigned short flags; /* File status flags */ wchar_t hold; /* Ungetc char if no buffer */ char fd; /* File descriptor */ unsigned char token; /* Used for validity checking */ } FILE; /* This is the FILE object */ 画素 ( ピクセル ) 一般的には8bit ~の範囲 (8bppなどと呼ぶ)
いよいよ画像続き カラー画像 メモリ上ではどうなってるの? 2 次元の情報である画像をどのようにメモリ (1 次元の配列 ) に収めるか? add value 5 1 x x1 5 2 1 x2 x3 1 2 15 2 x4 x5 1 画素 ( ピクセル ) 一般的には 24bit がそれぞれ ~ の範囲 (24bpp などと呼ぶ ) 必ずしもこの順番である必要はない どのように並べるかは基本的にプログラマの自由画面へ表示するためには いくつか約束事があるファイルに保存する場合も決まったやり方がある ( フォーマット ) x6 x7 x8 x9 xa 15 2 カラー画像の場合は? 画像ファイルのフォーマット 一般に 24bpp とは各画素が ~ 16777216 の値をもっているのではなく 画像が成立するための必要な情報 画像データ本体 当然必要 画像データ ( 赤 ) ( 緑 ) ( 青 ) がそれぞれ ~ (8bit) の値をもっている 8 3 = 24(bit) 計 64 枚のタイルを渡されて これ並べて と命令されてもどのように並べればいいか分からない 2 1 2 画像のサイズ どんな順番で並んでいるか カラーかグレースケールか (1ピクセルのビット数) などなど ヘッダ情報
画像ファイルのフォーマット続き 画像の表現の仕方は色々あり得る みんなが使えるように画像の表現方法の約束を決める ヘッダ情報や画像データをどのように記録しておくか に関する取り決め ビットマップ (.bmp) windowsで標準 Jpeg (.jpg) 圧縮画像の標準形式 TIFF (.tif) DTPなどでよく使用される IF (.gif) インターネットでよく使用される (256 色 ) ポータブルグレーマップ (.pgm) Xwindow(Unix) でよく 使われる.pgm( ポータブルグレーマップ ) グレースケール画像に対する最もシンプルな画像ファイルフォーマット PM P5 フォーマット ( グレースケール バイナリ ) P5 画像の横幅縦幅 32 24 最大階調値 rrtuvwxxz {}~~ =cту左 演糾給血麹克諮瑞荘葬涛湯粕燈末楓迄劍劍履囹囮囹尹屆屁惧撼撼棍沺棍 ヲ ヲヲ ヲヲ ヲヲァヲヲヲァァァァヲィァァィァァィィァゥィゥィゥゥィィィゥェィィィゥェィィゥィゥゥィィゥゥェェェゥゥゥォォォォォォォゥゥゥェゥゥェゥゥェゥェィェゥィゥゥィゥゥィィゥィァ ヲィヲァァィィァィァィヲヲァヲヲヲァヲ ヲヲ ヲ ずっと続く ファイル識別子 pgm (P5) をテキストエディタで強引に開いたもの ヘッダ バイナリ画像データ 1 バイト /1 画素 ITMAPFILEHEADE( ファイルヘッダ ).bmp( ビットマップ ) Windows では標準のビットマップ PM より複雑 ( 基本的な考え方は同じ ) ヘッダ情報 ( バイナリ ) 画像データ ( バイナリ ) 2byte ファイルのタイプ "M" 4byte ファイルのサイズ (byte) 2byte 予約 1 2byte 予約 2 14byte 4byte byte 数は画像サイズによる 4byte ITMAPFILEHEADE から実際のビットマップデータまでのオフセット値ヘッダーのサイズと考えてよい (byte) ITMAPINFOHEADE( 情報ヘッダ ) 4byte 情報ヘッダのサイズ 4 4byte 画像の幅 ( 画素数 ) 4byte 画像の高さ ( 画素数 ) 2byte プレーンの数 1 2byte 1 画素のビット数 (bpp) 4byte 圧縮形式 : 無圧縮 4byte 画像データのサイズ (byte) 4byte 横方向解像度 4byte 縦方向解像度 4byte パレット数 4byte 重要なパレットのインデックス 撮影 ( デジカメ ) 画像処理するために 本講義での流れ PC へ転送 ( ハードディスク ) 自らのプログラムで画像ファイルを読み込む ( メモリ ) 入力 画像処理 自らのプログラムでメモリ上の画像データを処理 ( メモリ ) 画像データを viewer で開き結果を確認 出力自らのプログラムでメモリ上の画像データをファイルに書き出す ( ハードディスク )
この講義における画像処理 Step1 まず画像を用意する (bmp や jpg) Step2 既存のソフト (paint,xv など ) を使って bmp 形式に変換する ( もともとビットマップであれば変換の必要ないが 以下の条件を満たしていない場合はやはり変換が必要 ) * 画像の幅が 4 の倍数である *1 ピクセルが 24 ビット (24bpp) また 画像を処理して保存する際もこれらの条件が満たされなくてはいけない Step3 p8-1.c に画像処理部分を追加する Step4 既存のソフト (paint,xv など ) を使って保存した画像を開く プログラムで画像を扱う p8-1.cの使い方 p8-1forint.cとp8-1formot.cがある CPUの違いで使い分ける必要がある 使っているCPUがインテル系である (windows Linuxの人はこちらだと思ってよい ) p8-1forint.c 使っているCPUがモトローラ系である (Mac Solaris(sun) の人はこちらだと思ってよい : 大学の情報処理センターで行う人はこっち ) p8-1formot.c プログラムで画像を扱う続き buffer[3*width(height-1)] height buffer[] 3*Width buffer[3*width*height-1] buffer[3*width-1] 1 画素分のデータ () buffer[] buffer[1] buffer[2] buffer[3*width-1] buffer[3*width] buffer[3*width(height-1)] buffer[3*width*height-1] buffer 処理はbuffer 上で行うか out_bufferにデータをコピーしてout_buffer 上で行うもしくは 新たな画像用配列を宣言して使用してもよい但し 書き込みの際は必ずout_bufferに結果の画像データが存在する必要がある ビットマップの読み込み部 (p8-1.c) /* メイン関数 */ int main(void) { int size, width, height; FILE * = NULL; unsigned char *buffer; /* 入力画像用メモリのポインタ */ unsigned char *out_buffer; /* 出力画像用メモリのポインタ */ ITMAPFILEHEADE bmfh; ITMAPINFOHEADE bmih; /* ファイルを開く */ = fopen( "bitmap.bmp", "rb" ); /* ビットマップのヘッダーを読み込む */ /* 構造体のメンバはメモリ上に定義順に確保されていることに注目 */ fread( &bmfh, sizeof(itmapfileheade), 1, ); fread( &bmih, sizeof(itmapinfoheade), 1, ); /* 使いやすいように画像の幅と高さをコピーする */ width = bmih.biwidth; height = bmih.biheight; height out_buffer width ファイル名 result.bmp で保存される size = width * height; /* ビットマップのサイズを算出 */ buffer = (unsigned char*)malloc( 3*size ); /* 必要なサイズのメモリを確保 */ fread( buffer, 3*size, 1, ); /* 画像データ本体の読み込み */ fclose( ); /* 読み終わったのでファイルを閉じる */
ビットマップのための構造体 関数 fread による読み込み /* ビットマップファイルヘッダのための構造体定義 */ typedef struct tagitmapfileheade { unsigned short bftype; unsigned long bfsize; unsigned short bfeserved1; unsigned short bfeserved2; unsigned long bfoffits; } ITMAPFILEHEADE; /* ビットマップインフォヘッダのための構造体定義 */ typedef struct tagitmapinfoheade{ unsigned long bisize; long biwidth; long biheight; unsigned short biplanes; unsigned short biitcount; unsigned long bicompression; unsigned long bisizeimage; long bixpixpermeter; long biypixpermeter; unsigned long biclrused; unsigned long biclrimporant; } ITMAPINFOHEADE; 画像情報のための構造体を定義 windows.h をインクルードできれば その中に定義されているので自分でする必要はない p8-1.c fread 関数を使って ファイルからバイナリデータを直接読み込む fread( コピー先のポインタ, 何バイト読むか, 何個読むか, ファイルポインタ ); fread( &bmfh, sizeof(itmapfileheade), 1, ); fread( &bmih, sizeof(itmapinfoheade), 1, ); ポイントは 構造体を直接読み込んでいることメンバごとに読み込む必要がない メンバがメモリ上に順番に並んでいるため fread( buffer, 3*size, 1, ); 画像データも直接読み込んでいる 14 バイト読んだ次に 4 バイト読む 今ファイルのどこまで読んだかがファイルポインタ に記録されている ビットマップの読み込み 構造体の直接読み込み fread( &bmfh, sizeof(itmapfileheade), 1, ); fread( &bmih, sizeof(itmapinfoheade), 1, ); bmfh の先頭 メモリ fread( &bmfh, sizeof(itmapfileheade), 1, ); ファイルの最後 bmfh の先頭 メモリ fread( buffer, 3*size, 1, ); bmih の先頭 HD bftype 2byte ファイルのタイプ "M" bfsize HD 4byte ファイルのサイズ (byte) bfeserved1 ファイルの先頭 ファイルの最後 buffer[] の先頭 2byte 予約 1 bfeserved2 2byte 予約 2 bfoffits 4byte ITMAPFILEHEADE から実際のビットマップデータまでのオフセット値ヘッダーのサイズと考えてよい (byte)
ビットマップの出力部 (p8-1.c) 関数 fwrite による書き出し /* 書き込み処理 */ /* 結果をファイルに書くためには out_buffer に結果をしまう必要がある */ = fopen( "result.bmp", "wb" ); /* ファイルを書き込み用で開く */ /* 画像の情報を作成する ( 画像処理によって変わる可能性があるものだけ作成 )*/ /* 出力画像の width height が変更されていればそれを書き込む */ bmfh.bfsize = width*height*3 + MP_HEADE_SIZE; bmih.biwidth = width; bmih.biheight = height; 出力はまさに入力の逆 fwrite 関数を使ってバイナリデータを直接ファイルに書き込む /* ビットマップを書き込む */ fwrite( &bmfh, sizeof(itmapfileheade), 1, ); fwrite( &bmih, sizeof(itmapinfoheade), 1, ); fwrite( out_buffer, 3*size, 1, ); fclose( ); /* ファイルを閉じる */ /* メモリを解放する */ free( buffer ); free( out_buffer ); fwrite( &bmfh, sizeof(itmapfileheade), 1, ); fwrite( &bmih, sizeof(itmapinfoheade), 1, ); fwrite( out_buffer, 3*size, 1, ); 14 バイト分書き込んだ後に 4 バイト分書き込みその後に画像サイズ分書き込む } return ; まずは以下をやってみよう これは課題ではないので提出する必要ありませんがまず試すことをお勧めします 好きなビットマップ画像を用意する (24ビットカラー) 画像の幅が4の倍数以上のものは 画像ソフトで4の倍数になるように切り取る p8-1.cをコンパイルし実行する 実行すると新たなビットマップ画像ができるはず 保存したファイルを画像ソフトで開き 入力した画像と全く同じであれば成功 p8-1.cを眺めてみる