You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

447 lines
10 KiB
C++

#include "TestTransform.h"
#include <QTransform>
#include <QPainter>
#include <opencv.hpp>
#include <vector>
using namespace cv;
using namespace std;
#define M_LOW_TOLERANCE 0.000001
void transPoints(vector<Point2d>& vec, const Matx33d& mat)
{
for (size_t i = 0; i < vec.size(); ++i)
{
Point2d p = vec[i];
Point3d tp = mat * p;
vec[i] = Point2d(tp.x / tp.z, tp.y / tp.z);
}
}
void transPoints(vector<Point2d>& vec, const Mat& mat)
{
Matx33d matx = Matx33d::eye();
Mat matx_(3, 3, CV_64FC1, matx.val);
if (mat.rows == 2 && mat.cols == 3)
{
mat.copyTo(matx_.rowRange(0, 2));
}
else if (mat.rows == 3 && mat.cols == 3)
{
mat.copyTo(matx_);
}
else
{
std::cout << "not supported transformation mat with its size as " \
<< mat.rows << "x" << mat.cols << std::endl;
return;
}
transPoints(vec, matx);
}
Matx33d affineTrans(const vector<Point2d>& src, const vector<Point2d>& dst)
{
if (dst.empty() || src.empty()) {
return Mat();
}
Point2d pc, qc;
int smallerSize = src.size() < dst.size() ? src.size() : dst.size();
for (int i = 0; i < smallerSize; i++) {
pc += src[i];
qc += dst[i];
}
pc.x /= smallerSize;
pc.y /= smallerSize;
qc.x /= smallerSize;
qc.y /= smallerSize;
Matx21d pit;
Matx12d pi;
Matx12d qi;
Matx22d spitpi = Matx22d::zeros();
Matx22d pitpi;
Matx22d pitqi;
Matx22d spitqi = Matx22d::zeros();
for (int i = 0; i < src.size() && i < dst.size(); i++) {
Point2d qpi = src[i] - pc;
Point2d qqi = dst[i] - qc;
pit(0) = qpi.x;
pit(1) = qpi.y;
pi(0) = qpi.x;
pi(1) = qpi.y;
qi(0) = qqi.x;
qi(1) = qqi.y;
pitpi = pit * pi;
spitpi = pitpi + spitpi;
pitqi = pit * qi;
spitqi = pitqi + spitqi;
}
Matx22d ispitpi;
ispitpi = spitpi.inv();
Matx22d M = ispitpi * spitqi;
double m11 = M(0, 0);
double m21 = M(0, 1);
double m12 = M(1, 0);
double m22 = M(1, 1);
Matx33d qm(m11, m12, 0, m21, m22, 0, 0, 0, 1);
Matx33d pcm(1.0, 0, -pc.x, 0, 1.0, -pc.y, 0, 0, 1);
Matx33d qcm(1.0, 0, qc.x, 0, 1.0, qc.y, 0, 0, 1);
Matx33d ret = qcm * qm*pcm;
return ret;
}
cv::Matx33d rigidTrans(const vector<Point2d>& src, const vector<Point2d>& dst,
Mat* pCenRotScaleMat = NULL)
{
if (dst.empty() || src.empty()) {
return Mat();
}
vector<double> weights(src.size(), 1.0 / src.size());
Point2d pc, qc;
double wsum = 0;
for (int i = 0; i < src.size(); i++) {
double w = 1.0 / src.size();
weights[i] = w;
pc += src[i] * w;
qc += dst[i] * w;
wsum += w;
}
pc.x /= wsum;
pc.y /= wsum;
qc.x /= wsum;
qc.y /= wsum;
double u = 0;
double u1, u2;
u1 = 0;
u2 = 0;
for (int i = 0; i < src.size() && i < dst.size(); i++) {
Point2d qpi = src[i] - pc;
Point2d qqi = dst[i] - qc;
Point2d pi(qpi.x, qpi.y);
Point2d qi(qqi.x, qqi.y);
u1 += pi.dot(qi)*weights[i];
Point2d pi_(pi.y, -pi.x);
u2 += qi.dot(pi_)*weights[i];
}
u = sqrt(u1*u1 + u2 * u2);
if (u < M_LOW_TOLERANCE) {
u = M_LOW_TOLERANCE;
}
Matx22d R = Matx22d::zeros();
Matx22d r = Matx22d::zeros();
for (int i = 0; i < src.size() && i < dst.size(); i++) {
Point2d qpi = src[i] - pc;
Point2d qqi = dst[i] - qc;
Point2d pi(qpi.x, qpi.y);
Point2d qi(qqi.x, qqi.y);
Point2d pi_(pi.y, -pi.x);
Point2d qi_(qi.y, -qi.x);
r(0, 0) = pi.dot(qi);
r(0, 1) = pi.dot(qi_);
r(1, 0) = pi_.dot(qi);
r(1, 1) = pi_.dot(qi_);
R = R + r * (weights[i] / u);
}
double m11 = R(0, 0);
double m21 = R(0, 1);
double m12 = R(1, 0);
double m22 = R(1, 1);
Matx33d qm(m11, m12, 0, m21, m22, 0, 0, 0, 1);
Matx33d pcm(1.0, 0, -pc.x, 0, 1.0, -pc.y, 0, 0, 1);
Matx33d qcm(1.0, 0, qc.x, 0, 1.0, qc.y, 0, 0, 1);
Matx33d ret = qcm * qm*pcm;
if (pCenRotScaleMat)
{
*pCenRotScaleMat = Mat(qm);
}
return ret;
}
bool cmpPointVec(const vector<Point2d>& vec0, const vector<Point2d>& vec1, const Matx33d& mat, double tor)
{
int smallerSize = vec0.size() < vec1.size() ? vec0.size() : vec1.size();
for (int i = 0; i < smallerSize; ++i)
{
Point2d p0, p1;
p0 = vec0[i];
p1 = vec1[i];
Point3d tp0 = mat * p0;
tp0.x /= tp0.z;
tp0.y /= tp0.z;
if (abs(p1.x - tp0.x) > tor || abs(p1.y - tp0.y) > tor)
{
return false;
}
}
return true;
}
void testTransSolver()
{
{
//rotation only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(0, 1));
vec1.push_back(Point2d(-1, 1));
vec1.push_back(Point2d(-1, 0));
Matx33d ret = affineTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
ret = rigidTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// rotation and scale
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(1, 1));
vec1.push_back(Point2d(0, 2));
vec1.push_back(Point2d(-1, 1));
Matx33d ret = affineTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// scale only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(2, 0));
vec1.push_back(Point2d(2, 2));
vec1.push_back(Point2d(0, 2));
Matx33d ret = affineTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// translation only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d(1, 1));
vec1.push_back(Point2d(2, 1));
vec1.push_back(Point2d(2, 2));
vec1.push_back(Point2d(1, 2));
Matx33d ret = affineTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
ret = rigidTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
for (int i = 0; i < 10000; ++i)
{
// random rotation and translation
Mat mat23 = getRotationMatrix2D(Point2f(), rand() % 360, 1.0);
mat23.at<double>(0, 2) = (rand() % 1000) / 1000.0;
mat23.at<double>(1, 2) = (rand() % 1000) / 1000.0;
Matx33d matx = Matx33d::eye();
Mat mat(3, 3, CV_64FC1, matx.val);
mat23.copyTo(mat.rowRange(0, 2));
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1 = vec0;
transPoints(vec1, matx);
Matx33d ret = affineTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
ret = rigidTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
}
{
// skew only
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
vec1.push_back(Point2d());
vec1.push_back(Point2d(1, 0));
vec1.push_back(Point2d(2, 1));
vec1.push_back(Point2d(1, 1));
Matx33d ret = affineTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
{
// random affine
for (int i = 0; i < 10000; ++i)
{
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d());
vec0.push_back(Point2d(1, 0));
vec0.push_back(Point2d(1, 1));
vec0.push_back(Point2d(0, 1));
Matx33d trans = Matx33d::eye();
Mat mat(3, 3, CV_64FC1, trans.val);
randu(mat.rowRange(0, 2), 0, 1.0);
vec1 = vec0;
transPoints(vec1, trans);
Matx33d ret = affineTrans(vec0, vec1);
_ASSERTE(cmpPointVec(vec0, vec1, ret, M_LOW_TOLERANCE));
}
}
}
void translate(QPolygonF srcPos, QPolygonF dstPos)
{
vector<Point2d> vec0, vec1;
vec0.push_back(Point2d(srcPos.at(0).x(), srcPos.at(0).y()));
vec0.push_back(Point2d(srcPos.at(1).x(), srcPos.at(1).y()));
vec0.push_back(Point2d(srcPos.at(2).x(), srcPos.at(2).y()));
vec0.push_back(Point2d(srcPos.at(3).x(), srcPos.at(3).y()));
vec1.push_back(Point2d(dstPos.at(0).x(), dstPos.at(0).y()));
vec1.push_back(Point2d(dstPos.at(1).x(), dstPos.at(1).y()));
vec1.push_back(Point2d(dstPos.at(2).x(), dstPos.at(2).y()));
vec1.push_back(Point2d(dstPos.at(3).x(), dstPos.at(3).y()));
Matx33d ret = affineTrans(vec0, vec1);
Mat retMat = Mat(ret);
Point2f srcVec[4];
Point2f dstTri[4];
srcVec[0].x = srcPos.at(0).x();
srcVec[0].y = srcPos.at(0).y();
srcVec[1].x = srcPos.at(1).x();
srcVec[1].y = srcPos.at(1).y();
srcVec[2].x = srcPos.at(2).x();
srcVec[2].y = srcPos.at(2).y();
srcVec[3].x = srcPos.at(3).x();
srcVec[3].y = srcPos.at(3).y();
dstTri[0].x = dstPos.at(0).x();
dstTri[0].y = dstPos.at(0).y();
dstTri[1].x = dstPos.at(1).x();
dstTri[1].y = dstPos.at(1).y();
dstTri[2].x = dstPos.at(2).x();
dstTri[2].y = dstPos.at(2).y();
dstTri[3].x = dstPos.at(3).x();
dstTri[3].y = dstPos.at(3).y();
/// <20><><EFBFBD>÷<EFBFBD><C3B7><EFBFBD><EFBFBD>
Mat warp_mat = getAffineTransform(srcVec, dstTri);
double* pTransMat = (double*)(warp_mat.data);
double m00=pTransMat[0];
double m10=pTransMat[1];
double m20=pTransMat[2];
double m01=pTransMat[3];
double m11=pTransMat[4];
double m21=pTransMat[5];
int newX = 100;
int newY = 100;
double temX = newX * m00 + newY * m10 + m20;
double temY = newX * m01 + newY * m11 + m21;
int a = 0;
}
TestTransform::TestTransform(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(onButtonClicked()));
}
Q_SLOT void TestTransform::onButtonClicked()
{
//testTransSolver();
QPolygonF srcP;
srcP.append(QPointF(-105, -100));
srcP.append(QPointF(-204, -100));
srcP.append(QPointF(-300, -200));
srcP.append(QPointF(-200, -200));
QPolygonF dstP;
dstP.append(QPointF(-50, 50));
dstP.append(QPointF(50, 50));
dstP.append(QPointF(50, -50));
dstP.append(QPointF(-50, -50));
translate(srcP, dstP);
}
void TestTransform::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
QFont font("Courier", 24);
painter.setFont(font);
painter.drawText(100, 100, "Hello, world!");
QTransform transform;
transform.rotate(15.0);
painter.setWorldTransform(transform);
painter.drawText(100, 100, "Hello, world!");
}