992 lines
31 KiB
C++
992 lines
31 KiB
C++
// Copyright (C) 2003 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#ifndef DLIB_POINT_TrANSFORMS_H_
|
|
#define DLIB_POINT_TrANSFORMS_H_
|
|
|
|
#include "point_transforms_abstract.h"
|
|
#include "../algs.h"
|
|
#include "vector.h"
|
|
#include "../matrix.h"
|
|
#include "../matrix/matrix_la.h"
|
|
#include "../optimization/optimization.h"
|
|
#include "rectangle.h"
|
|
#include "drectangle.h"
|
|
#include <vector>
|
|
#include <cmath>
|
|
|
|
namespace dlib
|
|
{
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class point_rotator
|
|
{
|
|
public:
|
|
point_rotator (
|
|
)
|
|
{
|
|
sin_angle = 0;
|
|
cos_angle = 1;
|
|
}
|
|
|
|
point_rotator (
|
|
const double& angle
|
|
)
|
|
{
|
|
sin_angle = std::sin(angle);
|
|
cos_angle = std::cos(angle);
|
|
}
|
|
|
|
template <typename T>
|
|
const dlib::vector<T,2> operator() (
|
|
const dlib::vector<T,2>& p
|
|
) const
|
|
{
|
|
double x = cos_angle*p.x() - sin_angle*p.y();
|
|
double y = sin_angle*p.x() + cos_angle*p.y();
|
|
|
|
return dlib::vector<double,2>(x,y);
|
|
}
|
|
|
|
const matrix<double,2,2> get_m(
|
|
) const
|
|
{
|
|
matrix<double,2,2> temp;
|
|
temp = cos_angle, -sin_angle,
|
|
sin_angle, cos_angle;
|
|
return temp;
|
|
}
|
|
|
|
inline friend void serialize (const point_rotator& item, std::ostream& out)
|
|
{
|
|
serialize(item.sin_angle, out);
|
|
serialize(item.cos_angle, out);
|
|
}
|
|
|
|
inline friend void deserialize (point_rotator& item, std::istream& in)
|
|
{
|
|
deserialize(item.sin_angle, in);
|
|
deserialize(item.cos_angle, in);
|
|
}
|
|
|
|
private:
|
|
double sin_angle;
|
|
double cos_angle;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class point_transform
|
|
{
|
|
public:
|
|
|
|
point_transform (
|
|
)
|
|
{
|
|
sin_angle = 0;
|
|
cos_angle = 1;
|
|
translate.x() = 0;
|
|
translate.y() = 0;
|
|
}
|
|
|
|
point_transform (
|
|
const double& angle,
|
|
const dlib::vector<double,2>& translate_
|
|
)
|
|
{
|
|
sin_angle = std::sin(angle);
|
|
cos_angle = std::cos(angle);
|
|
translate = translate_;
|
|
}
|
|
|
|
template <typename T>
|
|
const dlib::vector<T,2> operator() (
|
|
const dlib::vector<T,2>& p
|
|
) const
|
|
{
|
|
double x = cos_angle*p.x() - sin_angle*p.y();
|
|
double y = sin_angle*p.x() + cos_angle*p.y();
|
|
|
|
return dlib::vector<double,2>(x,y) + translate;
|
|
}
|
|
|
|
const matrix<double,2,2> get_m(
|
|
) const
|
|
{
|
|
matrix<double,2,2> temp;
|
|
temp = cos_angle, -sin_angle,
|
|
sin_angle, cos_angle;
|
|
return temp;
|
|
}
|
|
|
|
const dlib::vector<double,2> get_b(
|
|
) const { return translate; }
|
|
|
|
inline friend void serialize (const point_transform& item, std::ostream& out)
|
|
{
|
|
serialize(item.sin_angle, out);
|
|
serialize(item.cos_angle, out);
|
|
serialize(item.translate, out);
|
|
}
|
|
|
|
inline friend void deserialize (point_transform& item, std::istream& in)
|
|
{
|
|
deserialize(item.sin_angle, in);
|
|
deserialize(item.cos_angle, in);
|
|
deserialize(item.translate, in);
|
|
}
|
|
|
|
private:
|
|
double sin_angle;
|
|
double cos_angle;
|
|
dlib::vector<double,2> translate;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class point_transform_affine
|
|
{
|
|
public:
|
|
|
|
point_transform_affine (
|
|
)
|
|
{
|
|
m = identity_matrix<double>(2);
|
|
b.x() = 0;
|
|
b.y() = 0;
|
|
}
|
|
|
|
point_transform_affine (
|
|
const matrix<double,2,2>& m_,
|
|
const dlib::vector<double,2>& b_
|
|
) :m(m_), b(b_)
|
|
{
|
|
}
|
|
|
|
const dlib::vector<double,2> operator() (
|
|
const dlib::vector<double,2>& p
|
|
) const
|
|
{
|
|
return m*p + b;
|
|
}
|
|
|
|
const matrix<double,2,2>& get_m(
|
|
) const { return m; }
|
|
|
|
const dlib::vector<double,2>& get_b(
|
|
) const { return b; }
|
|
|
|
inline friend void serialize (const point_transform_affine& item, std::ostream& out)
|
|
{
|
|
serialize(item.m, out);
|
|
serialize(item.b, out);
|
|
}
|
|
|
|
inline friend void deserialize (point_transform_affine& item, std::istream& in)
|
|
{
|
|
deserialize(item.m, in);
|
|
deserialize(item.b, in);
|
|
}
|
|
|
|
private:
|
|
matrix<double,2,2> m;
|
|
dlib::vector<double,2> b;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class rectangle_transform
|
|
{
|
|
public:
|
|
|
|
rectangle_transform (
|
|
)
|
|
{
|
|
}
|
|
|
|
rectangle_transform (
|
|
const point_transform_affine& tform_
|
|
) :tform(tform_)
|
|
{
|
|
}
|
|
|
|
drectangle operator() (
|
|
const drectangle& r
|
|
) const
|
|
{
|
|
dpoint tl = r.tl_corner();
|
|
dpoint tr = r.tr_corner();
|
|
dpoint bl = r.bl_corner();
|
|
dpoint br = r.br_corner();
|
|
// The new rectangle would ideally have this area if we could actually rotate
|
|
// the box. Note the 1+ is because that's how the rectangles calculate their
|
|
// width and height.
|
|
double new_area = (1+length(tform(tl)-tform(tr)))*(1+length(tform(tl)-tform(bl)));
|
|
|
|
// But if we rotate the corners of the rectangle and then find the rectangle
|
|
// that contains them we get this, which might have a much larger area than we
|
|
// want.
|
|
drectangle temp;
|
|
temp += tform(tl);
|
|
temp += tform(tr);
|
|
temp += tform(bl);
|
|
temp += tform(br);
|
|
// so we adjust the area to match the target area and have the same center as
|
|
// the above box.
|
|
double scale = std::sqrt(new_area/temp.area());
|
|
|
|
return centered_rect(center(temp), std::round(temp.width()*scale), std::round(temp.height()*scale));
|
|
}
|
|
|
|
rectangle operator() (
|
|
const rectangle& r
|
|
) const
|
|
{
|
|
return (*this)(drectangle(r));
|
|
}
|
|
|
|
const point_transform_affine& get_tform(
|
|
) const { return tform; }
|
|
|
|
inline friend void serialize (const rectangle_transform& item, std::ostream& out)
|
|
{
|
|
serialize(item.tform, out);
|
|
}
|
|
|
|
inline friend void deserialize (rectangle_transform& item, std::istream& in)
|
|
{
|
|
deserialize(item.tform, in);
|
|
}
|
|
|
|
private:
|
|
point_transform_affine tform;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine operator* (
|
|
const point_transform_affine& lhs,
|
|
const point_transform_affine& rhs
|
|
)
|
|
{
|
|
return point_transform_affine(lhs.get_m()*rhs.get_m(), lhs.get_m()*rhs.get_b()+lhs.get_b());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine inv (
|
|
const point_transform_affine& trans
|
|
)
|
|
{
|
|
matrix<double,2,2> im = inv(trans.get_m());
|
|
return point_transform_affine(im, -im*trans.get_b());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
point_transform_affine find_affine_transform (
|
|
const std::vector<dlib::vector<T,2> >& from_points,
|
|
const std::vector<dlib::vector<T,2> >& to_points
|
|
)
|
|
{
|
|
// make sure requires clause is not broken
|
|
DLIB_ASSERT(from_points.size() == to_points.size() &&
|
|
from_points.size() >= 3,
|
|
"\t point_transform_affine find_affine_transform(from_points, to_points)"
|
|
<< "\n\t Invalid inputs were given to this function."
|
|
<< "\n\t from_points.size(): " << from_points.size()
|
|
<< "\n\t to_points.size(): " << to_points.size()
|
|
);
|
|
|
|
matrix<double,3,0> P(3, from_points.size());
|
|
matrix<double,2,0> Q(2, from_points.size());
|
|
|
|
for (unsigned long i = 0; i < from_points.size(); ++i)
|
|
{
|
|
P(0,i) = from_points[i].x();
|
|
P(1,i) = from_points[i].y();
|
|
P(2,i) = 1;
|
|
|
|
Q(0,i) = to_points[i].x();
|
|
Q(1,i) = to_points[i].y();
|
|
}
|
|
|
|
const matrix<double,2,3> m = Q*pinv(P);
|
|
return point_transform_affine(subm(m,0,0,2,2), colm(m,2));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
point_transform_affine find_similarity_transform (
|
|
const std::vector<dlib::vector<T,2> >& from_points,
|
|
const std::vector<dlib::vector<T,2> >& to_points
|
|
)
|
|
{
|
|
// make sure requires clause is not broken
|
|
DLIB_ASSERT(from_points.size() == to_points.size() &&
|
|
from_points.size() >= 2,
|
|
"\t point_transform_affine find_similarity_transform(from_points, to_points)"
|
|
<< "\n\t Invalid inputs were given to this function."
|
|
<< "\n\t from_points.size(): " << from_points.size()
|
|
<< "\n\t to_points.size(): " << to_points.size()
|
|
);
|
|
|
|
// We use the formulas from the paper: Least-squares estimation of transformation
|
|
// parameters between two point patterns by Umeyama. They are equations 34 through
|
|
// 43.
|
|
|
|
dlib::vector<double,2> mean_from, mean_to;
|
|
double sigma_from = 0, sigma_to = 0;
|
|
matrix<double,2,2> cov;
|
|
cov = 0;
|
|
|
|
for (unsigned long i = 0; i < from_points.size(); ++i)
|
|
{
|
|
mean_from += from_points[i];
|
|
mean_to += to_points[i];
|
|
}
|
|
mean_from /= from_points.size();
|
|
mean_to /= from_points.size();
|
|
|
|
for (unsigned long i = 0; i < from_points.size(); ++i)
|
|
{
|
|
sigma_from += length_squared(from_points[i] - mean_from);
|
|
sigma_to += length_squared(to_points[i] - mean_to);
|
|
cov += (to_points[i] - mean_to)*trans(from_points[i] - mean_from);
|
|
}
|
|
|
|
sigma_from /= from_points.size();
|
|
sigma_to /= from_points.size();
|
|
cov /= from_points.size();
|
|
|
|
matrix<double,2,2> u, v, s, d;
|
|
svd(cov, u,d,v);
|
|
s = identity_matrix(cov);
|
|
if (det(cov) < 0 || (det(cov) == 0 && det(u)*det(v)<0))
|
|
{
|
|
if (d(1,1) < d(0,0))
|
|
s(1,1) = -1;
|
|
else
|
|
s(0,0) = -1;
|
|
}
|
|
|
|
matrix<double,2,2> r = u*s*trans(v);
|
|
double c = 1;
|
|
if (sigma_from != 0)
|
|
c = 1.0/sigma_from * trace(d*s);
|
|
vector<double,2> t = mean_to - c*r*mean_from;
|
|
|
|
return point_transform_affine(c*r, t);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class point_transform_projective
|
|
{
|
|
public:
|
|
|
|
point_transform_projective (
|
|
)
|
|
{
|
|
m = identity_matrix<double>(3);
|
|
}
|
|
|
|
point_transform_projective (
|
|
const matrix<double,3,3>& m_
|
|
) :m(m_)
|
|
{
|
|
}
|
|
|
|
point_transform_projective (
|
|
const point_transform_affine& tran
|
|
)
|
|
{
|
|
set_subm(m, 0,0, 2,2) = tran.get_m();
|
|
set_subm(m, 0,2, 2,1) = tran.get_b();
|
|
m(2,0) = 0;
|
|
m(2,1) = 0;
|
|
m(2,2) = 1;
|
|
}
|
|
|
|
|
|
const dlib::vector<double,2> operator() (
|
|
const dlib::vector<double,2>& p
|
|
) const
|
|
{
|
|
dlib::vector<double,3> temp(p);
|
|
temp.z() = 1;
|
|
temp = m*temp;
|
|
if (temp.z() != 0)
|
|
temp = temp/temp.z();
|
|
return temp;
|
|
}
|
|
|
|
const matrix<double,3,3>& get_m(
|
|
) const { return m; }
|
|
|
|
inline friend void serialize (const point_transform_projective& item, std::ostream& out)
|
|
{
|
|
serialize(item.m, out);
|
|
}
|
|
|
|
inline friend void deserialize (point_transform_projective& item, std::istream& in)
|
|
{
|
|
deserialize(item.m, in);
|
|
}
|
|
|
|
private:
|
|
matrix<double,3,3> m;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_projective operator* (
|
|
const point_transform_projective& lhs,
|
|
const point_transform_projective& rhs
|
|
)
|
|
{
|
|
return point_transform_projective(lhs.get_m()*rhs.get_m());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_projective inv (
|
|
const point_transform_projective& trans
|
|
)
|
|
{
|
|
return point_transform_projective(inv(trans.get_m()));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
namespace impl_proj
|
|
{
|
|
|
|
inline point_transform_projective find_projective_transform_basic (
|
|
const std::vector<dlib::vector<double,2> >& from_points,
|
|
const std::vector<dlib::vector<double,2> >& to_points
|
|
)
|
|
/*!
|
|
ensures
|
|
- Uses the system of equations approach to finding a projective transform.
|
|
This is "Method 3" from Estimating Projective Transformation Matrix by
|
|
Zhengyou Zhang.
|
|
- It should be emphasized that the find_projective_transform_basic()
|
|
routine, which uses the most popular method for finding projective
|
|
transformations, doesn't really work well when the minimum error solution
|
|
doesn't have zero error. In this case, it can deviate by a large amount
|
|
from the proper minimum mean squared error transformation. Therefore,
|
|
our overall strategy will be to use the solution from
|
|
find_projective_transform_basic() as a starting point for a BFGS based
|
|
non-linear optimizer which will optimize the correct mean squared error
|
|
criterion.
|
|
|
|
A great essay on this subject is Homography Estimation by Elan Dubrofsky.
|
|
!*/
|
|
{
|
|
// make sure requires clause is not broken
|
|
DLIB_ASSERT(from_points.size() == to_points.size() &&
|
|
from_points.size() >= 4,
|
|
"\t point_transform_projective find_projective_transform_basic(from_points, to_points)"
|
|
<< "\n\t Invalid inputs were given to this function."
|
|
<< "\n\t from_points.size(): " << from_points.size()
|
|
<< "\n\t to_points.size(): " << to_points.size()
|
|
);
|
|
|
|
matrix<double,9,9> accum, u, v;
|
|
matrix<double,9,1> w;
|
|
matrix<double,2,9> B;
|
|
accum = 0;
|
|
B = 0;
|
|
for (unsigned long i = 0; i < from_points.size(); ++i)
|
|
{
|
|
dlib::vector<double,3> f = from_points[i];
|
|
f.z() = 1;
|
|
dlib::vector<double,3> t = to_points[i];
|
|
t.z() = 1;
|
|
|
|
set_subm(B,0,0,1,3) = t.y()*trans(f);
|
|
set_subm(B,1,0,1,3) = trans(f);
|
|
|
|
set_subm(B,0,3,1,3) = -t.x()*trans(f);
|
|
set_subm(B,1,6,1,3) = -t.x()*trans(f);
|
|
|
|
accum += trans(B)*B;
|
|
}
|
|
|
|
svd2(true, false, accum, u, w, v);
|
|
long j = index_of_min(w);
|
|
|
|
return point_transform_projective(reshape(colm(u,j),3,3));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
struct obj
|
|
{
|
|
/*!
|
|
WHAT THIS OBJECT REPRESENTS
|
|
This is the objective function we really want to minimize when looking
|
|
for a transformation matrix. That is, we would like the transformed
|
|
points to be as close as possible to their "to" points. Here,
|
|
closeness is measured using Euclidean distance.
|
|
|
|
!*/
|
|
obj(
|
|
const std::vector<dlib::vector<double,2> >& from_points_,
|
|
const std::vector<dlib::vector<double,2> >& to_points_
|
|
) :
|
|
from_points(from_points_) ,
|
|
to_points(to_points_)
|
|
{}
|
|
const std::vector<dlib::vector<double,2> >& from_points;
|
|
const std::vector<dlib::vector<double,2> >& to_points;
|
|
|
|
double operator() (
|
|
const matrix<double,9,1>& p
|
|
) const
|
|
{
|
|
point_transform_projective tran(reshape(p,3,3));
|
|
|
|
double sum = 0;
|
|
for (unsigned long i = 0; i < from_points.size(); ++i)
|
|
{
|
|
sum += length_squared(tran(from_points[i]) - to_points[i]);
|
|
}
|
|
return sum;
|
|
}
|
|
};
|
|
|
|
struct obj_der
|
|
{
|
|
/*!
|
|
WHAT THIS OBJECT REPRESENTS
|
|
This is the derivative of obj.
|
|
!*/
|
|
|
|
obj_der(
|
|
const std::vector<dlib::vector<double,2> >& from_points_,
|
|
const std::vector<dlib::vector<double,2> >& to_points_
|
|
) :
|
|
from_points(from_points_) ,
|
|
to_points(to_points_)
|
|
{}
|
|
const std::vector<dlib::vector<double,2> >& from_points;
|
|
const std::vector<dlib::vector<double,2> >& to_points;
|
|
|
|
matrix<double,9,1> operator() (
|
|
const matrix<double,9,1>& p
|
|
) const
|
|
{
|
|
const matrix<double,3,3> H = reshape(p,3,3);
|
|
|
|
matrix<double,3,3> grad;
|
|
grad = 0;
|
|
for (unsigned long i = 0; i < from_points.size(); ++i)
|
|
{
|
|
dlib::vector<double,3> from, to;
|
|
from = from_points[i];
|
|
from.z() = 1;
|
|
to = to_points[i];
|
|
to.z() = 1;
|
|
|
|
matrix<double,3,1> w = H*from;
|
|
const double scale = (w(2) != 0) ? (1.0/w(2)) : (1);
|
|
w *= scale;
|
|
matrix<double,3,1> residual = (w-to)*2*scale;
|
|
|
|
grad(0,0) += from.x()*residual(0);
|
|
grad(0,1) += from.y()*residual(0);
|
|
grad(0,2) += residual(0);
|
|
|
|
grad(1,0) += from.x()*residual(1);
|
|
grad(1,1) += from.y()*residual(1);
|
|
grad(1,2) += residual(1);
|
|
|
|
grad(2,0) += -(from.x()*w(0)*residual(0) + from.x()*w(1)*residual(1));
|
|
grad(2,1) += -(from.y()*w(0)*residual(0) + from.y()*w(1)*residual(1));
|
|
grad(2,2) += -( w(0)*residual(0) + w(1)*residual(1));
|
|
|
|
}
|
|
return reshape_to_column_vector(grad);
|
|
}
|
|
};
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_projective find_projective_transform (
|
|
const std::vector<dlib::vector<double,2> >& from_points,
|
|
const std::vector<dlib::vector<double,2> >& to_points
|
|
)
|
|
{
|
|
using namespace impl_proj;
|
|
// make sure requires clause is not broken
|
|
DLIB_ASSERT(from_points.size() == to_points.size() &&
|
|
from_points.size() >= 4,
|
|
"\t point_transform_projective find_projective_transform(from_points, to_points)"
|
|
<< "\n\t Invalid inputs were given to this function."
|
|
<< "\n\t from_points.size(): " << from_points.size()
|
|
<< "\n\t to_points.size(): " << to_points.size()
|
|
);
|
|
|
|
|
|
// Find a candidate projective transformation. Also, find the best affine
|
|
// transform and then compare it with the projective transform estimated using the
|
|
// direct SVD method. Use whichever one works better as the starting point for a
|
|
// BFGS based optimizer. If the best solution has large mean squared error and is
|
|
// also close to affine then find_projective_transform_basic() might give a very
|
|
// bad initial guess. So also checking for a good affine transformation can
|
|
// produce a much better final result in many cases.
|
|
point_transform_projective tran1 = find_projective_transform_basic(from_points, to_points);
|
|
point_transform_affine tran2 = find_affine_transform(from_points, to_points);
|
|
|
|
// check which is best
|
|
double error1 = 0;
|
|
double error2 = 0;
|
|
for (unsigned long i = 0; i < from_points.size(); ++i)
|
|
{
|
|
error1 += length_squared(tran1(from_points[i])-to_points[i]);
|
|
error2 += length_squared(tran2(from_points[i])-to_points[i]);
|
|
}
|
|
matrix<double,9,1> params;
|
|
// Pick the minimum error solution among the two so far.
|
|
if (error1 < error2)
|
|
params = reshape_to_column_vector(tran1.get_m());
|
|
else
|
|
params = reshape_to_column_vector(point_transform_projective(tran2).get_m());
|
|
|
|
|
|
// Now refine the transformation matrix so that we can be sure we have
|
|
// at least a local minimizer.
|
|
obj o(from_points, to_points);
|
|
obj_der der(from_points, to_points);
|
|
find_min(bfgs_search_strategy(),
|
|
objective_delta_stop_strategy(1e-6,100),
|
|
o,
|
|
der,
|
|
params,
|
|
0);
|
|
|
|
return point_transform_projective(reshape(params,3,3));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <typename T>
|
|
const dlib::vector<T,2> rotate_point (
|
|
const dlib::vector<T,2>& center,
|
|
const dlib::vector<T,2>& p,
|
|
double angle
|
|
)
|
|
{
|
|
point_rotator rot(angle);
|
|
return rot(p-center)+center;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline matrix<double,2,2> rotation_matrix (
|
|
double angle
|
|
)
|
|
{
|
|
const double ca = std::cos(angle);
|
|
const double sa = std::sin(angle);
|
|
|
|
matrix<double,2,2> m;
|
|
m = ca, -sa,
|
|
sa, ca;
|
|
return m;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class point_transform_affine3d
|
|
{
|
|
public:
|
|
|
|
point_transform_affine3d (
|
|
)
|
|
{
|
|
m = identity_matrix<double>(3);
|
|
b.x() = 0;
|
|
b.y() = 0;
|
|
}
|
|
|
|
point_transform_affine3d (
|
|
const matrix<double,3,3>& m_,
|
|
const dlib::vector<double,3>& b_
|
|
) :m(m_), b(b_)
|
|
{
|
|
}
|
|
|
|
const dlib::vector<double,3> operator() (
|
|
const dlib::vector<double,3>& p
|
|
) const
|
|
{
|
|
return m*p + b;
|
|
}
|
|
|
|
const matrix<double,3,3>& get_m(
|
|
) const { return m; }
|
|
|
|
const dlib::vector<double,3>& get_b(
|
|
) const { return b; }
|
|
|
|
inline friend void serialize (const point_transform_affine3d& item, std::ostream& out)
|
|
{
|
|
serialize(item.m, out);
|
|
serialize(item.b, out);
|
|
}
|
|
|
|
inline friend void deserialize (point_transform_affine3d& item, std::istream& in)
|
|
{
|
|
deserialize(item.m, in);
|
|
deserialize(item.b, in);
|
|
}
|
|
|
|
private:
|
|
matrix<double,3,3> m;
|
|
dlib::vector<double,3> b;
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine3d operator* (
|
|
const point_transform_affine3d& lhs,
|
|
const point_transform_affine& rhs
|
|
)
|
|
{
|
|
matrix<double,3,3> m;
|
|
m = 0;
|
|
set_subm(m, get_rect(rhs.get_m())) = rhs.get_m();
|
|
vector<double,3> b = rhs.get_b();
|
|
|
|
return point_transform_affine3d(lhs.get_m()*m, lhs.get_m()*b+lhs.get_b());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine3d operator* (
|
|
const point_transform_affine3d& lhs,
|
|
const point_transform_affine3d& rhs
|
|
)
|
|
{
|
|
return point_transform_affine3d(lhs.get_m()*rhs.get_m(), lhs.get_m()*rhs.get_b()+lhs.get_b());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine3d inv (
|
|
const point_transform_affine3d& trans
|
|
)
|
|
{
|
|
matrix<double,3,3> im = inv(trans.get_m());
|
|
return point_transform_affine3d(im, -im*trans.get_b());
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine3d rotate_around_x (
|
|
double angle
|
|
)
|
|
{
|
|
const double ca = std::cos(angle);
|
|
const double sa = std::sin(angle);
|
|
|
|
matrix<double,3,3> m;
|
|
m = 1, 0, 0,
|
|
0, ca, -sa,
|
|
0, sa, ca;
|
|
|
|
vector<double,3> b;
|
|
|
|
return point_transform_affine3d(m,b);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine3d rotate_around_y (
|
|
double angle
|
|
)
|
|
{
|
|
const double ca = std::cos(angle);
|
|
const double sa = std::sin(angle);
|
|
|
|
matrix<double,3,3> m;
|
|
m = ca, 0, sa,
|
|
0, 1, 0,
|
|
-sa, 0, ca;
|
|
|
|
vector<double,3> b;
|
|
|
|
return point_transform_affine3d(m,b);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine3d rotate_around_z (
|
|
double angle
|
|
)
|
|
{
|
|
const double ca = std::cos(angle);
|
|
const double sa = std::sin(angle);
|
|
|
|
matrix<double,3,3> m;
|
|
m = ca, -sa, 0,
|
|
sa, ca, 0,
|
|
0, 0, 1;
|
|
|
|
vector<double,3> b;
|
|
|
|
return point_transform_affine3d(m,b);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
inline point_transform_affine3d translate_point (
|
|
const vector<double,3>& delta
|
|
)
|
|
{
|
|
return point_transform_affine3d(identity_matrix<double>(3),delta);
|
|
}
|
|
|
|
inline point_transform_affine3d translate_point (
|
|
double x,
|
|
double y,
|
|
double z
|
|
)
|
|
{
|
|
return translate_point(vector<double>(x,y,z));
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
class camera_transform
|
|
{
|
|
|
|
public:
|
|
|
|
camera_transform (
|
|
)
|
|
{
|
|
*this = camera_transform(vector<double>(1,1,1),
|
|
vector<double>(0,0,0),
|
|
vector<double>(0,0,1),
|
|
90,
|
|
1);
|
|
}
|
|
|
|
camera_transform (
|
|
const vector<double>& camera_pos_,
|
|
const vector<double>& camera_looking_at_,
|
|
const vector<double>& camera_up_direction_,
|
|
const double camera_field_of_view_,
|
|
const unsigned long num_pixels_
|
|
)
|
|
{
|
|
// make sure requires clause is not broken
|
|
DLIB_CASSERT(0 < camera_field_of_view_ && camera_field_of_view_ < 180,
|
|
"\t camera_transform::camera_transform()"
|
|
<< "\n\t Invalid inputs were given to this function."
|
|
<< "\n\t camera_field_of_view_: " << camera_field_of_view_
|
|
);
|
|
|
|
camera_pos = camera_pos_;
|
|
camera_looking_at = camera_looking_at_;
|
|
camera_up_direction = camera_up_direction_;
|
|
camera_field_of_view = camera_field_of_view_;
|
|
num_pixels = num_pixels_;
|
|
|
|
dlib::vector<double> X,Y,Z;
|
|
Z = (camera_looking_at - camera_pos).normalize();
|
|
Y = camera_up_direction - dot(camera_up_direction,Z)*Z;
|
|
Y = Y.normalize();
|
|
X = Z.cross(Y);
|
|
|
|
set_rowm(proj,0) = trans(X);
|
|
// Minus because images have y axis going down but we want the 3d projection to appear using a normal coordinate system with y going up.
|
|
set_rowm(proj,1) = -trans(Y);
|
|
set_rowm(proj,2) = trans(Z);
|
|
|
|
width = num_pixels/2.0;
|
|
dist_scale = width/std::tan(pi/180*camera_field_of_view/2);
|
|
}
|
|
|
|
vector<double> get_camera_pos() const { return camera_pos; }
|
|
vector<double> get_camera_looking_at() const { return camera_looking_at; }
|
|
vector<double> get_camera_up_direction()const { return camera_up_direction; }
|
|
double get_camera_field_of_view() const { return camera_field_of_view; }
|
|
unsigned long get_num_pixels() const { return num_pixels; }
|
|
|
|
inline dpoint operator() (
|
|
const vector<double>& p,
|
|
double& scale,
|
|
double& distance
|
|
) const
|
|
{
|
|
vector<double> temp = p-camera_pos;
|
|
temp = proj*temp;
|
|
distance = temp.z();
|
|
scale = dist_scale/(temp.z()>0 ? temp.z() : 1e-9);
|
|
temp.x() = temp.x()*scale + width;
|
|
temp.y() = temp.y()*scale + width;
|
|
return temp;
|
|
}
|
|
|
|
dpoint operator() (
|
|
const vector<double>& p
|
|
) const
|
|
{
|
|
double scale, distance;
|
|
return (*this)(p,scale,distance);
|
|
}
|
|
|
|
inline friend void serialize (const camera_transform& item, std::ostream& out)
|
|
{
|
|
serialize(item.camera_pos, out);
|
|
serialize(item.camera_looking_at, out);
|
|
serialize(item.camera_up_direction, out);
|
|
serialize(item.camera_field_of_view, out);
|
|
serialize(item.num_pixels, out);
|
|
serialize(item.proj, out);
|
|
serialize(item.dist_scale, out);
|
|
serialize(item.width, out);
|
|
}
|
|
|
|
inline friend void deserialize (camera_transform& item, std::istream& in)
|
|
{
|
|
deserialize(item.camera_pos, in);
|
|
deserialize(item.camera_looking_at, in);
|
|
deserialize(item.camera_up_direction, in);
|
|
deserialize(item.camera_field_of_view, in);
|
|
deserialize(item.num_pixels, in);
|
|
deserialize(item.proj, in);
|
|
deserialize(item.dist_scale, in);
|
|
deserialize(item.width, in);
|
|
}
|
|
|
|
private:
|
|
|
|
vector<double> camera_pos;
|
|
vector<double> camera_looking_at;
|
|
vector<double> camera_up_direction;
|
|
double camera_field_of_view;
|
|
unsigned long num_pixels;
|
|
matrix<double,3,3> proj;
|
|
double dist_scale;
|
|
double width;
|
|
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
}
|
|
|
|
#endif // DLIB_POINT_TrANSFORMS_H_
|
|
|