アフィン変換(変換前後の3点の組を与えて,変換行列を求める)

13 1月 2010 Under: opencv2.x-samples

C

#include <cv.h>
#include <highgui.h>

int
main(int argc, char **argv)
{
  IplImage *src_img = 0, *dst_img = 0;
  CvMat *affine_matrix = NULL;
  CvPoint2D32f src_pt[3], dst_pt[3];
  char *imagename;

  // (1)load a specified file as a 3-channel color image,
  //    and allocate a destination image
  imagename = argc > 1 ? argv[1] : "../image/fruit.png";
  src_img = cvLoadImage(imagename, CV_LOAD_IMAGE_COLOR);
  if(src_img == 0)
    return -1;
  dst_img = cvCloneImage(src_img);

  // (2)setting three points before and after transformation, 
  //    an affine transformation matrix was calculated by getAffineTransform
  src_pt[0] = cvPoint2D32f(200.0, 200.0);
  src_pt[1] = cvPoint2D32f(250.0, 200.0);
  src_pt[2] = cvPoint2D32f(200.0, 100.0);
  dst_pt[0] = cvPoint2D32f(300.0, 100.0);
  dst_pt[1] = cvPoint2D32f(300.0, 50.0);
  dst_pt[2] = cvPoint2D32f(200.0, 100.0);
  affine_matrix = cvCreateMat(2, 3, CV_32FC1);
  cvGetAffineTransform(src_pt, dst_pt, affine_matrix);

  // (3)rotate the image by warpAffine taking the affine matrix
  cvWarpAffine(src_img, dst_img, affine_matrix, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0));


  // (4)show source and destination images with two triangles representing the transformation
  cvLine(src_img, cvPointFrom32f(src_pt[0]), cvPointFrom32f(src_pt[1]), CV_RGB(0,255,255), 2, 8, 0);
  cvLine(src_img, cvPointFrom32f(src_pt[1]), cvPointFrom32f(src_pt[2]), CV_RGB(0,255,255), 2, 8, 0);
  cvLine(src_img, cvPointFrom32f(src_pt[2]), cvPointFrom32f(src_pt[0]), CV_RGB(0,255,255), 2, 8, 0);
  cvLine(src_img, cvPointFrom32f(dst_pt[0]), cvPointFrom32f(dst_pt[1]), CV_RGB(255,0,255), 2, 8, 0);
  cvLine(src_img, cvPointFrom32f(dst_pt[1]), cvPointFrom32f(dst_pt[2]), CV_RGB(255,0,255), 2, 8, 0);
  cvLine(src_img, cvPointFrom32f(dst_pt[2]), cvPointFrom32f(dst_pt[0]), CV_RGB(255,0,255), 2, 8, 0);

  cvNamedWindow("src", CV_WINDOW_AUTOSIZE);
  cvNamedWindow("dst", CV_WINDOW_AUTOSIZE);
  cvShowImage("src", src_img);
  cvShowImage("dst", dst_img);
  cvWaitKey(0);

  cvDestroyWindow("src");
  cvDestroyWindow("dst");
  cvReleaseImage(&src_img);
  cvReleaseImage(&dst_img);
  cvReleaseMat(&affine_matrix);

  return 0;
}

// (1)画像の読み込み(カラー),出力用画像領域の確保を行ないます.

指定されたカラー画像を読み込みます.読み込みに失敗した場合,return -1 で終了します.また,入力画像と同じサイズ,同じビット深度の出力画像領域を確保します.

// (2)三角形の回転前と回転後の対応する頂点をそれぞれセットし cvGetAffineTransform を用いてアフィン行列を求めます.

アフィン変換前の3点と,変換後の3点を指定して, cvGetAffineTransform によりアフィン変換行列を求めます.
ここでは,src_pnt[]に回転前の頂点座標を,dst_pnt[]に回転後の対応点をセットしています.セットする変換前(水色),変換後(ピンク)の3点を三角形の頂点と見なすと,以下の画像のように表すことができます.
このサンプルプログラムでは,三角形のそれぞれの対応点が既知としてコードを記述していますが,実際のプログラムでは三角形似対応する特徴点のトラッキングなどにより点対応を求める事が出来ます.
これらの点対応を引数に関数 cvGetAffineTransform を呼び出すと, affine_matrix にアフィン変換行列が格納されます.

// (3)求めたアフィン変換行列を引数に与え,cvWarpAffine を用いて画像を回転させます.

変換後対応のとれない画素に対しては,関数 cvWarpAffine の最後の引数(fillval) としてcvScalarAll(0) を指定し,変換後の画素を黒で埋めます.このように2次元のアフィン変換を用いると,画像の拡大・縮小,並進・回転を行なう事が可能です.

// (4)元画像上に変換前後の三点をあらわす三角形を描画し,結果を表示します.

ウィンドウを生成し,そこに入力画像,結果画像を表示して,何かキーが押されるまで待ちます.

C++

#include <cv.h>
#include <highgui.h>

using namespace cv;

int
main(int argc, char **argv)
{
  // (1)load a specified file as a 3-channel color image
  const string imagename = argc > 1 ? argv[1] : "../image/fruit.png";
  Mat src_img = imread(imagename);
  if(!src_img.data)
    return -1;

  // (2)setting three points before and after transformation, 
  //    an affine transformation matrix was calculated by getAffineTransform
  const Point2f src_pt[] = { Point2f(200.0, 200.0), Point2f(250.0, 200.0), Point2f(200.0, 100.0) };
  const Point2f dst_pt[] = { Point2f(300.0, 100.0), Point2f(300.0, 50.0), Point2f(200.0, 100.0) };
  const Mat affine_matrix = getAffineTransform(src_pt, dst_pt);

  // (3)rotate the image by warpAffine taking the affine matrix
  Mat dst_img;
  warpAffine(src_img, dst_img, affine_matrix, src_img.size());

  // (4)show source and destination image with two triangles representing the transformation
  line(src_img, src_pt[0], src_pt[1], Scalar(255,255,0), 2);
  line(src_img, src_pt[1], src_pt[2], Scalar(255,255,0), 2);
  line(src_img, src_pt[2], src_pt[0], Scalar(255,255,0), 2);
  line(src_img, dst_pt[0], dst_pt[1], Scalar(255,0,255), 2);
  line(src_img, dst_pt[1], dst_pt[2], Scalar(255,0,255), 2);
  line(src_img, dst_pt[2], dst_pt[0], Scalar(255,0,255), 2);

  namedWindow("src", CV_WINDOW_AUTOSIZE);
  namedWindow("dst", CV_WINDOW_AUTOSIZE);
  imshow("src", src_img);
  imshow("dst", dst_img);
  waitKey(0);

  return 0;
}

// (1)画像を読み込みます(カラー).

指定されたカラー画像を読み込みます.読み込みに失敗した場合,return -1 で終了します.

// (2)三角形の回転前と回転後の対応する頂点をそれぞれセットし getAffineTransform を用いてアフィン行列を求めます.

アフィン変換前の3点と,変換後の3点を指定して, getAffineTransform によりアフィン変換行列を求めます.
ここでは,src_pnt[]に回転前の頂点座標を,dst_pnt[]に回転後の対応点をセットしています.セットする変換前(水色),変換後(ピンク)の3点を三角形の頂点と見なすと,以下の画像のように表すことができます.
このサンプルプログラムでは,三角形のそれぞれの対応点が既知としてコードを記述していますが,実際のプログラムでは三角形似対応する特徴点のトラッキングなどにより点対応を求める事が出来ます.
これらの点対応を引数に関数 getAffineTransform を呼び出すと, affine_matrix にアフィン変換行列が格納されます.

// (3)求めたアフィン変換行列を引数に与え,warpAffineを用いて画像を回転させます.

関数 warpAffine のデフォルト引数により,変換は「定数境界モード」かつ「定数値=Scalar()」なので,変換後のピクセル値が求められない領域は黒で埋められます.また,出力画像が格納される dst_img は,関数 warpAffine の内部で自動的に確保されます.このように2次元のアフィン変換を用いると,画像の拡大・縮小,並進・回転を行なう事が可能です.

// (4)元画像上に変換前後の三点をあらわす三角形を描画し,結果を表示します.

ウィンドウを生成し,そこに入力画像,結果画像を表示して,何かキーが押されるまで待ちます.画像領域のメモリを解放する必要はありません.

実行結果例

(左)元画像,(右)変換後の画像