#include <stdio.h>
#include <string>
#include <time.h>
#include <iostream>
#include <fstream>
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 | 输入图像文件名 }"
"{@templateImage | | 模板图像文件名 (可选)}"
"{@inputWarp | | 输入变换(矩阵)文件名 (可选)}"
"{n numOfIter | 50 | ECC迭代次数 }"
"{e epsilon | 0.0001 | ECC收敛阈值 }"
"{o outputWarp | outWarp.ecc | 输出变换(矩阵)文件名 }"
"{m motionType | affine | 运动类型 (平移, 欧几里得, 仿射, 单应性) }"
"{v verbose | 1 | 显示初始和最终图像 }"
"{w warpedImfile | warpedECC.png | 变形后的输入图像 }"
"{h help | | 打印帮助信息 }"
;
static void help(const char** argv)
{
cout << "\n此文件演示了ECC图像对齐算法的使用。当只给定一张图像时,"
" 模板图像会通过随机变换人工生成。当给定两张图像时,"
" 可以通过命令行解析来初始化变换。 "
"\n如果缺少inputWarp,算法将使用单位变换进行初始化。 \n" << endl;
cout << "\n使用示例(一张图像): \n"
<< argv[0]
<< " fruits.jpg -o=outWarp.ecc "
"-m=euclidean -e=1e-6 -N=70 -v=1 \n" << endl;
cout << "\n使用示例(两张图像及初始化): \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 << "无法打开文件 " << 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 << "保存时出错 "
<< "无法打开文件 '" << 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 演示");
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 << "无效的运动变换" << 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 << "无法加载输入图像" << endl;
return -1;
}
if (tempImgFile!="") {
inputImage.
copyTo(target_image);
template_image =
imread(samples::findFile(tempImgFile), IMREAD_GRAYSCALE);
if (template_image.
empty()){
cerr << "无法加载模板图像" << 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 << "-> 检查变换初始化文件" << endl << flush;
return -1;
}
}
else {
printf("\n ->性能警告:理想情况下,单位变换假定图像尺寸"
"相似。如果变形强烈,单位变换可能不是"
"一个好的初始化。 \n");
}
if (number_of_iterations > 200)
cout << "-> 警告:迭代次数过多 " << 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 << "执行被中断。相关值将最小化。" << endl;
cerr << "检查变换初始化和/或图像尺寸。" << endl << flush;
}
const double toc_final = (double) getTickCount ();
if (verbose){
cout << "对齐时间 (" << warpType << " 变换): "
<< total_time << " 秒" << endl << flush;
}
saveWarp(finalWarp, warp_matrix, warp_mode);
if (verbose){
cout << "\n最终变换已保存到文件: " << 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 << "变形图像已保存到文件: " << warpedImFile << endl << flush;
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 << "按任意键退出演示(您可能需要先点击图像)。" << endl << flush;
imshow (
"模板", template_image);
imshow (
"变形图像", warped_image);
imshow (
"误差 (黑色: 无误差)", abs(errorImage)*255/max_of_error);
}
return 0;
}
如果数组没有元素,则返回 true。
int64_t int64
从 Mat 派生的模板矩阵类。
定义 mat.hpp:2257
MatSize size
定义 mat.hpp:2187
void copyTo(OutputArray m) const
将矩阵复制到另一个矩阵。
uchar * ptr(int i0=0)
返回指向指定矩阵行的指针。
_Tp & at(int i0=0)
返回指定数组元素的引用。
cv::getTickFrequency
double getTickFrequency()
int rows
行数和列数,或当矩阵维度超过2时为(-1, -1)
定义 mat.hpp:2165
int type() const
返回矩阵元素的类型。
_Tp y
点的 y 坐标
定义 types.hpp:202
_Tp x
点的 x 坐标
定义 types.hpp:201
随机数生成器。
Definition core.hpp:2879
用于指定图像或矩形大小的模板类。
Definition types.hpp:335
定义迭代算法终止条件
定义 types.hpp:893
void subtract(InputArray src1, InputArray src2, OutputArray dst, InputArray mask=noArray(), int dtype=-1)
计算两个数组或数组与一个标量之间的逐元素差值。
void minMaxLoc(InputArray src, double *minVal, double *maxVal=0, Point *minLoc=0, Point *maxLoc=0, InputArray mask=noArray())
查找数组中的全局最小值和最大值。
void gemm(InputArray src1, InputArray src2, double alpha, InputArray src3, double beta, OutputArray dst, int flags=0)
执行广义矩阵乘法。
#define CV_32FC1
定义 interface.h:118
#define CV_32F
Definition interface.h:78
Quat< T > cos(const Quat< T > &q)
Quat< T > sin(const Quat< T > &q)
#define CV_PI
定义 cvdef.h:380
#define CV_Assert(expr)
在运行时检查条件,如果失败则抛出异常。
定义 base.hpp:423
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按键按下。
void namedWindow(const String &winname, int flags=WINDOW_AUTOSIZE)
创建窗口。
void moveWindow(const String &winname, int x, int y)
将窗口移动到指定位置。
CV_EXPORTS_W bool imwrite(const String &filename, InputArray img, const std::vector< int > ¶ms=std::vector< int >())
将图像保存到指定文件。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
从文件加载图像。
void line(InputOutputArray img, Point pt1, Point pt2, const Scalar &color, int thickness=1, int lineType=LINE_8, int shift=0)
绘制连接两点的线段。
double findTransformECC(InputArray templateImage, InputArray inputImage, InputOutputArray warpMatrix, int motionType, TermCriteria criteria, InputArray inputMask, int gaussFiltSize)
根据ECC准则ep08找到两幅图像之间的几何变换(扭曲)。
@ MOTION_TRANSLATION
定义 tracking.hpp:265
@ MOTION_EUCLIDEAN
定义 tracking.hpp:266
@ MOTION_HOMOGRAPHY
定义 tracking.hpp:268
@ MOTION_AFFINE
定义 tracking.hpp:267
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3