C++ による 画像処理プログラミング - 第 4 回 - 情報科学研究科視覚情報メディア講座 佐藤智和 tomoka-s@is.naist.jp version 1.0
今回説明すること 前回の課題の解答 バグを防ぐためのC++ の記述方法 const メモリリークのチェック (new, delete, malloc, free)
課題 1 の解答例 unsigned char getrgbintensity::crgbimage( int x, int y, int color_offset ) { return image_data[(x+y*width)*3+color_offset]; void setrgbintensity::crgbimage( int x, inty, intcolor_offset, intv ) { image_data[(x+y*width)*3+color_offset]=v; void copypixel::crgbimage_operator( Cimage *input, Cimage *output, int from_x, int from_y, int to_x, int to_y ) { output->setintensity(to_x, to_y, 0, input->getintensity( from_x, from_y, 0 ) ); output->setintensity(to_x, to_y, 1, input->getintensity( from_x, from_y, 1 ) ); output->setintensity(to_x, to_y, 2, input->getintensity( from_x, from_y, 2 ) );
課題 2 の解答例 int main() { // クラスのポインタを定義 CRGBimage *input_image, *mosaic_image, *mosaic_resized, *mirror_image, *resize_image, *trimm_image; // 画像処理用クラスを定義 CRGBimage_operator image_operator; // 入力画像用のクラスの実体を作成 input_image=new CRGBimage(); // 画像を読み込み input_image->loadimage( "./test.ppm" ); // 画像出力用のクラスの実体を作成 resize_image=new CRGBimage(); mosaic_image=new CRGBimage(); mosaic_resized=new CRGBimage(); mirror_image=new CRGBimage(); trimm_image=new CRGBimage(); // 各種画像処理の実行 image_operator.resize( input_image, resize_image, 0.1 ); image_operator.mosaic( input_image, mosaic_image, 10 ); image_operator.resize( mosaic_image, mosaic_resized, 0.5 ); image_operator.horizonalmirror( resize_image, mirror_image ); image_operator.trimming( input_image, trimm_image, 280, 350, 410, 450 ); // 画像の保存 resize_image->saveimage( "./resize.ppm" ); mosaic_resized->saveimage( "./mosaic.ppm" ); mirror_image->saveimage( "./mirror.ppm" ); trimm_image->saveimage( "./trimming.ppm" ); // 作成したクラスの実体を破棄 delete input_image; delete resize_image; delete mosaic_image; delete mosaic_resized; delete mirror_image; delete trimm_image; return 0;
const について const とは 関数の引数の前につける修飾子で その引数が関数内で変更されない ( できない ) ことを保障する // コンソールにテキストを出力する関数 void print_text( const char *string ) { // コンソールにテキストを出力 cout << string; // 配列を改変しようとする悪いコード string[0]=0; ここでコンパイルエラーとなる // メイン関数 int main() { char *test= It is test. ; print_text( test ); print_text( test );
他関数への const 変数の引渡し 特定の関数で const を引数の修飾子として記述した場合には その関数内で const でない型に変換することはできず const 引数をとらない関数にデータをそのまま渡すことはできない // 別の関数 void test_func( char *test ) { test[0]=0; //test の中身を改変 // コンソールにテキストを出力する関数 void print_text( const char *string ) { // コンソールにテキストを出力 cout << string; test_func( string ); ここでコンパイルエラーとなるため char *temp; temp=strdup( string ); test_func( temp ); free( temp ); 等とするか test_func の引数に const を付ける必要がある
クラスにおける const クラスのメンバ関数がメンバ変数を変更しないことを保障するために const を付ける // 画像オペレータクラス ( グレースケール用 ) class Cimage_operator { virtual void copypixel( const Cimage *input, Cimage *output, int from_x, int from_y, int to_x, int to_y ){ output->setintensity(to_x, to_y, input->getintensity( from_x, from_y ) ); public: void resize( const Cimage *input, Cimage *output, int new_width, int new_height ); void resize( Cimage *input, Cimage *output, float scale ); void horizonalmirror( Cimage *input, Cimage *output ) void trimming( Cimage *input, Cimage *output, int left, int top, int right, int bottom ); void mosaic( Cimage *input, Cimage *output, int scale ); ; ただし 関数内でクラスのポインタから呼ばれるメンバ関数 getintensity が メンバ変数を書き換えないことを保障するために getintensity の定義も以下のように変更する必要がある unsigned char Cimage::getIntensity( int x, int y ) const { return image_data[x+y*width]; メンバ変数を書き換えないことを保障する
メモリリークを防ぐテクニック - メモリリークとは? - c++ では new, delete を多様するが delete を忘れるとメモリが開放されず 最終的にはメモリが確保できない状態になり異常終了する 例 // コンソールにテキストを出力する関数 void image_test() { Cimage *input, *output; Cimage_operator op; input = new Cimage(); input > load(./test.ppm ); output = new Cimage(); op.resize( input, output, 0.5 ); delete input; // メイン関数 int main() { for (i=0 ; i<1000 ; i++ ) image_test(); delete output を忘れているため test.ppm の画像サイズ分のメモリ領域が開放されない
メモリリークを防ぐテクニック - クラスの解放忘れのチェック - コンストラクタ デストラクタにグローバルなカウンタをつけるだけで プログラム中で何回メモリ開放忘れが発生したか確認できる int number_of_cimage_instance=0; // カウンタをグローバル変数として作成 class Cimage { public: Cimage() : width(0), height(0), image_data(null), max_intensity(255) { number_of_cimage_instance++; ~Cimage( ){ number_of_cimage_instance--; static void printundeletedinstances() { cout<< number_of_cimage_instance << instances are still remained. << endl; クラスのインスタンス無しに呼ばれるメンバ関数には static をつけておく int main() { // 各処理の最後に以下の構文でチェック関数を呼ぶ Cimage::printUndeletedInstances(); return 0;
メモリリークを防ぐテクニック - malloc 解放忘れのチェック - malloc, free をカプセル化するクラス Cmemory を自作する Cmemory クラスの基本構造の例 class Cmemory { int counter; public: Cmemory() : counter(0) { ~Cimage(){ void *_malloc( int size, char *source_file, int line ) { counter++; return malloc( size ); void _free( void *buffer ) { counter--; free( buffer ); 確保したアドレスの情報や 呼び出し元のファイル名 ( FILE ), ライン ( LINE ) 等を引数にし リスト化して保存しておけば どこで確保したものが開放されていないかまで特定できる また 既に開放ずみのものを再度開放していないかどうか確認することも拡張次第で可能となる
前回の課題 提出者リスト 551203 651001 651002 651004 651005 651010 651011 651013 651014 651023 651027 651028 651029 651031 651034 651040 651043 651044 651045 651048 651050 651051 651052 651055 651057 651063 651065 651068 651074 651077 651080 651084 651085 651087 651089 651090 651100 651105 651108 651123 651125 651128 651135 651138 651145 以上 45 名 提出したのに自分の学籍番号がない人は連絡してください