|
|
|
@ -1,5 +1,6 @@ |
|
|
|
|
#include <string> |
|
|
|
|
#include <iostream> |
|
|
|
|
#include <fstream> |
|
|
|
|
#include <sstream> |
|
|
|
|
#include <stdexcept> |
|
|
|
|
#include "opencv2/core/core.hpp" |
|
|
|
@ -12,14 +13,54 @@ using namespace std; |
|
|
|
|
using namespace cv; |
|
|
|
|
using namespace cv::videostab; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ptr<IFrameSource> stabilizedFrames; |
|
|
|
|
string saveMotionsPath; |
|
|
|
|
double outputFps; |
|
|
|
|
string outputPath; |
|
|
|
|
bool quietMode; |
|
|
|
|
|
|
|
|
|
void run(); |
|
|
|
|
void saveMotionsIfNecessary(); |
|
|
|
|
void printHelp(); |
|
|
|
|
|
|
|
|
|
class GlobalMotionReader : public IGlobalMotionEstimator |
|
|
|
|
{ |
|
|
|
|
public: |
|
|
|
|
GlobalMotionReader(string path) |
|
|
|
|
{ |
|
|
|
|
ifstream f(path.c_str()); |
|
|
|
|
if (!f.is_open()) |
|
|
|
|
throw runtime_error("can't open motions file: " + path); |
|
|
|
|
int size; f >> size; |
|
|
|
|
motions_.resize(size); |
|
|
|
|
for (int i = 0; i < size; ++i) |
|
|
|
|
{ |
|
|
|
|
Mat_<float> M(3, 3); |
|
|
|
|
for (int l = 0; l < 3; ++l) |
|
|
|
|
for (int s = 0; s < 3; ++s) |
|
|
|
|
f >> M(l,s); |
|
|
|
|
motions_[i] = M; |
|
|
|
|
} |
|
|
|
|
pos_ = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
virtual Mat estimate(const Mat &/*frame0*/, const Mat &/*frame1*/) |
|
|
|
|
{ |
|
|
|
|
if (pos_ >= motions_.size()) |
|
|
|
|
{ |
|
|
|
|
stringstream text; |
|
|
|
|
text << "can't load motion between frames " << pos_ << " and " << pos_+1; |
|
|
|
|
throw runtime_error(text.str()); |
|
|
|
|
} |
|
|
|
|
return motions_[pos_++]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private: |
|
|
|
|
vector<Mat> motions_; |
|
|
|
|
size_t pos_; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
void run() |
|
|
|
|
{ |
|
|
|
|
VideoWriter writer; |
|
|
|
@ -27,6 +68,8 @@ void run() |
|
|
|
|
|
|
|
|
|
while (!(stabilizedFrame = stabilizedFrames->nextFrame()).empty()) |
|
|
|
|
{ |
|
|
|
|
if (!saveMotionsPath.empty()) |
|
|
|
|
saveMotionsIfNecessary(); |
|
|
|
|
if (!outputPath.empty()) |
|
|
|
|
{ |
|
|
|
|
if (!writer.isOpened()) |
|
|
|
@ -46,6 +89,33 @@ void run() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void saveMotionsIfNecessary() |
|
|
|
|
{ |
|
|
|
|
static bool areMotionsSaved = false; |
|
|
|
|
if (!areMotionsSaved) |
|
|
|
|
{ |
|
|
|
|
IFrameSource *frameSource = static_cast<IFrameSource*>(stabilizedFrames); |
|
|
|
|
TwoPassStabilizer *twoPassStabilizer = dynamic_cast<TwoPassStabilizer*>(frameSource); |
|
|
|
|
if (twoPassStabilizer) |
|
|
|
|
{ |
|
|
|
|
ofstream f(saveMotionsPath.c_str()); |
|
|
|
|
const vector<Mat> &motions = twoPassStabilizer->motions(); |
|
|
|
|
f << motions.size() << endl; |
|
|
|
|
for (size_t i = 0; i < motions.size(); ++i) |
|
|
|
|
{ |
|
|
|
|
Mat_<float> M = motions[i]; |
|
|
|
|
for (int l = 0, k = 0; l < 3; ++l) |
|
|
|
|
for (int s = 0; s < 3; ++s, ++k) |
|
|
|
|
f << M(l,s) << " "; |
|
|
|
|
f << endl; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
areMotionsSaved = true; |
|
|
|
|
cout << "motions are saved"; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void printHelp() |
|
|
|
|
{ |
|
|
|
|
cout << "OpenCV video stabilizer.\n" |
|
|
|
@ -58,6 +128,10 @@ void printHelp() |
|
|
|
|
" --min-inlier-ratio=<float_number>\n" |
|
|
|
|
" Minimum inlier ratio to decide if estimated motion is OK. The default is 0.1,\n" |
|
|
|
|
" but you may want to increase it.\n\n" |
|
|
|
|
" --save-motions=<file_path>\n" |
|
|
|
|
" Save estimated motions into file.\n" |
|
|
|
|
" --load-motions=<file_path>\n" |
|
|
|
|
" Load motions from file.\n\n" |
|
|
|
|
" -r, --radius=<int_number>\n" |
|
|
|
|
" Set smoothing radius. The default is 15.\n" |
|
|
|
|
" --stdev=<float_number>\n" |
|
|
|
@ -108,6 +182,8 @@ int main(int argc, const char **argv) |
|
|
|
|
"{ m | model | | }" |
|
|
|
|
"{ | min-inlier-ratio | | }" |
|
|
|
|
"{ | outlier-ratio | | }" |
|
|
|
|
"{ | save-motions | | }" |
|
|
|
|
"{ | load-motions | | }" |
|
|
|
|
"{ r | radius | | }" |
|
|
|
|
"{ | stdev | | }" |
|
|
|
|
"{ | deblur | | }" |
|
|
|
@ -145,7 +221,12 @@ int main(int argc, const char **argv) |
|
|
|
|
motionFilter->setStdev(cmd.get<float>("stdev")); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
bool isTwoPass = cmd.get<string>("est-trim") == "yes"; |
|
|
|
|
if (!cmd.get<string>("save-motions").empty()) |
|
|
|
|
saveMotionsPath = cmd.get<string>("save-motions"); |
|
|
|
|
|
|
|
|
|
bool isTwoPass = |
|
|
|
|
cmd.get<string>("est-trim") == "yes" || |
|
|
|
|
!cmd.get<string>("save-motions").empty(); |
|
|
|
|
|
|
|
|
|
if (isTwoPass) |
|
|
|
|
{ |
|
|
|
@ -181,19 +262,18 @@ int main(int argc, const char **argv) |
|
|
|
|
else if (cmd.get<string>("model") == "affine") |
|
|
|
|
motionEstimator->setMotionModel(AFFINE); |
|
|
|
|
else if (!cmd.get<string>("model").empty()) |
|
|
|
|
throw runtime_error("unknow motion mode: " + cmd.get<string>("model"));
|
|
|
|
|
|
|
|
|
|
throw runtime_error("unknow motion mode: " + cmd.get<string>("model")); |
|
|
|
|
if (!cmd.get<string>("outlier-ratio").empty()) |
|
|
|
|
{ |
|
|
|
|
RansacParams ransacParams = motionEstimator->ransacParams(); |
|
|
|
|
ransacParams.eps = cmd.get<float>("outlier-ratio"); |
|
|
|
|
motionEstimator->setRansacParams(ransacParams); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (!cmd.get<string>("min-inlier-ratio").empty()) |
|
|
|
|
motionEstimator->setMinInlierRatio(cmd.get<float>("min-inlier-ratio")); |
|
|
|
|
|
|
|
|
|
stabilizer->setMotionEstimator(motionEstimator); |
|
|
|
|
if (!cmd.get<string>("load-motions").empty()) |
|
|
|
|
stabilizer->setMotionEstimator(new GlobalMotionReader(cmd.get<string>("load-motions"))); |
|
|
|
|
|
|
|
|
|
if (!cmd.get<string>("radius").empty()) |
|
|
|
|
stabilizer->setRadius(cmd.get<int>("radius")); |
|
|
|
|