C++による
画像処理プログラミング
-第4回-情報科学研究科
視覚情報メディア講座
佐藤 智和 tomoka-s@is.naist.jp
version 1.0
今回説明すること
|
前回の課題の解答
|
バグを防ぐためのC++の記述方法
z
const
課題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, int y, int color_offset, int v ) {
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変数の引渡し
//別の関数
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
でない型に変換することはできず、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 );
};
クラスのメンバ関数がメンバ変数を変更しないことを保障するためにconstを付ける
ただし、関数内でクラスのポインタから呼ばれるメンバ関数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を自作する
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 ); } }