OpenCV 4.12.0
開源計算機視覺
載入中...
搜尋中...
無匹配項
samples/cpp/image_alignment.cpp

一個使用影像對齊ECC演算法的示例

/*
* 本示例演示了函式的使用
* findTransformECC,它實現了影像對齊ECC演算法
*
*
* 此演示載入一張影像(預設為fruits.jpg),並根據給定的運動型別人工建立
* 一個模板影像。當給出兩張影像時,
* 第一張影像是輸入影像,第二張定義了模板影像。
* 在後一種情況下,您還可以解析變換的初始化。
*
* 輸入和輸出變換檔案包含原始變換(轉換)元素
*
* 作者:G. Evangelidis, INRIA, 格勒諾布林, 法國
* M. Asbach, Fraunhofer IAIS, 聖奧古斯丁, 德國
*/
#include <stdio.h>
#include <string>
#include <time.h>
#include <iostream>
#include <fstream>
using namespace cv;
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 | 輸入影像檔名 }"
"{@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){
// 從檔案中讀取特定數量的原始值
// 單應性為9個值,否則為6個
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;
Mat H = Mat (3, 1, CV_32F);
Mat U = Mat (3, 1, CV_32F);
Mat warp_mat = Mat::eye (3, 3, CV_32F);
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_left, top_right, Scalar(255));
line(image, top_right, bottom_right, Scalar(255));
line(image, bottom_right, bottom_left, Scalar(255));
line(image, bottom_left, top_left, Scalar(255));
}
int main (const int argc, const char * argv[])
{
CommandLineParser parser(argc, argv, keys);
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")
mode_temp = MOTION_TRANSLATION;
else if (warpType == "euclidean")
mode_temp = MOTION_EUCLIDEAN;
else if (warpType == "affine")
mode_temp = MOTION_AFFINE;
else
mode_temp = MOTION_HOMOGRAPHY;
Mat inputImage = imread(samples::findFile(imgFile), IMREAD_GRAYSCALE);
if (inputImage.empty())
{
cerr << "無法載入輸入影像" << endl;
return -1;
}
Mat target_image;
Mat template_image;
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);
Mat warpGround;
RNG rng(getTickCount());
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);
warpPerspective(target_image, template_image, warpGround,
Size(200,200), INTER_LINEAR + WARP_INVERSE_MAP);
break;
}
}
const int warp_mode = mode_temp;
// 初始化或載入變換矩陣
Mat warp_matrix;
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)
warp_matrix.rows = 2;
// 開始計時
const double tic_init = (double) getTickCount ();
double cc = findTransformECC (template_image, target_image, warp_matrix, warp_mode,
TermCriteria (TermCriteria::COUNT+TermCriteria::EPS,
number_of_iterations, termination_eps));
if (cc == -1)
{
cerr << "執行被中斷。相關值將最小化。" << endl;
cerr << "檢查變換初始化和/或影像尺寸。" << endl << flush;
}
// 結束計時
const double toc_final = (double) getTickCount ();
const double total_time = (toc_final-tic_init)/(getTickFrequency());
if (verbose){
cout << "對齊時間 (" << warpType << " 變換): "
<< total_time << " 秒" << endl << flush;
// cout << "最終相關性: " << cc << endl << flush;
}
// 儲存最終變換矩陣
saveWarp(finalWarp, warp_matrix, warp_mode);
if (verbose){
cout << "\n最終變換已儲存到檔案: " << finalWarp << endl << flush;
}
// 儲存最終變形影像
Mat warped_image = Mat(template_image.rows, template_image.cols, CV_32FC1);
if (warp_mode != MOTION_HOMOGRAPHY)
warpAffine (target_image, warped_image, warp_matrix, warped_image.size(),
INTER_LINEAR + WARP_INVERSE_MAP);
else
warpPerspective (target_image, warped_image, warp_matrix, warped_image.size(),
INTER_LINEAR + WARP_INVERSE_MAP);
// 儲存變形影像
imwrite(warpedImFile, warped_image);
// 顯示結果影像
if (verbose)
{
cout << "變形影像已儲存到檔案: " << warpedImFile << endl << flush;
namedWindow ("影像", WINDOW_AUTOSIZE);
namedWindow ("模板", WINDOW_AUTOSIZE);
namedWindow ("變形影像", WINDOW_AUTOSIZE);
namedWindow ("誤差 (黑色: 無誤差)", WINDOW_AUTOSIZE);
moveWindow ("影像", 20, 300);
moveWindow ("模板", 300, 300);
moveWindow ("變形影像", 600, 300);
moveWindow ("誤差 (黑色: 無誤差)", 900, 300);
// 繪製對應區域的邊界
Mat identity_matrix = Mat::eye(3,3,CV_32F);
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);
Mat errorImage;
subtract(template_image, warped_image, errorImage);
double max_of_error;
minMaxLoc(errorImage, NULL, &max_of_error);
// 顯示影像
cout << "按任意鍵退出演示(您可能需要先點選影像)。" << endl << flush;
imshow ("影像", target_image);
waitKey (200);
imshow ("模板", template_image);
waitKey (200);
imshow ("變形影像", warped_image);
waitKey(200);
imshow ("誤差 (黑色: 無誤差)", abs(errorImage)*255/max_of_error);
waitKey(0);
}
// 完成
return 0;
}
如果陣列沒有元素,則返回 true。
int64_t int64
從 Mat 派生的模板矩陣類。
定義 mat.hpp:2257
n 維密集陣列類
定義 mat.hpp:830
MatSize size
定義 mat.hpp:2187
void copyTo(OutputArray m) const
將矩陣複製到另一個矩陣。
uchar * ptr(int i0=0)
返回指向指定矩陣行的指標。
_Tp & at(int i0=0)
返回指定陣列元素的引用。
int cols
定義 mat.hpp:2165
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
ximgproc.hpp
返回時鐘週期數。
#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 > &params=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)
繪製連線兩點的線段。
void warpAffine(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar &borderValue=Scalar())
對影像應用仿射變換。
void resize(InputArray src, OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR)
調整影像大小。
void warpPerspective(InputArray src, OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar &borderValue=Scalar())
對影像應用透視變換。
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
定義 core.hpp:107
STL 名稱空間。