応用プログラミング 第8回
~プログラミングの応用
画像処理入門1
~
電気通信大学電子工学専攻
Intelligent Electronic Systems Group
長井 隆行
本日の内容
1.
画像処理入門
~画像を知る~
1.
CCDカメラの仕組み
2.
グレースケール画像
3.
カラー画像
4.
画像ファイルのフォーマット
5.
画像の入出力
2.
課題3
FILE構造体とは?(前回の補足)
FILE構造体
操作するファイルの情報を格納する構造体
ファイルの情報とは現在読み書きしているファイルのアドレス(位置)など
直接この構造体にアクセスすることはあまりない
普通はfopenやfprintfなどで指定するだけ
以下は参考までにFILE構造体の例(コンパイラによって異なる)
typedef struct {unsigned char *curp; /* Current active pointer */ unsigned char *buffer; /* Data transfer buffer */ int level; /* fill/empty level of buffer */ int bsize; /* Buffer 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
0~255
の範囲
(
8bppなどと呼ぶ)
いよいよ画像 続き
カラー画像
画素(ピクセル)
一般的には24bit
RGBがそれぞれ
0~255
の範囲
(
24bppなどと呼ぶ)
メモリ上ではどうなってるの?
2次元の情報である画像をどのようにメモリ(1次元の配列)に収めるか?
0x0a
0x09
0x08
0x07
0x06
0x05
0x04
0x03
0x02
0x01
0x00
value
add
どのように並べるかは基本的にプログラマの自由
画面へ表示するためには、いくつか約束事がある
ファイルに保存する場合も決まったやり方がある(フォーマット)
255 50 10 20 0 100 255 150 200 255 50 10 20 0 100 255 150 200必ずしもこの順番である必要はない
カラー画像の場合は?
一般に
24bppとは各画素が0~
16777216の値をもっているのではなく
R(赤)、G(緑)、B(青)がそれぞれ0~
255(8bit)の値をもっている
⇒
8×3 = 24(bit)
255
B
0
G
0
R
200
B
100
G
20
R
0
B
255
G
0
R
0
B
0
G
255
R
画像ファイルのフォーマット
画像が成立するための必要な情報
画像データ本体 ⇒ 当然必要
計64枚のタイルを渡されて「これ並べて」と命令されても
ど
のように並べればいいか
分からない
画像のサイズ
どんな順番で並んでいるか
カラーかグレースケールか(1ピクセルのビット数)
などなど・・・
ヘッダ情報
画像データ
画像ファイルのフォーマット 続き
画像の表現の仕方は色々あり得る
みんなが使えるように画像の表現方法の約束を決める
「ヘッダ情報や画像データをどのように記録しておくか」に関
する取り決め
ビットマップ
(.bmp)
windowsで標準
Jpeg (.jpg)
圧縮画像の標準形式
TIFF (.tif)
DTPなどでよく使用される
GIF (.gif)
インターネットでよく使用される(256色)
ポータブルグレーマップ
(.pgm)
Xwindow(Unix)でよく
使われる
.pgm(ポータブルグレーマップ)
グレースケール画像に対する最もシンプルな画像ファイル
フォーマット
PGM P5 フォーマット (グレースケール、バイナリ)
P5 320 240 255 rrtuvwxxz|{}~~ =cту㊧・・演糾給血麹克諮瑞荘葬涛湯粕 燈末楓迄劍劍履囹囮囹尹屆屁惧撼撼棍沺棍。「。。。「。。。。「「」 「「「「「」」」、、・・ヲ・、・ヲヲ・ヲヲ・ヲヲァヲヲヲァァァァ ヲィァァィァァィィァゥィゥィゥゥィィィゥェィィィゥェィィゥィゥゥィィゥゥェェェゥゥゥォォォォォォォゥゥゥェゥゥェゥゥェゥ ェィェゥィゥゥィゥゥィィゥィァ・ヲィヲァァィィァィァィヲヲァヲヲヲァヲ・ヲヲ、ヲ、・、・、、」「」、、、「」」「。・ pgm (P5)をテキストエディタで強引に開いたもの ・・・ずっと続くヘッダ
バイナリ画像データ 1バイト/1画素 ファイル識別子 画像の横幅 縦幅 最大階調値.bmp(ビットマップ)
Windowsでは標準のビットマップ
PGMより複雑(基本的な考え方は同じ)
ヘッダ情報(バイナリ) 画像データ(バイナリ) BGRBGRBGR ・・・ BGR BITMAPFILEHEADER (ファイルヘッダ) BITMAPINFOHEADER (情報ヘッダ) 2byte ファイルのタイプ "BM" 4byte ファイルのサイズ(byte) 2byte 予約1 0 2byte 予約2 0 4byte BITMAPFILEHEADERから実際の ビットマップデータまで のオフセット値 ヘッダーのサイズと考えてよい(byte) 4byte 情報ヘッダのサイズ 40 4byte 画像の幅(画素数) 4byte 画像の高さ(画素数) 4byte 圧縮形式 0:無圧縮 2byte プレーンの数 1 2byte 1画素のビット数(bpp) 4byte 画像データのサイズ(byte) 4byte 横方向解像度 4byte 縦方向解像度 4byte パレット数 4byte 重要なパレットのインデックス14byte 40byte byte数は画像サイズによる
画像処理するために
本講義での流れ
撮影 (デジカメ) 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上で行うか、out_bufferにデータをコピーしてout_buffer上で行う もしくは、新たな画像用配列を宣言して使用してもよい 但し、書き込みの際は必ずout_bufferに結果の画像データが存在する必要がある buffer buffer[3*width(height-1)] buffer[3*width*height-1] buffer[0] 3*Width buffer[3*width-1] height out_buffer width height ファイル名”result.bmp”で保存される B G R 1画素分のデータ(BGR) BG R buffer[0] buffer[1] buffer[2] ・ ・ ・ buffer[3*width-1] buffer[3*width] buffer[3*width(height-1)] buffer[3*width*height-1] ・ ・ ・ ・ ・ ・ビットマップの読み込み部 (p8-1.c)
/*メイン関数*/ int main(void) {int size, width, height; FILE *fp = NULL;
unsigned char *buffer; /*入力画像用メモリのポインタ*/ unsigned char *out_buffer; /*出力画像用メモリのポインタ*/
BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; /*ファイルを開く*/ fp = fopen( "bitmap.bmp" , "rb" ); /*ビットマップのヘッダーを読み込む*/ /*構造体のメンバはメモリ上に定義順に確保されていることに注目*/
fread( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp ); fread( &bmih , sizeof(BITMAPINFOHEADER) , 1 , fp );
/*使いやすいように画像の幅と高さをコピーする*/ width = bmih.biWidth;
height = bmih.biHeight;
size = width * height; /*ビットマップのサイズを算出*/ buffer = (unsigned char*)malloc( 3*size ); /*必要なサイズのメモリを確保*/
fread( buffer , 3*size , 1 , fp ); /*画像データ本体の読み込み*/ fclose( fp ); /*読み終わったのでファイルを閉じる*/
ビットマップのための構造体
p8-1.c
/*ビットマップファイルヘッダのための構造体定義*/ typedef struct tagBITMAPFILEHEADER {
unsigned short bfType; unsigned long bfSize; unsigned short bfReserved1; unsigned short bfReserved2; unsigned long bfOffBits; } BITMAPFILEHEADER;
/*ビットマップインフォヘッダのための構造体定義*/ typedef struct tagBITMAPINFOHEADER{
unsigned long biSize; long biWidth; long biHeight; unsigned short biPlanes; unsigned short biBitCount; unsigned long biCompression; unsigned long biSizeImage; long biXPixPerMeter; long biYPixPerMeter; unsigned long biClrUsed; unsigned long biClrImporant; } BITMAPINFOHEADER; 画像情報のための構造体を定義 •windows.hをインクルードできれば、 その中に定義されているので自分で する必要はない
関数
freadによる読み込み
fread関数を使って、ファイルから
バイナリデータ
を直接読み込む
fread( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp );
fread( &bmih , sizeof(BITMAPINFOHEADER) , 1 , fp );
ポイントは、構造体を直接読み込んでいること
メンバごとに読み込む必要がない⇒
メンバがメモリ上に順番に並んでいるため
fread( buffer , 3*size , 1 , fp );
画像データも直接読み込んでいる
fread(コピー先のポインタ, 何バイト読むか, 何個読むか, ファイルポインタ);
14バイト読んだ 次に40バイト読む 今ファイルのどこまで読んだかが ファイルポインタfpに記録されているビットマップの読み込み
メモリ HD fread( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp );fp
ファイルの先頭 ファイルの最後
fread( &bmih , sizeof(BITMAPINFOHEADER) , 1 , fp );
fp
fread( buffer , 3*size , 1 , fp );
fp bmfhの先頭 bmihの先頭 buffer[0]の先頭 fp fp fp
構造体の直接読み込み
メモリ HD fread( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp );fp ファイルの最後 bmfhの先頭 2byte ファイルのタイプ "BM" 4byte ファイルのサイズ(byte) 2byte 予約1 0 2byte 予約2 0 4byte BITMAPFILEHEADERから実際の ビットマップデータまで のオフセット値 ヘッダーのサイズと考えてよい(byte) bfType bfSize bfReserved1 bfReserved2 bfOffBits
ビットマップの出力部 (
p8-1.c)
/*書き込み処理*/ /*結果をファイルに書くためには、out_bufferに結果をしまう必要がある*/ fp = fopen( "result.bmp" , "wb" ); /*ファイルを書き込み用で開く*/ /*画像の情報を作成する(画像処理によって変わる可能性があるものだけ作成)*/ /*出力画像のwidth、heightが変更されていればそれを書き込む*/bmfh.bfSize = width*height*3 + BMP_HEADER_SIZE; bmih.biWidth = width;
bmih.biHeight = height; /*ビットマップを書き込む*/
fwrite( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp ); fwrite( &bmih , sizeof(BITMAPINFOHEADER) , 1 , fp ); fwrite( out_buffer , 3*size , 1 , fp );
fclose( fp ); /*ファイルを閉じる*/ /*メモリを解放する*/ free( buffer ); free( out_buffer ); return 0; }
関数
fwriteによる書き出し
出力はまさに入力の逆
fwrite関数を使ってバイナリデータを直接ファイルに書き込む
fwrite( &bmfh , sizeof(BITMAPFILEHEADER) , 1 , fp );
fwrite( &bmih , sizeof(BITMAPINFOHEADER) , 1 , fp );
fwrite( out_buffer , 3*size , 1 , fp );
14バイト分書き込んだ後に 40バイト分書き込み その後に 画像サイズ分書き込む