#include <algorithm>
#include <string>
#include <vector>
#include "libmv/base/scoped_ptr.h"
#include "libmv/correspondence/feature.h"
#include "libmv/correspondence/import_matches_txt.h"
#include "libmv/correspondence/matches.h"
#include "libmv/correspondence/tracker.h"
#include "libmv/image/image.h"
#include "libmv/image/image_drawing.h"
#include "libmv/image/image_io.h"
#include "libmv/image/image_sequence_io.h"
#include "libmv/image/image_transform_linear.h"
#include "libmv/image/cached_image_sequence.h"
#include "libmv/image/sample.h"
#include "libmv/multiview/robust_affine.h"
#include "libmv/multiview/robust_euclidean.h"
#include "libmv/multiview/robust_homography.h"
#include "libmv/multiview/robust_similarity.h"
#include "libmv/logging/logging.h"
enum eGEOMETRIC_TRANSFORMATION {
EUCLIDEAN = 0, SIMILARITY , AFFINE, HOMOGRAPHY, };
DEFINE_string(m, "matches.txt", "Matches input file");
DEFINE_int32 (transformation, SIMILARITY, "Transformation type:\n\t 0: \
Euclidean\n\t 1:Similarity\n\t 2:Affinity\n\t 3:Homography");
DEFINE_bool(draw_lines, false, "Draw image bounds");
DEFINE_string(of, "./", "Output folder.");
DEFINE_string(os, "_stab", "Output file suffix.");
using namespace libmv;
std::string ReplaceFolder(const std::string &s,
const std::string &new_folder) {
std::string so = s;
std::string nf = new_folder;
if (new_folder == "")
return so;
#ifdef WIN32
size_t n = so.rfind("\\");
if (n == std::string::npos)
n = so.rfind("/");
if (nf.rfind("\\") != nf.size()-1)
nf.append("\\");
#else
size_t n = so.rfind("/");
if (nf.rfind("/") != nf.size()-1)
nf.append("/");
#endif
if (n != std::string::npos) {
so.replace(0, n+1, nf);
} else {
so = nf;
so.append(s);
}
return so;
}
void ComputeRelativeEuclideanMatrices(const Matches &matches,
vector<Mat3> *Es,
double outliers_prob = 1e-2,
double max_error_2d = 1) {
Es->reserve(matches.NumImages() - 1);
Mat3 E;
vector<Mat> xs2;
std::set<Matches::ImageID>::const_iterator image_iter =
matches.get_images().begin();
std::set<Matches::ImageID>::const_iterator prev_image_iter = image_iter;
image_iter++;
for (;image_iter != matches.get_images().end(); ++image_iter) {
TwoViewPointMatchMatrices(matches, *prev_image_iter, *image_iter, &xs2);
if (xs2[0].cols() >= 2) {
Euclidean2DFromCorrespondences2PointRobust(xs2[0], xs2[1],
max_error_2d ,
&E, NULL,
outliers_prob);
Es->push_back(E);
VLOG(2) << "E = " << std::endl << E << std::endl;
} ++prev_image_iter;
}
}
void ComputeRelativeSimilarityMatrices(const Matches &matches,
vector<Mat3> *Ss,
double outliers_prob = 1e-2,
double max_error_2d = 1) {
Ss->reserve(matches.NumImages() - 1);
Mat3 S;
vector<Mat> xs2;
std::set<Matches::ImageID>::const_iterator image_iter =
matches.get_images().begin();
std::set<Matches::ImageID>::const_iterator prev_image_iter = image_iter;
image_iter++;
for (;image_iter != matches.get_images().end(); ++image_iter) {
TwoViewPointMatchMatrices(matches, *prev_image_iter, *image_iter, &xs2);
if (xs2[0].cols() >= 2) {
Similarity2DFromCorrespondences2PointRobust(xs2[0], xs2[1],
max_error_2d ,
&S, NULL,
outliers_prob);
Ss->push_back(S);
VLOG(2) << "S = " << std::endl << S << std::endl;
} ++prev_image_iter;
}
}
void ComputeRelativeAffineMatrices(const Matches &matches,
vector<Mat3> *As,
double outliers_prob = 1e-2,
double max_error_2d = 1) {
As->reserve(matches.NumImages() - 1);
Mat3 A;
vector<Mat> xs2;
std::set<Matches::ImageID>::const_iterator image_iter =
matches.get_images().begin();
std::set<Matches::ImageID>::const_iterator prev_image_iter = image_iter;
image_iter++;
for (;image_iter != matches.get_images().end(); ++image_iter) {
TwoViewPointMatchMatrices(matches, *prev_image_iter, *image_iter, &xs2);
if (xs2[0].cols() >= 3) {
Affine2DFromCorrespondences3PointRobust(xs2[0], xs2[1],
max_error_2d ,
&A, NULL,
outliers_prob);
As->push_back(A);
VLOG(2) << "A = " << std::endl << A << std::endl;
} ++prev_image_iter;
}
}
void ComputeRelativeHomographyMatrices(const Matches &matches,
vector<Mat3> *Hs,
double outliers_prob = 1e-2,
double max_error_2d = 1) {
Hs->reserve(matches.NumImages() - 1);
Mat3 H;
vector<Mat> xs2;
std::set<Matches::ImageID>::const_iterator image_iter =
matches.get_images().begin();
std::set<Matches::ImageID>::const_iterator prev_image_iter = image_iter;
image_iter++;
for (;image_iter != matches.get_images().end(); ++image_iter) {
TwoViewPointMatchMatrices(matches, *prev_image_iter, *image_iter, &xs2);
if (xs2[0].cols() >= 4) {
Homography2DFromCorrespondences4PointRobust(xs2[0], xs2[1],
max_error_2d,
&H, NULL,
outliers_prob);
Hs->push_back(H);
VLOG(2) << "H = " << std::endl << H << std::endl;
} ++prev_image_iter;
}
}
void Stabilize(const std::vector<std::string> &image_files,
const vector<Mat3> &Hs,
bool draw_lines) {
assert(image_files.size() == Hs.size() - 1);
Vec2u images_size;
ByteImage imageArrayBytes;
ReadImage (image_files[0].c_str(), &imageArrayBytes);
images_size << imageArrayBytes.Width(), imageArrayBytes.Height();
Mat3 H;
H.setIdentity();
FloatImage image_stab(imageArrayBytes.Height(),
imageArrayBytes.Width(),
imageArrayBytes.Depth());
image_stab.Fill(0);
float lines_color[3] = {1, 1, 1};
FloatImage *image = NULL;
ImageCache cache;
scoped_ptr<ImageSequence> source(ImageSequenceFromFiles(image_files, &cache));
for (size_t i = 0; i < image_files.size(); ++i) {
if (i > 0)
H = Hs[i - 1].inverse() * H;
image = source->GetFloatImage(i);
if (image) {
if (draw_lines) {
DrawLine<FloatImage, float[3]>(0, 0, 0, images_size(1) - 1,
lines_color, image);
DrawLine<FloatImage, float[3]>(0, 0, images_size(0) - 1, 0,
lines_color, image);
DrawLine<FloatImage, float[3]>( 0, images_size(1)-1,
images_size(0)-1, images_size(1)-1,
lines_color, image);
DrawLine<FloatImage, float[3]>(images_size(0)-1, 0,
images_size(0)-1, images_size(1)-1,
lines_color, image);
}
WarpImage(*image, H, &image_stab);
std::stringstream s;
s << ReplaceFolder(image_files[i].substr(0, image_files[i].rfind(".")),
FLAGS_of);
s << FLAGS_os;
s << image_files[i].substr(image_files[i].rfind("."),
image_files[i].size());
WriteImage(image_stab, s.str().c_str());
}
source->Unpin(i);
}
}
int main(int argc, char **argv) {
std::string usage ="Stabilize a video.\n";
usage += "Usage: " + std::string(argv[0]) + " IMAGE1 [IMAGE2 ... IMAGEN] ";
usage += "-m MATCHES.txt [-of OUT_FOLDER] [-os OUT_FILE_SUFFIX]";
usage += "\t - IMAGEX is an input image {PNG, PNM, JPEG}\n";
gflags::SetUsageMessage(usage);
gflags::ParseCommandLineFlags(&argc, &argv, true);
std::vector<std::string> files;
for (int i = 1; i < argc; ++i) {
files.push_back(argv[i]);
}
std::sort(files.begin(), files.end());
tracker::FeaturesGraph fg;
FeatureSet *fs = fg.CreateNewFeatureSet();
VLOG(0) << "Loading Matches file..." << std::endl;
ImportMatchesFromTxt(FLAGS_m, &fg.matches_, fs);
VLOG(0) << "Loading Matches file...[DONE]." << std::endl;
vector<Mat3> Hs;
VLOG(0) << "Estimating relative matrices..." << std::endl;
switch (FLAGS_transformation) {
case EUCLIDEAN:
ComputeRelativeEuclideanMatrices(fg.matches_, &Hs);
break;
case SIMILARITY:
ComputeRelativeSimilarityMatrices(fg.matches_, &Hs);
break;
case AFFINE:
ComputeRelativeAffineMatrices(fg.matches_, &Hs);
break;
case HOMOGRAPHY:
ComputeRelativeHomographyMatrices(fg.matches_, &Hs);
break;
}
VLOG(0) << "Estimating relative matrices...[DONE]." << std::endl;
VLOG(0) << "Stabilizing images..." << std::endl;
Stabilize(files, Hs, FLAGS_draw_lines);
VLOG(0) << "Stabilizing images...[DONE]." << std::endl;
fg.DeleteAndClear();
return 0;
}