F行列に基づき,エピポーラ線の情報を計算してくれる

void cvComputeCorrespondEpilines( CvMat* points, int whichImage, CvMat* fmatrix, CvMat* lines );

fmatrixの情報に基づき,points内の点に対応する直線の方程式を計算する

引数

  • points:CvMat*型の点
  • whichImage:1から2への投影か,2から1への投影か.1か2を指定する
  • fmatrix:F行列
  • lines:CvMat*型の直線の方程式

返り値

  • void型なのでなし.

void computeCorrespondEpilines( Mat& points, int whichImage, Mat& fmatrix, std::vector<Vec3f>& lines);

  • C++インタフェース版
  • linesがCvMat型からVec3fのvectorに変わってる

解説

  • pointsは2xN,Nx2,3xN,Nx3のいずれか(Nは点数)
  • なお,計算してくれるのは直線の方程式だけなので,描画の作業自体は自分でやる必要がある.
  • whichImageには1か2を指定
    • 1:1枚目の点→2枚目の直線に(F行列をそのまま使用)
    • 2:2枚目の点→1枚目の直線に(F行列を転置して使用)
    • F行列計算時の順序がポイント
  • linesに格納されるベクトルlの特性
    • l^\top [\begin{array}x y 1\end{array}\left]^\top = 0
    • a * x + b * y + c = 0
    • a^2+b^2=1となるよう正規化されている

サンプルコード

  1.  #include <opencv2/core/core.hpp>
  2.  #include <opencv2/highgui/highgui.hpp>
  3.  #include <opencv2/calib3d/calib3d.hpp>
  4.  #include <iostream>
  5.  
  6.  const char windowName[] = "Test";
  7.  const char LeftImageFileName[] = "hogehoge\\Left.jpg";
  8.  const char RightImageFileName[] = "hogehoge\\Right.jpg";
  9.  const int pointCount = 10;
  10.  
  11.  using namespace cv;
  12.  
  13.  int main(int argc, char** argv);
  14.  // 対応する特徴点を適当に読み込む関数.
  15.  void readPointsLeft(const Mat& imageLeft, Mat& pointsLeft);
  16.  void readPointsRight(const Mat& imageRight, Mat& pointsRight);
  17.  // エピポーラ線を描画する関数
  18.  void drawEpilines(Mat& image, std::vector<Vec3f> lines, std::vector<uchar> mask);
  19.  // 直線の方程式があるx座標を通過するときのy座標を求める
  20.  inline double solveY(Vec3f efficient, double x);
  21.  // ×点を描画する関数
  22.  void drawCross(Mat& image, Mat& points);
  23.  // 画像を2枚描画する関数
  24.  void showBothImage(Mat *image);
  25.  
  26.  int main(int argc, char** argvv){
  27.  
  28.          // 2枚の画像.
  29.          Mat inputImage[2];
  30.          inputImage[0]   = imread(LeftImageFileName);  // ファイル名を適当に設定
  31.          inputImage[1]   = imread(RightImageFileName); // ファイル名を適当に設定
  32.  
  33.          // 2枚とも表示
  34.          showBothImage(inputImage);
  35.  
  36.          // 各画像から特徴点を読み込む
  37.          // 今回は事前に取った特徴点をハードコーディングしてある
  38.          Mat points[2];
  39.          readPointsLeft(inputImage[0], points[0]);
  40.          readPointsRight(inputImage[1], points[1]);
  41.  
  42.          // 画像に特徴点を×として描画
  43.          for(int i = 0;i < 2;i++){
  44.                  drawCross(inputImage[i], points[i]);
  45.          }
  46.  
  47.          // 2枚とも表示
  48.          showBothImage(inputImage);
  49.  
  50.          Mat FMatrix;              // F行列用変数
  51.          std::vector<uchar> mask;  // F行列計算に使われた特徴点を表す変数
  52.          // F行列を計算
  53.          // ここでwhichImageの1か2が決まる↓ここに入るのが1番の画像
  54.          FMatrix = findFundamentalMat(points[0], points[1], mask);
  55.          //                     ↑ここに入るのが2番の画像
  56.  
  57.          for(int i = 0;i < 2;i++){
  58.                  std::vector<Vec3f> lines;
  59.                  // F行列と特徴点から,エピポーラ線の方程式を計算
  60.                  computeCorrespondEpilines(points[i], 1+i, FMatrix, lines);
  61.                  // ↑で計算されたエピポーラ線の方程式を元に描画
  62.                  drawEpilines(inputImage[1-i], lines, mask);
  63.          }
  64.  
  65.          // 2枚とも表示
  66.          showBothImage(inputImage);
  67.  
  68.          // ウィンドウを破棄
  69.          destroyWindow(windowName);
  70.  
  71.          return 0;
  72.  }
  73.  
  74.  // 特徴点の座標を格納する関数
  75.  // 格納方法は適当で良いが,格納順に注目
  76.  void readPointsLeft(const Mat& imageLeft, Mat& pointsLeft){
  77.          static float points[]   = {     17,175,  // x1, y1,
  78.                                          370,24,  // x2, y2,
  79.                                          192,456, // :
  80.                                          614,202, // :
  81.                                          116,111, // :
  82.                                          305,32,  // :
  83.                                          249,268, // :
  84.                                          464,157, // :
  85.                                          259,333, // :
  86.                                          460,224};// x10, y10
  87.          // floatの配列を元にMatを作成
  88.          // double (CV_64F)にすると findFundamentalMat でassertionエラーが出るので注意
  89.          // CV_32FC2 にしたのは,データの格納方法をわかり易くするため
  90.          pointsLeft      = Mat::Mat(cv::Size(1, pointCount), CV_32FC2, (void*)points);
  91.  }
  92.  
  93.  // 基本的に readPointsLeft と同じ
  94.  void readPointsRight(const Mat& imageRight, Mat& pointsRight){
  95.          static float points[]   = {     295,28,
  96.                                          584,221,
  97.                                          67,172,
  98.                                          400,443,
  99.                                          330,9,
  100.                                          480,140,
  101.                                          181,140,
  102.                                          350,265,
  103.                                          176,193,
  104.                                          333,313};
  105.          pointsRight     = Mat::Mat(cv::Size(1, pointCount), CV_32FC2, (void*)points);
  106.  }
  107.  
  108.  // 方程式を元に直線を描画する
  109.  void drawEpilines(Mat& image, std::vector<Vec3f> lines, std::vector<uchar> mask){
  110.          // 直線の方程式を格納したvector
  111.          std::vector<Vec3f>::iterator it;
  112.          // 使われた特徴点フラグ
  113.          std::vector<uchar>::iterator itmask;
  114.          it      = lines.begin();
  115.          itmask  = mask.begin();
  116.          while(it != lines.end()){
  117.                  if(*itmask == 0){
  118.                          // mask の値が0ということは,計算に使われなかったということ
  119.                          it++;
  120.                          itmask++;
  121.                          continue;
  122.                  }
  123.                  if((*it)[1]){
  124.                          // Y軸と交わる直線の場合
  125.                          double width    = image.size().width;
  126.                          // left は x=0 における直線の通過座標
  127.                          Point2d left    = Point2d(0.0,   solveY(*it, 0.0));
  128.                          // right は x=width における直線の通過座標
  129.                          Point2d right   = Point2d(width, solveY(*it, width));
  130.                          // 左端から右端に直線を引く
  131.                          line(image, left, right, Scalar(0, 0, 255), 2);
  132.                  }else{
  133.                          // Y軸に平行な直線の場合
  134.                          // x座標は一定なので,あらかじめ求める
  135.                          double x        = -(*it)[2]/(*it)[0];
  136.                          // 画像の上端を通過する座標
  137.                          Point2d top     = Point2d(x, 0.0);
  138.                          // 画像の下端を通過する座標
  139.                          Point2d bottom  = Point2d(x, image.size().height);
  140.                          // 上端から下端に直線を引く
  141.                          line(image, top, bottom, Scalar(0, 0, 255), 2);
  142.                  }
  143.                  it++;
  144.                  itmask++;
  145.          }
  146.  }
  147.  
  148.  // 直線の方程式とx座標からy座標を計算する
  149.  inline double solveY(Vec3f efficient, double x){
  150.          return - (efficient[0] * x + efficient[2]) / efficient[1];
  151.  }
  152.  
  153.  // points で与えた座標を中心に×印を描く
  154.  void drawCross(Mat& image, Mat& points){
  155.  
  156.          Point2f cross1  = Point2f(2, 2);
  157.          Point2f cross2  = Point2f(2, -2);
  158.  
  159.          for(int i = 0;i < points.size().height;i++){
  160.                  Point2f point   = Point2f(points.at<float>(i, 0), points.at<float>(i, 1));
  161.                  line(image, point - cross1, point + cross1, Scalar(255, 255 - 20 * i, 20 * i));
  162.                  line(image, point - cross2, point + cross2, Scalar(255, 255 - 20 * i, 20 * i));
  163.          }
  164.  }
  165.  
  166.  // ウィンドウに画像を表示
  167.  void showBothImage(Mat *image){
  168.          namedWindow(windowName, CV_WINDOW_FREERATIO | CV_GUI_EXPANDED | CV_WINDOW_AUTOSIZE);
  169.          for(int i = 0;i < 2;i++){
  170.                  imshow(windowName, image[i]);
  171.                  waitKey(0);
  172.          }
  173.  }

実体ファイル

  • OpenCV1.1以前の場合
    • cv/include/cv.h
    • cv/src/cvfundam.cpp
  • OpenCV2.0,2.1
    • opencv/cv.h
    • src/cv/cvfundam.cpp
  • OpenCV2.2
    • opencv2/calib3d/calib3d.hpp
    • modules/calib3d/src/fundam.cpp

実行結果

左カメラ画像.見づらいけれど,計10点×印が付いている.
http://tessy.org/wiki/index.php?plugin=attach&pcmd=open&refer=%A5%A8%A5%D4%A5%DD%A1%BC%A5%E9%C0%FE%A4%CE%B7%D7%BB%BB&file=left_before.png

右カメラ画像.見づらいけれど,計10点×印が付いている.
http://tessy.org/wiki/index.php?plugin=attach&pcmd=open&refer=%A5%A8%A5%D4%A5%DD%A1%BC%A5%E9%C0%FE%A4%CE%B7%D7%BB%BB&file=right_before.png

左カメラ画像に引いた,右カメラからのエピポーラ線.何点かエピポーラ線が描かれてない点はF行列を求める最適化の過程で,外れ値としてはじかれた値
http://tessy.org/wiki/index.php?plugin=attach&pcmd=open&refer=%A5%A8%A5%D4%A5%DD%A1%BC%A5%E9%C0%FE%A4%CE%B7%D7%BB%BB&file=left_after.png

右カメラ画像に引いた,左カメラからのエピポーラ線.何点かエピポーラ線が描かれてない点はF行列を求める最適化の過程で,外れ値としてはじかれた値
http://tessy.org/wiki/index.php?plugin=attach&pcmd=open&refer=%A5%A8%A5%D4%A5%DD%A1%BC%A5%E9%C0%FE%A4%CE%B7%D7%BB%BB&file=right_after.png

注意

  • N<=3で投影するときは注意が必要.
  • \left(\begin{array}{cc}x_1&x_2\\  y_1& y_2 \\ \end{array}\right)\left(\begin{array}{cc}x_1&y_1\\  x_2& y_2 \\ \end{array}\right)で区別がつかなくなるため.
  • 問題を回避するためには,サンプルコードの様に幅1でマルチチャンネルの列ベクトルにデータを格納すれば良い
  • C++インタフェースはCインタフェースのラッパー関数.(2.2で確認)

ジャンル:OpenCV:OpenCV 1.0:OpenCV 1.1:OpenCV 2.0:OpenCV 2.1:OpenCV 2.2準拠


添付ファイル: fileright_after.png 10495件 [詳細] fileleft_after.png 10468件 [詳細] fileright_before.png 10465件 [詳細] fileleft_before.png 10475件 [詳細]

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2011-03-04 (金) 10:41:16 (2457d)