初期化
■ ROI
画像に対して ROI(Region Of Interest)を設定すると,その部分画像が,画像全体と同一に扱われる. つまり,部分画像に対して処理を行う場合,それが部分画像であることを考慮する必要なく扱うことができる. ただし,処理結果として座標や領域を得て,それを再び画像全体に戻す処理 (例えば,画像全体に対して描画するなど)を行う場合は,座標にオフセット (ROIの位置,幅など)を加えるなどの追加処理が必要なので注意すること.サンプル
部分画像のシャッフル cvSetImageROI, cvResetImageROI
ROIを利用して,部分画像同士を入れ換える
サンプルコード
#include <cv.h> #include <highgui.h> #include <time.h> /* 分割数の定義 */ #define DIVX (4) #define DIVY (4) #define DIVXY (DIVX*DIVY) int main (int argc, char **argv) { int w, h, i, j; IplImage *src_img, *dst_img, *tmp_img[2]; CvRect roi[DIVXY]; CvRNG rng = cvRNG (time (NULL)); // (1)画像を読み込む if (argc != 2 || (src_img = cvLoadImage (argv[1], CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR)) == 0) return -1; w = src_img->width - src_img->width % DIVX + DIVX; h = src_img->height - src_img->height % DIVY + DIVY; dst_img = cvCreateImage (cvSize (w, h), src_img->depth, src_img->nChannels); tmp_img[0] = cvCreateImage (cvSize (w / DIVX, h / DIVY), src_img->depth, src_img->nChannels); tmp_img[1] = cvCreateImage (cvSize (w / DIVX, h / DIVY), src_img->depth, src_img->nChannels); cvResize (src_img, dst_img); // (2)画像を分割するための矩形を設定 for (i = 0; i < DIVX; i++) { for (j = 0; j < DIVY; j++) { roi[DIVX * j + i].x = w / DIVX * i; roi[DIVX * j + i].y = h / DIVY * j; roi[DIVX * j + i].width = w / DIVX; roi[DIVX * j + i].height = h / DIVY; } } // (3)ROIを利用して部分画像を入れ換える for (i = 0; i < DIVXY * 2; i++) { int p1 = cvRandInt (&rng) % DIVXY; int p2 = cvRandInt (&rng) % DIVXY; cvSetImageROI (dst_img, roi[p1]); cvCopy (dst_img, tmp_img[0]); cvSetImageROI (dst_img, roi[p2]); cvCopy (dst_img, tmp_img[1]); cvCopy (tmp_img[0], dst_img); cvSetImageROI (dst_img, roi[p1]); cvCopy (tmp_img[1], dst_img); } cvResetImageROI (dst_img); cvResize (dst_img, src_img); // (4)画像の表示 cvNamedWindow ("Image", CV_WINDOW_AUTOSIZE); cvShowImage ("Image", src_img); cvWaitKey (0); cvDestroyWindow ("Image"); cvReleaseImage (&src_img); cvReleaseImage (&dst_img); cvReleaseImage (&tmp_img[0]); cvReleaseImage (&tmp_img[1]); return 0; }
// (1)画像を読み込む
コマンド引数で指定されたファイル名の画像(入力画像)をオープンし,関数
cvLoadImage()で読み込む.2番目の引数にCV_LOAD_IMAGE_ANYDEPTH |
CV_LOAD_IMAGE_ANYCOLORを指定することで,元画像のデプス,チャンネルを変
更せずに読み込む.
読み込まれた画像は,指定された分割数で等分可能なように,関数cvResize()によりサイズを調整する.
この際に,補間方法を指定しない場合は,デフォルト値のCV_INTER_LINEARが利用される.
// (2)画像を分割するための矩形を設定
画像を分割するために,構造体CvRectを設定する.
// (3)ROIを利用して部分画像を入れ換える
設定された矩形 CvRect を利用して画像に ROI を設定し,その ROI 全体をコピーして,ランダムに入れ換える.
このような処理は ROI を利用しなくても,MASKの利用や1ピクセルづつコピーする,実際に画像を分割するなどの手法により実現できるが,
ROI を利用することで比較的単純な処理にできる.
また,関数 cvRandShuffle() を用いると,繰り返し計算なしで画像をシャッフルすることが可能である.
入れ換え後は,ROIを解放し,サイズを元の大きさ(入力画像サイズ)に戻す.
サイズを戻す際も同様に, 関数cvResize()を利用し,ここで補間方法を指定しない場合は,デフォルト値のCV_INTER_LINEARが利用される.
// (4)画像の表示
部分画像がシャッフルされた画像含むウィンドウを実際に表示し,何かキーが押されるまで待つ.
実行結果例
[左]入力画像 [右]処理結果画像の連結 cvSetImageROI, cvResetImageROI
複数の画像を横方向に連結して1枚の画像にする
サンプルコード
#include <cv.h> #include <highgui.h> #include <stdlib.h> /* プロトタイプ宣言 */ IplImage *combine_image (int num, IplImage ** tmp); /* メイン関数 */ int main (int argc, char **argv) { int i, img_num; IplImage **img; IplImage *combined_img; // (1)コマンド引数で指定された画像を全て読み込む if (argc < 2) { return 1; } else { img_num = argc - 1; img = (IplImage **) cvAlloc (sizeof (IplImage *) * img_num); for (i = 0; i < img_num; i++) { img[i] = cvLoadImage (argv[i + 1], CV_LOAD_IMAGE_COLOR); if (img[i] == 0) return -1; } } // (2)画像を連結する combined_img = combine_image (img_num, img); cvNamedWindow ("Image", CV_WINDOW_AUTOSIZE); cvShowImage ("Image", combined_img); cvWaitKey (0); cvDestroyWindow ("Image"); cvReleaseImage (&combined_img); for (i = 0; i < img_num; i++) { cvReleaseImage (&img[i]); } cvFree (&img); return 0; } /* 画像を連結する関数 */ IplImage * combine_image (int num, IplImage ** tmp) { int i; int width = 0, height = 0; IplImage *cimg; CvRect roi = cvRect (0, 0, 0, 0); // (3)与えられた各画像から,連結後の幅と高さを求める for (i = 0; i < num; i++) { width += tmp[i]->width; height = height < tmp[i]->height ? tmp[i]->height : height; } cimg = cvCreateImage (cvSize (width, height), IPL_DEPTH_8U, 3); cvZero (cimg); // (4)ROIを利用して各画像をコピーする for (i = 0; i < num; i++) { roi.width = tmp[i]->width; roi.height = tmp[i]->height; cvSetImageROI (cimg, roi); cvCopy (tmp[i], cimg); roi.x += roi.width; } cvResetImageROI (cimg); return cimg; }
// (1)コマンド引数で指定された画像を全て読み込む
コマンド引数で指定された画像を全て,カラー画像として読み込む.そのなかに1つでも読み込めない画像がある場合には終了する.
// (2)画像を連結する
あらかめ定義した関数combined_img()を呼び出して,画像を横方向に連結する.
この例では単純に画像の上辺を揃えて横方向に連結を行うだけであるが,
画像を折り返して連結したり,さらにいわゆる「矩形パッキング問題」をの解を求めることで無駄な領域が小さくなるように連結することもできる.
また,このサンプルでは連結する関数combined_image()に画像のポインタの配列を渡しているが,
可変引数を利用(stdarg.h)して,combine_image()を次のように定義する方法もある.
IplImage* combine_image(int num, ...) { va_list list; int i; int width = 0, height = 0; IplImage *tmp[num]; IplImage *cimg; CvRect roi = cvRect(0, 0, 0, 0); va_start(list, num); for(i=0; iwidth; height = height < tmp[i]->height ? tmp[i]->height : height; } va_end(list); cimg = cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 3); cvZero(cimg); for(i=0; i width; roi.height = tmp[i]->height; cvSetImageROI(cimg, roi); cvCopy(tmp[i], cimg); roi.x += roi.width; } cvResetImageROI(cimg); return cimg; }
// (3)与えられた各画像から,連結後の幅と高さを求める
与えられた各画像の幅の合計値を連結後の画像幅(横方向に連結するので)とし,
また,与えられた各画像の最大の高さを連結後の画像高さとして,
画像を作成する.
作成された画像を,関数cvZero()により初期化する.
// (4)ROIを利用して各画像をコピーする
各画像のサイズ(幅と高さ)に会わせてROIを設定し,各画像をcvCopy()によりコピーする.
マスクは指定しないので,デフォルト値NULLが利用される.
また,最後に関数cvResetImageROI()を利用して,設定したROIをリセットする事をを忘れないように注意.