プログラミング演習Ⅱ 課題4第2週 画像ファイル(ppm)の読み書き 画像データ用のメモリ確保・解放 1 画像データの基礎 255 200 130 0 100 ... 画素ごとの明るさを数値で表現 (8bitなら0から255) カラー画像は各画素にRed, Green, Blue (R,G,B)= (255, 0, 0): 赤,(255, 255, 255): 白 raw画像ファイルでは1次元上に数値が並んでいる B1 B2 B3 B4 G1 G2 B7 G4 G5 R1 R2 R3 R4 G7 R5 G8 R6 R7 R8 R9 B5 B6 G3 B8 B9 G6 ファイル上の画像データの並び R1 G1 B1 R2 G2 B2 ... B3 R4 G4 ... G9 2 PPM画像ファイル PPMファイルの中身 解説 P6 # Created by IrfanView 1024 768 255 [ここから画像データ] ..... マジックナンバー(バイナリPPM,固定) #から改行まではコメント文 横方向の画素数[空白]縦方向の画素数 最大輝度 [ここから画像データ] ..... 画像に関する情報をヘッダとして持っている 今回使用する画像データはR,G,B各8ビット R1 G1 B1 R2 G2 B2 ... B3 R4 G4 ... 8bit 24bit 3 データ構造~画像データをどう表現するか~ 画像ファイルのフォーマット ヘッダ(画像サイズ,最大輝度) int xsize; int ysize; int level; 88 B1 E7 (136) (177) (231) 画像データ unsigned char buffer_r[ysize][xsize], buffer_g[ysize][xsize], buffer_b[ysize][xsize]; プログラムでデータを読み込む場合・・・ ・変数が6つ必要 → 構造体でまとめる ・配列サイズはヘッダを読むまで決められない → メモリの動的確保・解放 4 データ構造~構造体定義~ 画像ファイルのフォーマット ヘッダ(画像サイズ,最大輝度) typedef struct { int xsize; int ysize; int level; PIXEL **pBuffer; } IMAGE; typedef struct { unsigned char r; unsigned char g; unsigned char b; } PIXEL; 88 B1 E7 (136) (177) (231) 画像データ ヘッダ 画像データへの二重ポインタ 画像データ (1画素分) 5 データ構造~構造体定義~ 画像ファイルのフォーマット ヘッダ(画像サイズ,最大輝度) 88 B1 E7 (136) (177) (231) 画像データ typedef struct { int xsize; int ysize; int level; PPIXEL *pBuffer; } IMAGE; typedef struct { unsigned char r; unsigned char g; unsigned char b; } PIXEL, *PPIXEL; (PIXEL*)型として PPIXELを新たに定義 6 ポインタの配列~画像バッファの確保~ void iioMallocImageBuffer(IMAGE *pImage) pImage xsize 256 ysize 192 level 255 pBuffer xsize個のPIXEL の配列を確保 malloc(xsize * sizeof(PIXEL)); ysize個のPPIXEL の配列を確保 malloc(ysize * sizeof(PPIXEL)); 7 画像バッファの様子 → i pImage xsize ysize level pBuffer 256 192 255 ↓ j pImage->pBuffer[0][0] → i ↓ j ysize個 pImage->pBuffer[j][i] pImage->pBuffer[j] xsize個 8 画像へのアクセス方法 IMAGE *pImage; /* pImageに画像データを読み込んだ後と仮定する */ pImage->xsize; /* int型,画像幅 */ pImage->ysize; /* int型,画像高さ */ pImage->pBuffer[j][i]; /* PIXEL型,位置(i,j)における{R,G,B}*/ pImage->pBuffer[j][i].r; /* unsigned char型 */ /* 位置(i,j)におけるRの値 */ pImage->pBuffer[j]; /* PPIXEL型,(j)行の画素値が連続で格納されて /* いる場所の先頭アドレス */ 9 モジュール設計について 10 モジュール設計 利点: 可読性,分割コンパイル,情報の隠ぺい モジュールごとの独立性を高めるように設計 メイン main.c 画像のファイル入出力 画像データ用メモリ確保と解放 img_io.c img_io.h 画像処理 img_proc.c img_proc.h 11 関数プロトタイプ宣言 あるファイル内で定義した関数(func)を,定義してい る位置より前で呼び出したい場合 最後はセミコロン double func(double d); int caller(void) { f = func(4); } double func(double d) { return d*2; } 関数呼出部分より前に プロトタイプ宣言 を行う プロトタイブ宣言がないと, func 関数は未定義エラー func 関数の定義部 12 ヘッダファイルの利用 あるファイル内で定義した関数(func)を,違うファイル で呼び出したい場合 関数のプロトタイプ宣言をヘッダファイルに記述 呼び出し側のファイルで #include module.h ヘッダファイル //プロトタイプ宣言 double func(double d); module.c #include “module.h” //呼び出される関数 double func(double d) { return d*2; } caller.c #include “module.h” ” ” で囲んでいる ことに注意! //呼び出し側関数 int caller(void) { f = func(4); // 関数呼び出し } 13 < >と " " #include <stdio.h>のように< >で囲まれてい ると,プリプロセッサはシステムで定められた場所に あるファイルを取り込む #include “module.h”のように” ”で囲まれてい ると,まずプログラムファイルと同じ場所を探し,存在 しない場合はシステムで定められた場所を探す 14 モジュール化の基本 module.h ヘッダファイルには 外部に公開する情報のみを記述 #define MAX 1024 typedef unsigned char BYTE; void exported_func(int i); module.c #include <string.h> #include “module.h” int internal_func(int a); #define INT_MACRO(a) (…) void exported_func(int i) { //外部に公開する関数 } static int internal_func(int a) { //モジュール内で //使用する関数 } • 関数のプロトタイプ宣言 • typedef 型宣言 • #define マクロ定義 など プロトタイプ宣言が関数本体と一致 するように注意 モジュール内のみで使用する関数の プロトタイプ宣言やマクロ定義などは モジュール内で行う モジュールで,何を公開し, 何を隠すかがポイント 15 ファイルを分割したときの関数のスコープ module.h #define MAX 1024 typedef unsigned char BYTE; void exported_func(int i); module.c #include <string.h> #include “module.h” 別のモジュール foo.c #include “module.h” exported_func(15); internal_func(30); int internal_func(int a); #define INT_MACRO(a) (…) ○ × • ヘッダファイルにプロトタイプ宣言がない 関数は外部から参照できない • staticな関数は外部から参照できない void exported_func(int i) { //外部に公開する関数 } static int internal_func(int a) { //モジュール内で //使用する関数 } 16 サンプルプログラム2について 17 全体の流れ 画像ファイルを開き,画像データをメモリ上にロード ロードした画像データに処理を加える 処理後のデータを出力ファイルに書き出す 画像データ用に確保したメモリを解放 18 サンプルプログラム2の関数呼び出し関係 main.c 画像ファイルをロードする関数を呼び出す 画像処理関数を呼び出す 画像ファイルをセーブする関数を呼び出す 画像バッファを解放 img_io.c 画像ファイルのロード ファイルオープン 画像バッファ確保 データコピー ファイルクローズ img_proc.c 画像を90度回転 作業用バッファを確保 データを移し変え 不要なバッファを解放 画像ファイルのセーブ 画像バッファ確保 画像バッファ解放 (あとで処理を追加したい) etc 19 サンプルプログラム2の実装状況 main.c 実装済 画像ファイルをロードする関数を呼び出す 画像処理関数を呼び出す 画像ファイルをセーブする関数を呼び出す 画像バッファを解放 img_io.c 画像ファイルのロード 実装済 演習課題 実装済 演習課題 ファイルオープン 画像バッファ確保 データコピー ファイルクローズ img_proc.c 画像を90度回転 作業用バッファを確保 データを移し変え 不要なバッファを解放 実装済 画像ファイルのセーブ 画像バッファ確保 画像バッファ解放 演習課題 (第3週以降) (あとで処理を追加したい) etc 20 関数の設計 ファイル入出力 (済) int iioLoadFile(IMAGE *pImage, const char *fname); (未) int iioSaveFile(IMAGE *pImage, const char *fname); メモリ確保/解放 (済) void iioMallocImageBuffer(IMAGE *pImage); (未) void iioFreeImageBuffer(IMAGE *pImage); 画像処理関連 (済) void ipRotateImage(IMAGE *pImage); 21 第2週目の目標 画像ファイルを読み込み 90°回転させた後, 画像ファイルを出力するプログラムを作成する 読み込み関数や回転処理関数は既に実装済み 画像ファイルをセーブする関数”iioSaveFile”と 画像バッファを解放する関数” iioFreeImageBuffer” を完成させる 22 画像ファイルのセーブのヒント int iioSaveFile(IMAGE *pImage, const char *fname) 開始 出力用ファイルを バイナリモードでオープン ヘッダ情報を出力 pImage xsize 256 ysize 192 level 255 pBuffer ヒント1: fprintf ヒント2: xsizeの後ろに空白1つ ysizeの後ろに改行 levelの後ろに改行 を忘れない 一行ずつ出力 ヒント: fwrite pBuffer[0] 画像データを出力 pBuffer[1] pBuffer[2] ファイルクローズ 終了 23 画像バッファの解放のヒント void iioFreeImageBuffer(IMAGE *pImage) 開始 pImage j =0; j < ysize? Y pBuffer[ j ]を解放 ① j++; N xsize 256 ysize 192 level 255 pBuffer pBuffer[0] ① 一行ずつ解放 pBuffer[1] pBuffer[2] pBufferを解放 ② 終了 ②最後に各行へのポイ ンタの配列を解放 24
© Copyright 2024 ExpyDoc