#include <stdio.h>
#include <string>
#include <time.h>
#include <iostream>
#include <fstream>
using namespace std;
static void help(const char** argv);
static int readWarp(
string iFilename,
Mat& warp,
int motionType);
static int saveWarp(
string fileName,
const Mat& warp,
int motionType);
static void draw_warped_roi(
Mat& image,
const int width,
const int height,
Mat& W);
#define HOMO_VECTOR(H, x, y)\
H.at<float>(0,0) = (float)(x);\
H.at<float>(1,0) = (float)(y);\
H.at<float>(2,0) = 1.;
#define GET_HOMO_VALUES(X, x, y)\
(x) = static_cast<float> (X.at<float>(0,0)/X.at<float>(2,0));\
(y) = static_cast<float> (X.at<float>(1,0)/X.at<float>(2,0));
const std::string keys =
"{@inputImage | fruits.jpg | input image filename }"
"{@templateImage | | template image filename (optional)}"
"{@inputWarp | | input warp (matrix) filename (optional)}"
"{n numOfIter | 50 | ECC's iterations }"
"{e epsilon | 0.0001 | ECC's convergence epsilon }"
"{o outputWarp | outWarp.ecc | output warp (matrix) filename }"
"{m motionType | affine | type of motion (translation, euclidean, affine, homography) }"
"{v verbose | 1 | display initial and final images }"
"{w warpedImfile | warpedECC.png | warped input image }"
"{h help | | print help message }"
;
static void help(const char** argv)
{
cout << "\nThis file demonstrates the use of the ECC image alignment algorithm. When one image"
" is given, the template image is artificially formed by a random warp. When both images"
" are given, the initialization of the warp by command line parsing is possible. "
"If inputWarp is missing, the identity transformation initializes the algorithm. \n" << endl;
cout << "\nUsage example (one image): \n"
<< argv[0]
<< " fruits.jpg -o=outWarp.ecc "
"-m=euclidean -e=1e-6 -N=70 -v=1 \n" << endl;
cout << "\nUsage example (two images with initialization): \n"
<< argv[0]
<< " yourInput.png yourTemplate.png "
"yourInitialWarp.ecc -o=outWarp.ecc -m=homography -e=1e-6 -N=70 -v=1 -w=yourFinalImage.png \n" << endl;
}
static int readWarp(
string iFilename,
Mat& warp,
int motionType){
int numOfElements;
if (motionType==MOTION_HOMOGRAPHY)
numOfElements=9;
else
numOfElements=6;
int i;
int ret_value;
ifstream myfile(iFilename.c_str());
if (myfile.is_open()){
float* matPtr = warp.
ptr<
float>(0);
for(i=0; i<numOfElements; i++){
myfile >> matPtr[i];
}
ret_value = 1;
}
else {
cout << "Unable to open file " << iFilename.c_str() << endl;
ret_value = 0;
}
return ret_value;
}
static int saveWarp(
string fileName,
const Mat& warp,
int motionType)
{
const float* matPtr = warp.
ptr<
float>(0);
int ret_value;
ofstream outfile(fileName.c_str());
if( !outfile ) {
cerr << "error in saving "
<< "Couldn't open file '" << fileName.c_str() << "'!" << endl;
ret_value = 0;
}
else {
outfile << matPtr[0] << " " << matPtr[1] << " " << matPtr[2] << endl;
outfile << matPtr[3] << " " << matPtr[4] << " " << matPtr[5] << endl;
if (motionType==MOTION_HOMOGRAPHY){
outfile << matPtr[6] << " " << matPtr[7] << " " << matPtr[8] << endl;
}
ret_value = 1;
}
return ret_value;
}
static void draw_warped_roi(
Mat& image,
const int width,
const int height,
Mat& W)
{
Point2f top_left, top_right, bottom_left, bottom_right;
for (
int y = 0; y < W.
rows; y++)
for (
int x = 0; x < W.
cols; x++)
warp_mat.
at<
float>(y,x) = W.
at<
float>(y,x);
HOMO_VECTOR(H, 1, 1);
gemm(warp_mat, H, 1, 0, 0, U);
GET_HOMO_VALUES(U, top_left.
x, top_left.
y);
HOMO_VECTOR(H, width, 1);
gemm(warp_mat, H, 1, 0, 0, U);
GET_HOMO_VALUES(U, top_right.
x, top_right.
y);
HOMO_VECTOR(H, 1, height);
gemm(warp_mat, H, 1, 0, 0, U);
GET_HOMO_VALUES(U, bottom_left.
x, bottom_left.
y);
HOMO_VECTOR(H, width, height);
gemm(warp_mat, H, 1, 0, 0, U);
GET_HOMO_VALUES(U, bottom_right.
x, bottom_right.
y);
line(image, top_right, bottom_right,
Scalar(255));
line(image, bottom_right, bottom_left,
Scalar(255));
}
int main (
const int argc,
const char * argv[])
{
parser.about("ECC demo");
parser.printMessage();
help(argv);
string imgFile = parser.get<string>(0);
string tempImgFile = parser.get<string>(1);
string inWarpFile = parser.get<string>(2);
int number_of_iterations = parser.get<int>("n");
double termination_eps = parser.get<double>("e");
string warpType = parser.get<string>("m");
int verbose = parser.get<int>("v");
string finalWarp = parser.get<string>("o");
string warpedImFile = parser.get<string>("w");
if (!parser.check())
{
parser.printErrors();
return -1;
}
if (!(warpType == "translation" || warpType == "euclidean"
|| warpType == "affine" || warpType == "homography"))
{
cerr << "Invalid motion transformation" << endl;
return -1;
}
int mode_temp;
if (warpType == "translation")
else if (warpType == "euclidean")
else if (warpType == "affine")
else
Mat inputImage =
imread(samples::findFile(imgFile), IMREAD_GRAYSCALE);
{
cerr << "Unable to load the inputImage" << endl;
return -1;
}
if (tempImgFile!="") {
inputImage.
copyTo(target_image);
template_image =
imread(samples::findFile(tempImgFile), IMREAD_GRAYSCALE);
if (template_image.
empty()){
cerr << "Unable to load the template image" << endl;
return -1;
}
}
else{
resize(inputImage, target_image,
Size(216, 216), 0, 0, INTER_LINEAR_EXACT);
double angle;
switch (mode_temp) {
warpGround = (
Mat_<float>(2,3) << 1, 0, (rng.uniform(10.f, 20.f)),
0, 1, (rng.uniform(10.f, 20.f)));
warpAffine(target_image, template_image, warpGround,
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
break;
angle =
CV_PI/30 +
CV_PI*rng.uniform((
double)-2.f, (
double)2.f)/180;
warpGround = (
Mat_<float>(2,3) <<
cos(angle), -
sin(angle), (rng.uniform(10.f, 20.f)),
sin(angle),
cos(angle), (rng.uniform(10.f, 20.f)));
warpAffine(target_image, template_image, warpGround,
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
break;
warpGround = (
Mat_<float>(2,3) << (1-rng.uniform(-0.05f, 0.05f)),
(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),
(rng.uniform(10.f, 20.f)));
warpAffine(target_image, template_image, warpGround,
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
break;
warpGround = (
Mat_<float>(3,3) << (1-rng.uniform(-0.05f, 0.05f)),
(rng.uniform(-0.03f, 0.03f)), (rng.uniform(10.f, 20.f)),
(rng.uniform(-0.03f, 0.03f)), (1-rng.uniform(-0.05f, 0.05f)),(rng.uniform(10.f, 20.f)),
(rng.uniform(0.0001f, 0.0003f)), (rng.uniform(0.0001f, 0.0003f)), 1.f);
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
break;
}
}
const int warp_mode = mode_temp;
if (warpType == "homography")
warp_matrix = Mat::eye(3, 3,
CV_32F);
else
warp_matrix = Mat::eye(2, 3,
CV_32F);
if (inWarpFile!=""){
int readflag = readWarp(inWarpFile, warp_matrix, warp_mode);
if ((!readflag) || warp_matrix.
empty())
{
cerr << "-> Check warp initialization file" << endl << flush;
return -1;
}
}
else {
printf("\n ->Performance Warning: Identity warp ideally assumes images of "
"similar size. If the deformation is strong, the identity warp may not "
"be a good initialization. \n");
}
if (number_of_iterations > 200)
cout << "-> Warning: too many iterations " << endl;
if (warp_mode != MOTION_HOMOGRAPHY)
const double tic_init = (double) getTickCount ();
double cc =
findTransformECC (template_image, target_image, warp_matrix, warp_mode,
number_of_iterations, termination_eps));
if (cc == -1)
{
cerr << "The execution was interrupted. The correlation value is going to be minimized." << endl;
cerr << "Check the warp initialization and/or the size of images." << endl << flush;
}
const double toc_final = (double) getTickCount ();
if (verbose){
cout << "Alignment time (" << warpType << " transformation): "
<< total_time << " sec" << endl << flush;
}
saveWarp(finalWarp, warp_matrix, warp_mode);
if (verbose){
cout << "\nThe final warp has been saved in the file: " << finalWarp << endl << flush;
}
if (warp_mode != MOTION_HOMOGRAPHY)
warpAffine (target_image, warped_image, warp_matrix, warped_image.
size(),
INTER_LINEAR + WARP_INVERSE_MAP);
else
INTER_LINEAR + WARP_INVERSE_MAP);
imwrite(warpedImFile, warped_image);
if (verbose)
{
cout << "The warped image has been saved in the file: " << warpedImFile << endl << flush;
namedWindow (
"error (black: no error)", WINDOW_AUTOSIZE);
draw_warped_roi (target_image, template_image.
cols-2, template_image.
rows-2, warp_matrix);
draw_warped_roi (template_image, template_image.
cols-2, template_image.
rows-2, identity_matrix);
subtract(template_image, warped_image, errorImage);
double max_of_error;
cout << "Press any key to exit the demo (you might need to click on the images before)." << endl << flush;
imshow (
"image", target_image);
imshow (
"template", template_image);
imshow (
"warped image", warped_image);
imshow (
"error (black: no error)",
abs(errorImage)*255/max_of_error);
}
return 0;
}
Designed for command line parsing.
Definition utility.hpp:890
Template matrix class derived from Mat.
Definition mat.hpp:2517
n-dimensional dense array class
Definition mat.hpp:950
MatSize size
Definition mat.hpp:2447
void copyTo(OutputArray m) const
Copies the matrix to another one.
uchar * ptr(int i0=0)
Returns a pointer to the specified matrix row.
_Tp & at(int i0=0)
Returns a reference to the specified array element.
int cols
Definition mat.hpp:2424
bool empty() const
Returns true if the array has no elements.
int rows
the number of rows and columns or (-1, -1) when the matrix has more than 2 dimensions
Definition mat.hpp:2424
int type() const
Returns the type of a matrix element.
_Tp y
y coordinate of the point
Definition types.hpp:202
_Tp x
x coordinate of the point
Definition types.hpp:201
Random Number Generator.
Definition core.hpp:2805
Template class for specifying the size of an image or rectangle.
Definition types.hpp:338
The class defining termination criteria for iterative algorithms.
Definition types.hpp:896
void subtract(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray(), int dtype=-1)
Calculates the per-element difference between two arrays or array and a scalar.
void minMaxLoc(InputArray src, double *minVal, double *maxVal=0, Point *minLoc=0, Point *maxLoc=0, InputArray mask=noArray())
Finds the global minimum and maximum in an array.
void gemm(InputArray src1, InputArray src2, double alpha, InputArray src3, double beta, OutputArray dst, int flags=0)
Performs generalized matrix multiplication.
#define CV_32FC1
Definition interface.h:129
#define CV_32F
Definition interface.h:81
Quat< T > cos(const Quat< T > &q)
Quat< T > sin(const Quat< T > &q)
softfloat abs(softfloat a)
Absolute value.
Definition softfloat.hpp:444
#define CV_PI
Definition cvdef.h:382
double getTickFrequency()
Returns the number of ticks per second.
int64 getTickCount()
Returns the number of ticks.
#define CV_Assert(expr)
Checks a condition at runtime and throws exception if it fails.
Definition exception.hpp:198
void imshow(const String &winname, InputArray mat)
Displays an image in the specified window.
int waitKey(int delay=0)
Waits for a pressed key.
void namedWindow(const String &winname, int flags=WINDOW_AUTOSIZE)
Creates a window.
void moveWindow(const String &winname, int x, int y)
Moves the window to the specified position.
CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > ¶ms=std::vector< int >())
Saves an image to a specified file.
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
Loads an image from a file.
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
Draws a line segment connecting two points.
double findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix, int motionType, TermCriteria criteria, InputArray inputMask, int gaussFiltSize)
Finds the geometric transform (warp) between two images in terms of the ECC criterion .
@ MOTION_TRANSLATION
Definition tracking.hpp:271
@ MOTION_EUCLIDEAN
Definition tracking.hpp:272
@ MOTION_HOMOGRAPHY
Definition tracking.hpp:274
@ MOTION_AFFINE
Definition tracking.hpp:273
int main(int argc, char *argv[])
Definition highgui_qt.cpp:3