Hello,
I am using FindTransformECC to align two images. This works perfectly, as long as I set the initial value of the warpMatrix to the identity matrix. However, if it is anything else, If I try to provide some initial rotation or translation to roughly align the images, it throws cv::Error::StsNoConv.
The OpenCV documentation says: "In essence, the function updates the initial transformation that roughly aligns the images. If this information is missing, the identity warp (unity matrix) should be given as input." I would like to provide an this initial guess transformation so I can align images that are more than a few degrees or a few pixels out of alignment.
My code is not currently in a form appropriate for posting. I thought I would throw this out and see if there is an obvious solution, but if necessary, I would be happy to provide a test case.
I am using OpenCV 3.0 Alpha from the binary distribution.
Example code is below:
#include "stdafx.h"
#include <opencv2/video/video.hpp>
#include <ObjIdl.h>
#include <GdiPlus.h>
#include <iostream>
using namespace Gdiplus;
using namespace cv;
using namespace std;
void findTransformEccTest()
{
// Start up GDI
// Initialize GDI+
ULONG_PTR m_gdiplusToken;
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
Gdiplus::GdiplusStartup(&m_gdiplusToken, &gdiplusStartupInput, NULL);
{
// rotation of test image
double angle = 1;
// Rotation used for initial value of warp_matrix.
// If this angle is not zero (which results in an identity matrix) then findTransformECC will not converge!
double angle2 = 1;
// test image dimensions
double sizex = 500;
double sizey = 500;
// test image offset
double xoffset = 0;
double yoffset = 0;
int warp_mode = MOTION_EUCLIDEAN;
double termination_eps = 0.0000001;
double maxiter = 200;
// generate two images which are offset and rotated to test image alignment
SolidBrush brush1(Color(255,255,0,0));
Bitmap bitmap1(sizex, sizey, PixelFormat32bppARGB);
Bitmap bitmap2(sizex, sizey, PixelFormat32bppARGB);
Graphics graphics1(&bitmap1);
graphics1.FillRectangle(&brush1, 0,0, sizex, sizey);
Graphics graphics2(&bitmap2);
graphics2.FillRectangle(&brush1, 0,0, sizex, sizey);
Pen pen1(Color(255,0,0,0), 0.05 * sizex);
pen1.SetStartCap(LineCapRound);
pen1.SetEndCap(LineCapRound);
// draw test pattern
graphics1.DrawLine(&pen1, int(0.2 * sizex), int(0.2 * sizey), int(0.2 * sizex), int(0.7 * sizey));
graphics1.DrawLine(&pen1, int(0.2 * sizex), int(0.7 * sizey), int(0.6 * sizex), int(0.7 * sizey));
graphics1.DrawLine(&pen1, int(0.6 * sizex), int(0.7 * sizey), int(0.2 * sizex), int(0.2 * sizey));
graphics1.DrawLine(&pen1, int(0.8 * sizex), int(0.1 * sizey), int(0.8 * sizex), int(0.8 * sizey));
// Transform second test image
// transform must be applied before the test pattern is drawn.
Matrix transformMatrix;
transformMatrix.RotateAt(angle, PointF(0.5 * sizex, 0.5 * sizey), MatrixOrderAppend);
transformMatrix.Translate(xoffset * sizex / 100, yoffset * sizey / 100, MatrixOrderAppend);
graphics2.MultiplyTransform(&transformMatrix);
// draw test pattern
graphics2.DrawLine(&pen1, int(0.2 * sizex), int(0.2 * sizey), int(0.2 * sizex), int(0.7 * sizey));
graphics2.DrawLine(&pen1, int(0.2 * sizex), int(0.7 * sizey), int(0.6 * sizex), int(0.7 * sizey));
graphics2.DrawLine(&pen1, int(0.6 * sizex), int(0.7 * sizey), int(0.2 * sizex), int(0.2 * sizey));
graphics2.DrawLine(&pen1, int(0.8 * sizex), int(0.1 * sizey), int(0.8 * sizex), int(0.8 * sizey));
// convert Bitmap to Mat
Mat templateMat;
Mat targetMat;
Gdiplus::BitmapData templateData = {};
Mat graymat;
Gdiplus::Rect rect(0,0,bitmap1.GetWidth(), bitmap1.GetHeight());
bitmap1.LockBits(&rect,Gdiplus::ImageLockModeRead, bitmap1.GetPixelFormat(), &templateData);
Mat newmat1(bitmap1.GetHeight(), bitmap1.GetWidth(),CV_8UC4, templateData.Scan0,std::abs(templateData.Stride));
cvtColor(newmat1, graymat, COLOR_RGB2GRAY);
graymat.copyTo(templateMat);
bitmap2.LockBits(&rect,Gdiplus::ImageLockModeRead, bitmap2.GetPixelFormat(), &templateData);
Mat newmat2(bitmap2.GetHeight(), bitmap2.GetWidth(),CV_8UC4, templateData.Scan0,std::abs(templateData.Stride));
cvtColor(newmat2, graymat, COLOR_RGB2GRAY);
graymat.copyTo(targetMat);
// create Gdiplus::Matrix transformation matrix
Matrix t1;
t1.RotateAt(angle2, PointF(0.5 * templateMat.cols, 0.5 * templateMat.rows));
// convert to cv::Mat
Gdiplus::REAL m[3][2];
Gdiplus::REAL * x = (Gdiplus::REAL * )&m;
t1.GetElements(x);
Mat warp_matrix = Mat::eye(2, 3, CV_32F);
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
warp_matrix.at<float>(i,j) = m[j][i];
}
}
// calculate image alignment
try
{
double cc = findTransformECC (templateMat, targetMat, warp_matrix, warp_mode,
TermCriteria (TermCriteria::EPS + TermCriteria::COUNT,
maxiter, termination_eps));
}
catch(cv::Exception e)
{
if (e.code == cv::Error::StsNoConv)
{
cout << "findTransformECC did not converge";
}
}
}
// We need gdi objects to go out of scope before we call GdiplusShutdown.
GdiplusShutdown(m_gdiplusToken);
}
int _tmain(int argc, _TCHAR* argv[])
{
findTransformEccTest();
return 0;
}