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

#contents

*void cvComputeCorrespondEpilines( CvMat* points, int whichImage, CvMat* fmatrix, CvMat* lines ); [#v297770d]

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

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

**返り値 [#w5b621d4]
-void型なのでなし.

*void computeCorrespondEpilines( Mat& points, int whichImage, Mat& fmatrix, std::vector<Vec3f>& lines); [#n0d4a0a7]

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

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

*サンプルコード [#a62b1957]

#geshi(c++,number){{
 #include <opencv2/core/core.hpp>
 #include <opencv2/highgui/highgui.hpp>
 #include <opencv2/calib3d/calib3d.hpp>
 #include <iostream>
 
 const char windowName[]		= "Test";
 const char LeftImageFileName[]		= "hogehoge\\Left.jpg";
 const char RightImageFileName[]	= "hogehoge\\Right.jpg";
 const int pointCount			= 10;
 
 using namespace cv;
 
 int main(int argc, char** argv);
 // 対応する特徴点を適当に読み込む関数.
 void readPointsLeft(const Mat& imageLeft, Mat& pointsLeft);
 void readPointsRight(const Mat& imageRight, Mat& pointsRight);
 // エピポーラ線を描画する関数
 void drawEpilines(Mat& image, std::vector<Vec3f> lines, std::vector<uchar> mask);
 // 直線の方程式があるx座標を通過するときのy座標を求める
 inline double solveY(Vec3f efficient, double x);
 // ×点を描画する関数
 void drawCross(Mat& image, Mat& points);
 // 画像を2枚描画する関数
 void showBothImage(Mat *image); 
 
 int main(int argc, char** argvv){
 
         // 2枚の画像.
         Mat inputImage[2];
         inputImage[0]   = imread(LeftImageFileName);  // ファイル名を適当に設定
         inputImage[1]   = imread(RightImageFileName); // ファイル名を適当に設定

         // 2枚とも表示
         showBothImage(inputImage);
 
         // 各画像から特徴点を読み込む
         // 今回は事前に取った特徴点をハードコーディングしてある
         Mat points[2];
         readPointsLeft(inputImage[0], points[0]);
         readPointsRight(inputImage[1], points[1]);
 
         // 画像に特徴点を×として描画
         for(int i = 0;i < 2;i++){
                 drawCross(inputImage[i], points[i]);
         }
 
         // 2枚とも表示
         showBothImage(inputImage);
 
         Mat FMatrix;              // F行列用変数
         std::vector<uchar> mask;  // F行列計算に使われた特徴点を表す変数
         // F行列を計算
         // ここでwhichImageの1か2が決まる↓ここに入るのが1番の画像
         FMatrix = findFundamentalMat(points[0], points[1], mask);
         //                     ↑ここに入るのが2番の画像
 
         for(int i = 0;i < 2;i++){
                 std::vector<Vec3f> lines;
                 // F行列と特徴点から,エピポーラ線の方程式を計算
                 computeCorrespondEpilines(points[i], 1+i, FMatrix, lines);
                 // ↑で計算されたエピポーラ線の方程式を元に描画
                 drawEpilines(inputImage[1-i], lines, mask);
         }
 
         // 2枚とも表示
         showBothImage(inputImage);
 
         // ウィンドウを破棄
         destroyWindow(windowName);
 
         return 0;
 }
 
 // 特徴点の座標を格納する関数
 // 格納方法は適当で良いが,格納順に注目
 void readPointsLeft(const Mat& imageLeft, Mat& pointsLeft){
         static float points[]   = {     17,175,  // x1, y1,
                                         370,24,  // x2, y2,
                                         192,456, // :
                                         614,202, // :
                                         116,111, // :
                                         305,32,  // :
                                         249,268, // :
                                         464,157, // :
                                         259,333, // :
                                         460,224};// x10, y10
         // floatの配列を元にMatを作成
         // double (CV_64F)にすると findFundamentalMat でassertionエラーが出るので注意
         // CV_32FC2 にしたのは,データの格納方法をわかり易くするため
         pointsLeft      = Mat::Mat(cv::Size(1, pointCount), CV_32FC2, (void*)points);
 }
 
 // 基本的に readPointsLeft と同じ
 void readPointsRight(const Mat& imageRight, Mat& pointsRight){
         static float points[]   = {     295,28,
                                         584,221,
                                         67,172,
                                         400,443,
                                         330,9,
                                         480,140,
                                         181,140,
                                         350,265,
                                         176,193,
                                         333,313};
         pointsRight     = Mat::Mat(cv::Size(1, pointCount), CV_32FC2, (void*)points);
 }
 
 // 方程式を元に直線を描画する
 void drawEpilines(Mat& image, std::vector<Vec3f> lines, std::vector<uchar> mask){
         // 直線の方程式を格納したvector
         std::vector<Vec3f>::iterator it;
         // 使われた特徴点フラグ
         std::vector<uchar>::iterator itmask;
         it      = lines.begin();
         itmask  = mask.begin();
         while(it != lines.end()){
                 if(*itmask == 0){
                         // mask の値が0ということは,計算に使われなかったということ
                         it++;
                         itmask++;
                         continue;
                 }
                 if((*it)[1]){
                         // Y軸と交わる直線の場合
                         double width    = image.size().width;
                         // left は x=0 における直線の通過座標
                         Point2d left    = Point2d(0.0,   solveY(*it, 0.0));
                         // right は x=width における直線の通過座標
                         Point2d right   = Point2d(width, solveY(*it, width));
                         // 左端から右端に直線を引く
                         line(image, left, right, Scalar(0, 0, 255), 2);
                 }else{
                         // Y軸に平行な直線の場合
                         // x座標は一定なので,あらかじめ求める
                         double x        = -(*it)[2]/(*it)[0];
                         // 画像の上端を通過する座標
                         Point2d top     = Point2d(x, 0.0);
                         // 画像の下端を通過する座標
                         Point2d bottom  = Point2d(x, image.size().height);
                         // 上端から下端に直線を引く
                         line(image, top, bottom, Scalar(0, 0, 255), 2);
                 }
                 it++;
                 itmask++;
         }
 }
 
 // 直線の方程式とx座標からy座標を計算する
 inline double solveY(Vec3f efficient, double x){
         return - (efficient[0] * x + efficient[2]) / efficient[1];
 }
 
 // points で与えた座標を中心に×印を描く
 void drawCross(Mat& image, Mat& points){
 
         Point2f cross1  = Point2f(2, 2);
         Point2f cross2  = Point2f(2, -2);
 
         for(int i = 0;i < points.size().height;i++){
                 Point2f point   = Point2f(points.at<float>(i, 0), points.at<float>(i, 1));
                 line(image, point - cross1, point + cross1, Scalar(255, 255 - 20 * i, 20 * i));
                 line(image, point - cross2, point + cross2, Scalar(255, 255 - 20 * i, 20 * i));
         }
 }
 
 // ウィンドウに画像を表示
 void showBothImage(Mat *image){
         namedWindow(windowName, CV_WINDOW_FREERATIO | CV_GUI_EXPANDED | CV_WINDOW_AUTOSIZE);
         for(int i = 0;i < 2;i++){
                 imshow(windowName, image[i]);
                 waitKey(0);
         }
 }
}}

*実体ファイル [#m91740bc]
-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

*実行結果 [#ze886565]
左カメラ画像.見づらいけれど,計10点×印が付いている.&br();
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点×印が付いている.&br();
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行列を求める最適化の過程で,外れ値としてはじかれた値&br();
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行列を求める最適化の過程で,外れ値としてはじかれた値&br();
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

*注意 [#b0043d2e]
-N<=3で投影するときは注意が必要.
-&mimetex(\left(\begin{array}{cc}x_1&x_2\\  y_1& y_2 \\ \end{array}\right));と&mimetex(\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]]準拠

トップ   編集 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS