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]]準拠