#include <algorithm>
#include <iostream>
#include <string>
#include "libmv/base/vector.h"
#include "libmv/base/vector_utils.h"
#include "libmv/base/scoped_ptr.h"
#include "libmv/correspondence/feature.h"
#include "libmv/correspondence/feature_matching.h"
#include "libmv/detector/fast_detector.h"
#include "libmv/detector/surf_detector.h"
#include "libmv/detector/detector.h"
#include "libmv/descriptor/descriptor.h"
#include "libmv/descriptor/simpliest_descriptor.h"
#include "libmv/descriptor/vector_descriptor.h"
#include "libmv/image/image.h"
#include "libmv/image/image_converter.h"
#include "libmv/image/image_drawing.h"
#include "libmv/image/image_io.h"
#include "libmv/image/sample.h"
#include "libmv/multiview/affine_kernel.h"
#include "libmv/multiview/homography_kernel.h"
#include "libmv/multiview/panography_kernel.h"
#include "libmv/multiview/robust_affine.h"
#include "libmv/multiview/robust_homography.h"
#include "libmv/multiview/robust_similarity.h"
#include "libmv/multiview/two_view_kernel.h"
#include "libmv/tools/tool.h"
using namespace libmv;
using namespace std;
enum eGEOMETRIC_CONSTRAINT {
AFFINE = 0, HOMOGRAPHY = 1, MINIMAL_PANORAMIC_CASE = 2 };
void usage() {
LOG(ERROR) << " points_detector ImageNameA ImageNameB ImageNameOut.jpg"
<< std::endl
<< " ImageNameA : the input image that you want stitch to B,"
<< std::endl
<< " ImageNameB : the input image that you want stitch to A,"
<< " ImageNameOut.jpg : the localized keypoints will be displayed on it."
<< std::endl
<< " INFO : 2Point algorithm work with image with a sufficient angle \
between view." << std::endl;
}
void Overlap_ComputeBoundingBox(int w1, int h1, int w2, int h2,
const Mat3 & Homography,
int & tx, int & ty, int & wOut, int & hOut);
int main(int argc, char **argv) {
libmv::Init("Stitch together two images", &argc, &argv);
if (argc != 4 ) {
usage();
LOG(ERROR) << "Missing parameters or errors in the command line.";
return 1;
}
const string sImageA = argv[1];
const string sImageB = argv[2];
const string sImageOut = argv[3];
Array3Du imageA;
if (!ReadImage(sImageA.c_str(), &imageA)) {
LOG(FATAL) << "Failed loading image: " << sImageA;
return 0;
}
Array3Du imageB;
if (!ReadImage(sImageB.c_str(), &imageB)) {
LOG(FATAL) << "Failed loading image: " << sImageB;
return 0;
}
eGEOMETRIC_CONSTRAINT eGeometricConstraint = HOMOGRAPHY;
scoped_ptr<detector::Detector> detector(detector::CreateFastDetector(9, 20));
FeatureSet KeypointImgA;
FeatureSet KeypointImgB;
{
Array3Du imageTemp;
Rgb2Gray( imageA, &imageTemp);
libmv::vector<libmv::Feature *> features;
Image im( new Array3Du(imageTemp) );
detector->Detect( im, &features, NULL);
libmv::vector<descriptor::Descriptor *> descriptors;
scoped_ptr<descriptor::Describer>
describer(descriptor::CreateSimpliestDescriber());
describer->Describe(features, im, NULL, &descriptors);
KeypointImgA.features.resize(descriptors.size());
for(int i = 0;i < descriptors.size(); ++i)
{
KeypointFeature & feat = KeypointImgA.features[i];
feat.descriptor = *(descriptor::VecfDescriptor*)descriptors[i];
*(PointFeature*)(&feat) = *(PointFeature*)features[i];
}
DeleteElements(&features);
DeleteElements(&descriptors);
}
{
Array3Du imageTemp;
Rgb2Gray( imageB, &imageTemp);
libmv::vector<libmv::Feature *> features;
Image im( new Array3Du(imageTemp) );
detector->Detect( im, &features, NULL);
libmv::vector<descriptor::Descriptor *> descriptors;
scoped_ptr<descriptor::Describer>
describer(descriptor::CreateSimpliestDescriber());
describer->Describe(features, im, NULL, &descriptors);
KeypointImgB.features.resize(descriptors.size());
for(int i = 0;i < descriptors.size(); ++i)
{
KeypointFeature & feat = KeypointImgB.features[i];
feat.descriptor = *(descriptor::VecfDescriptor*)descriptors[i];
*(PointFeature*)(&feat) = *(PointFeature*)features[i];
}
DeleteElements(&features);
DeleteElements(&descriptors);
}
Matches matches;
FindCandidateMatches(KeypointImgA,
KeypointImgB,
&matches);
libmv::vector<Mat> x;
libmv::vector<int> tracks;
libmv::vector<int> images;
images.push_back(0);
images.push_back(1);
PointMatchMatrices(matches, images, &tracks, &x);
libmv::vector<int> inliers;
Mat3 H;
switch( eGeometricConstraint )
{
case AFFINE :
Affine2DFromCorrespondences3PointRobust(x[0], x[1], 1, &H, &inliers);
break;
case HOMOGRAPHY :
Homography2DFromCorrespondences4PointRobust(x[0], x[1], 1, &H, &inliers);
break;
case MINIMAL_PANORAMIC_CASE:
Similarity2DFromCorrespondences2PointRobust(x[0], x[1], 1, &H, &inliers);
break;
}
std::cout<< inliers.size() << " inliers" << std::endl;
if (inliers.size() < 6) {
return -1;
}
Matches consistent_matches;
for (int j = 0; j < inliers.size(); ++j) {
int k = inliers[j];
for (int i = 0; i < 2; ++i) {
consistent_matches.Insert(images[i], tracks[k],
matches.Get(images[i], tracks[k]));
}
}
TwoViewPointMatchMatrices(consistent_matches, 0, 1, &x);
libmv::vector<Mat3> Hs;
switch( eGeometricConstraint )
{
case AFFINE :
affine::affine2D::kernel::ThreePointSolver::Solve(x[0], x[1], &Hs);
if (Hs.size() == 0) {
Hs.push_back(H); }
break;
case HOMOGRAPHY :
homography::homography2D::kernel::FourPointSolver::Solve(x[0], x[1], &Hs);
break;
case MINIMAL_PANORAMIC_CASE:
Hs.push_back(H);
std::cout << "Todo : write down focal solving from N view" <<std::endl;
break;
}
if (Hs.size() < 1) {
LOG(ERROR) << " Cannot refine a solution using all the inliers ";
return -1;
}
H = Hs[0];
LOG(INFO) << "H:\n" << H << std::endl;
int tx=0, ty=0, wOut=0, hOut=0;
Overlap_ComputeBoundingBox(imageA.Width(), imageA.Height(),
imageB.Width(), imageB.Height(),
H, tx, ty, wOut, hOut);
Array3Du warpingImage(hOut, wOut,3);
warpingImage.Fill(0);
for(int j=0; j < hOut; ++j)
for(int i=0; i < wOut; ++i)
{
Vec3 Pos;
Pos << i-tx,j-ty,1.0;
const int xPos = Pos(0), yPos = Pos(1);
bool bAContrib = false, bBContrib=false;
if( imageA.Contains( yPos, xPos ) )
bAContrib = true;
Vec3 imagePosB = H*Pos;
imagePosB/=imagePosB(2);
if( imageB.Contains( imagePosB(1), imagePosB(0) ) )
bBContrib = true;
if(bAContrib && bBContrib) {
warpingImage(j,i,0) =
(imageA(yPos,xPos,0) +
SampleLinear( imageB, imagePosB(1),imagePosB(0),0))/2;
warpingImage(j,i,1) =
(imageA(yPos,xPos,1) +
SampleLinear( imageB, imagePosB(1),imagePosB(0),1))/2;
warpingImage(j,i,2) =
(imageA(yPos,xPos,2) +
SampleLinear( imageB, imagePosB(1),imagePosB(0),2))/2;
continue;
}
if(bAContrib && !bBContrib) {
warpingImage(j,i,0) = imageA(yPos,xPos,0);
warpingImage(j,i,1) = imageA(yPos,xPos,1);
warpingImage(j,i,2) = imageA(yPos,xPos,2);
continue;
}
if(!bAContrib && bBContrib) {
warpingImage(j,i,0) = SampleLinear( imageB, imagePosB(1),imagePosB(0),0);
warpingImage(j,i,1) = SampleLinear( imageB, imagePosB(1),imagePosB(0),1);
warpingImage(j,i,2) = SampleLinear( imageB, imagePosB(1),imagePosB(0),2);
continue;
}
}
WriteJpg(warpingImage, string(sImageOut).c_str(), 100);
return 0;
}
void Overlap_ComputeBoundingBox(int w1, int h1, int w2, int h2,
const Mat3 & Homography,
int & tx, int & ty, int & wOut, int & hOut)
{
int minx=0, miny=0, maxx=w1, maxy=h1;
int xCoord[4] = {0, w2, w2, 0 };
int yCoord[4] = {0, 0, h2, h2};
Mat3 Hinv(3,3);
Hinv = Homography.inverse();
for(int i=0; i<4; ++i)
{
Vec3 Pos;
Pos << xCoord[i],yCoord[i],1.0;
Vec3 Pos2 = Hinv * Pos;
Vec3 posImage = Pos2/Pos2(2);
double xT = posImage(0), yT = posImage(1);
if( xT < minx)
minx=floor(xT);
else if( xT > maxx)
maxx=ceil(xT);
if( yT < miny)
miny=floor(yT);
else if( yT > maxy)
maxy=ceil(yT);
}
tx = - minx;
ty = - miny;
wOut = maxx - minx;
hOut = maxy - miny;
}