Skip to content

Commit

Permalink
Add tutorial and codes for the homography tutorial.
Browse files Browse the repository at this point in the history
  • Loading branch information
catree committed Dec 14, 2017
1 parent 7b701fe commit b417fb0
Show file tree
Hide file tree
Showing 22 changed files with 1,196 additions and 0 deletions.
7 changes: 7 additions & 0 deletions doc/opencv.bib
Original file line number Diff line number Diff line change
Expand Up @@ -554,6 +554,13 @@ @INCOLLECTION{MOG2001
pages = {135--144},
publisher = {Springer}
}
@book{Ma:2003:IVI,
author = {Ma, Yi and Soatto, Stefano and Kosecka, Jana and Sastry, S. Shankar},
title = {An Invitation to 3-D Vision: From Images to Geometric Models},
year = {2003},
isbn = {0387008934},
publisher = {SpringerVerlag},
}
@ARTICLE{Malis,
author = {Malis, Ezio and Vargas, Manuel and others},
title = {Deeper understanding of the homography decomposition for vision-based control},
Expand Down
467 changes: 467 additions & 0 deletions doc/tutorials/features2d/homography/homography.markdown

Large diffs are not rendered by default.

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions doc/tutorials/features2d/table_of_content_features2d.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,10 @@ OpenCV.
*Author:* Fedor Morozov

Using *AKAZE* and *ORB* for planar object tracking.

- @subpage tutorial_homography

*Compatibility:* \> OpenCV 3.0

This tutorial will explain the basic concepts of the homography with some
demonstration codes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
%YAML:1.0
---
image_width: 640
image_height: 480
board_width: 9
board_height: 6
square_size: 1.
aspectRatio: 1.
flags: 2
camera_matrix: !!opencv-matrix
rows: 3
cols: 3
dt: d
data: [ 5.3591575307485539e+02, 0., 3.4228314953752817e+02, 0.,
5.3591575307485539e+02, 2.3557082321320789e+02, 0., 0., 1. ]
distortion_coefficients: !!opencv-matrix
rows: 5
cols: 1
dt: d
data: [ -2.6637290673868386e-01, -3.8586722644459073e-02,
1.7831841406179300e-03, -2.8122035403651473e-04,
2.3838760574917545e-01 ]
avg_reprojection_error: 3.9259109564815858e-01
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
#include <iostream>
#include <opencv2/opencv_modules.hpp>
#ifdef HAVE_OPENCV_ARUCO
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>

using namespace std;
using namespace cv;

namespace
{
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };

void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
{
corners.resize(0);

switch (patternType)
{
case CHESSBOARD:
case CIRCLES_GRID:
//! [compute-chessboard-object-points]
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float(j*squareSize),
float(i*squareSize), 0));
//! [compute-chessboard-object-points]
break;

case ASYMMETRIC_CIRCLES_GRID:
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float((2*j + i % 2)*squareSize),
float(i*squareSize), 0));
break;

default:
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
}
}

void poseEstimationFromCoplanarPoints(const string &imgPath, const string &intrinsicsPath, const Size &patternSize,
const float squareSize)
{
Mat img = imread(imgPath);
Mat img_corners = img.clone(), img_pose = img.clone();

//! [find-chessboard-corners]
vector<Point2f> corners;
bool found = findChessboardCorners(img, patternSize, corners);
//! [find-chessboard-corners]

if (!found)
{
cout << "Cannot find chessboard corners." << endl;
return;
}
drawChessboardCorners(img_corners, patternSize, corners, found);
imshow("Chessboard corners detection", img_corners);

//! [compute-object-points]
vector<Point3f> objectPoints;
calcChessboardCorners(patternSize, squareSize, objectPoints);
vector<Point2f> objectPointsPlanar;
for (size_t i = 0; i < objectPoints.size(); i++)
{
objectPointsPlanar.push_back(Point2f(objectPoints[i].x, objectPoints[i].y));
}
//! [compute-object-points]

//! [load-intrinsics]
FileStorage fs(intrinsicsPath, FileStorage::READ);
Mat cameraMatrix, distCoeffs;
fs["camera_matrix"] >> cameraMatrix;
fs["distortion_coefficients"] >> distCoeffs;
//! [load-intrinsics]

//! [compute-image-points]
vector<Point2f> imagePoints;
undistortPoints(corners, imagePoints, cameraMatrix, distCoeffs);
//! [compute-image-points]

//! [estimate-homography]
Mat H = findHomography(objectPointsPlanar, imagePoints);
cout << "H:\n" << H << endl;
//! [estimate-homography]

//! [pose-from-homography]
// Normalization to ensure that ||c1|| = 1
double norm = sqrt(H.at<double>(0,0)*H.at<double>(0,0) +
H.at<double>(1,0)*H.at<double>(1,0) +
H.at<double>(2,0)*H.at<double>(2,0));

H /= norm;
Mat c1 = H.col(0);
Mat c2 = H.col(1);
Mat c3 = c1.cross(c2);

Mat tvec = H.col(2);
Mat R(3, 3, CV_64F);

for (int i = 0; i < 3; i++)
{
R.at<double>(i,0) = c1.at<double>(i,0);
R.at<double>(i,1) = c2.at<double>(i,0);
R.at<double>(i,2) = c3.at<double>(i,0);
}
//! [pose-from-homography]

//! [display-pose]
Mat rvec;
Rodrigues(R, rvec);
aruco::drawAxis(img_pose, cameraMatrix, distCoeffs, rvec, tvec, 2*squareSize);
imshow("Pose from coplanar points", img_pose);
waitKey();
//! [display-pose]
}

const char* about = "Code for homography tutorial.\n"
"Example 1: pose from homography with coplanar points.\n";

const char* params
= "{ h help | false | print usage }"
"{ image | | path to a chessboard image (left04.jpg) }"
"{ intrinsics | | path to camera intrinsics (left_intrinsics.yml) }"
"{ width w | 9 | chessboard width }"
"{ height h | 6 | chessboard height }"
"{ square_size | 0.025 | chessboard square size }";
}

int main(int argc, char *argv[])
{
CommandLineParser parser(argc, argv, params);

if (parser.get<bool>("help"))
{
cout << about << endl;
parser.printMessage();
return 0;
}

Size patternSize(parser.get<int>("width"), parser.get<int>("height"));
float squareSize = (float) parser.get<double>("square_size");
poseEstimationFromCoplanarPoints(parser.get<string>("image"),
parser.get<string>("intrinsics"),
patternSize, squareSize);

return 0;
}
#else
int main()
{
std::cerr << "FATAL ERROR: This sample requires opencv_aruco module (from opencv_contrib)" << std::endl;
return 0;
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
#include <iostream>
#include <opencv2/opencv_modules.hpp>
#ifdef HAVE_OPENCV_ARUCO
#include <opencv2/opencv.hpp>
#include <opencv2/aruco.hpp>

using namespace std;
using namespace cv;

namespace
{
enum Pattern { CHESSBOARD, CIRCLES_GRID, ASYMMETRIC_CIRCLES_GRID };

Scalar randomColor( RNG& rng )
{
int icolor = (unsigned int) rng;
return Scalar( icolor & 255, (icolor >> 8) & 255, (icolor >> 16) & 255 );
}

void calcChessboardCorners(Size boardSize, float squareSize, vector<Point3f>& corners, Pattern patternType = CHESSBOARD)
{
corners.resize(0);

switch (patternType)
{
case CHESSBOARD:
case CIRCLES_GRID:
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float(j*squareSize),
float(i*squareSize), 0));
break;

case ASYMMETRIC_CIRCLES_GRID:
for( int i = 0; i < boardSize.height; i++ )
for( int j = 0; j < boardSize.width; j++ )
corners.push_back(Point3f(float((2*j + i % 2)*squareSize),
float(i*squareSize), 0));
break;

default:
CV_Error(Error::StsBadArg, "Unknown pattern type\n");
}
}

void perspectiveCorrection(const string &img1Path, const string &img2Path, const Size &patternSize, RNG &rng)
{
Mat img1 = imread(img1Path);
Mat img2 = imread(img2Path);

//! [find-corners]
vector<Point2f> corners1, corners2;
bool found1 = findChessboardCorners(img1, patternSize, corners1);
bool found2 = findChessboardCorners(img2, patternSize, corners2);
//! [find-corners]

if (!found1 || !found2)
{
cout << "Error, cannot find the chessboard corners in both images." << endl;
return;
}

//! [estimate-homography]
Mat H = findHomography(corners1, corners2);
cout << "H:\n" << H << endl;
//! [estimate-homography]

//! [warp-chessboard]
Mat img1_warp;
warpPerspective(img1, img1_warp, H, img1.size());
//! [warp-chessboard]

Mat img_draw_warp;
hconcat(img2, img1_warp, img_draw_warp);
imshow("Desired chessboard view / Warped source chessboard view", img_draw_warp);

//! [compute-transformed-corners]
Mat img_draw_matches;
hconcat(img1, img2, img_draw_matches);
for (size_t i = 0; i < corners1.size(); i++)
{
Mat pt1 = (Mat_<double>(3,1) << corners1[i].x, corners1[i].y, 1);
Mat pt2 = H * pt1;
pt2 /= pt2.at<double>(2);

Point end( (int) (img1.cols + pt2.at<double>(0)), (int) pt2.at<double>(1) );
line(img_draw_matches, corners1[i], end, randomColor(rng), 2);
}

imshow("Draw matches", img_draw_matches);
waitKey();
//! [compute-transformed-corners]
}

const char* about = "Code for homography tutorial.\n"
"Example 2: perspective correction.\n";

const char* params
= "{ h help | false | print usage }"
"{ image1 | | path to the source chessboard image (left02.jpg) }"
"{ image2 | | path to the desired chessboard image (left01.jpg) }"
"{ width w | 9 | chessboard width }"
"{ height h | 6 | chessboard height }";
}

int main(int argc, char *argv[])
{
cv::RNG rng( 0xFFFFFFFF );
CommandLineParser parser(argc, argv, params);

if (parser.get<bool>("help"))
{
cout << about << endl;
parser.printMessage();
return 0;
}

Size patternSize(parser.get<int>("width"), parser.get<int>("height"));
perspectiveCorrection(parser.get<string>("image1"),
parser.get<string>("image2"),
patternSize, rng);

return 0;
}
#else
int main()
{
std::cerr << "FATAL ERROR: This sample requires opencv_aruco module (from opencv_contrib)" << std::endl;
return 0;
}
#endif
Loading

0 comments on commit b417fb0

Please sign in to comment.