|
|
|
|
|
#ifndef DLIB_POLY_ImAGE_Hh_ |
|
#define DLIB_POLY_ImAGE_Hh_ |
|
|
|
#include "poly_image_abstract.h" |
|
#include "build_separable_poly_filters.h" |
|
#include "../algs.h" |
|
#include "../matrix.h" |
|
#include "../array2d.h" |
|
#include "../geometry.h" |
|
#include <cmath> |
|
|
|
namespace dlib |
|
{ |
|
|
|
|
|
|
|
template < |
|
long Downsample |
|
> |
|
class poly_image : noncopyable |
|
{ |
|
COMPILE_TIME_ASSERT(Downsample >= 1); |
|
public: |
|
const static long downsample = Downsample; |
|
typedef matrix<double, 0, 1> descriptor_type; |
|
|
|
poly_image( |
|
long order_, |
|
long window_size_, |
|
bool normalization = true, |
|
bool rotation_invariance_ = false |
|
) |
|
{ |
|
setup(order_, window_size_); |
|
set_uses_normalization(normalization); |
|
set_is_rotationally_invariant(rotation_invariance_); |
|
} |
|
|
|
poly_image ( |
|
) |
|
{ |
|
clear(); |
|
} |
|
|
|
void clear ( |
|
) |
|
{ |
|
normalize = true; |
|
rotation_invariance = false; |
|
poly_coef.clear(); |
|
order = 3; |
|
window_size = 13; |
|
border_size = (long)std::ceil(std::floor(window_size/2.0)/downsample); |
|
num_rows = 0; |
|
num_cols = 0; |
|
filters = build_separable_poly_filters(order, window_size); |
|
} |
|
|
|
long get_order ( |
|
) const |
|
{ |
|
return order; |
|
} |
|
|
|
long get_window_size ( |
|
) const |
|
{ |
|
return window_size; |
|
} |
|
|
|
void setup ( |
|
long order_, |
|
long window_size_ |
|
) |
|
{ |
|
|
|
DLIB_ASSERT(1 <= order_ && order_ <= 6 && |
|
window_size_ >= 3 && (window_size_%2) == 1, |
|
"\t descriptor_type poly_image::setup()" |
|
<< "\n\t Invalid arguments were given to this function." |
|
<< "\n\t order_: " << order_ |
|
<< "\n\t window_size_: " << window_size_ |
|
<< "\n\t this: " << this |
|
); |
|
|
|
|
|
poly_coef.clear(); |
|
order = order_; |
|
window_size = window_size_; |
|
border_size = (long)std::ceil(std::floor(window_size/2.0)/downsample); |
|
num_rows = 0; |
|
num_cols = 0; |
|
filters = build_separable_poly_filters(order, window_size); |
|
} |
|
|
|
bool uses_normalization ( |
|
) const { return normalize; } |
|
|
|
void set_uses_normalization ( |
|
bool normalization |
|
) |
|
{ |
|
normalize = normalization; |
|
} |
|
|
|
bool is_rotationally_invariant ( |
|
) const { return rotation_invariance; } |
|
|
|
void set_is_rotationally_invariant ( |
|
bool rotation_invariance_ |
|
) |
|
{ |
|
rotation_invariance = rotation_invariance_; |
|
} |
|
|
|
void copy_configuration ( |
|
const poly_image& item |
|
) |
|
{ |
|
normalize = item.normalize; |
|
rotation_invariance = item.rotation_invariance; |
|
if (order != item.order || |
|
window_size != item.window_size) |
|
{ |
|
order = item.order; |
|
window_size = item.window_size; |
|
border_size = item.border_size; |
|
filters = item.filters; |
|
} |
|
} |
|
|
|
template < |
|
typename image_type |
|
> |
|
inline void load ( |
|
const image_type& img |
|
) |
|
{ |
|
COMPILE_TIME_ASSERT( pixel_traits<typename image_traits<image_type>::pixel_type>::has_alpha == false ); |
|
|
|
poly_coef.resize(get_num_dimensions()); |
|
des.set_size(get_num_dimensions()); |
|
|
|
|
|
if (normalize) |
|
{ |
|
array2d<float> coef0; |
|
rectangle rect = filter_image(img, coef0, filters[0]); |
|
num_rows = rect.height(); |
|
num_cols = rect.width(); |
|
|
|
for (unsigned long i = 1; i < filters.size(); ++i) |
|
{ |
|
filter_image(img, poly_coef[i-1], filters[i]); |
|
|
|
|
|
for (long r = 0; r < coef0.nr(); ++r) |
|
{ |
|
for (long c = 0; c < coef0.nc(); ++c) |
|
{ |
|
if (coef0[r][c] >= 1) |
|
poly_coef[i-1][r][c] /= coef0[r][c]; |
|
else |
|
poly_coef[i-1][r][c] = 0; |
|
} |
|
} |
|
} |
|
|
|
if (rotation_invariance) |
|
rotate_polys(rect); |
|
} |
|
else |
|
{ |
|
rectangle rect; |
|
for (unsigned long i = 0; i < filters.size(); ++i) |
|
{ |
|
rect = filter_image(img, poly_coef[i], filters[i]); |
|
} |
|
num_rows = rect.height(); |
|
num_cols = rect.width(); |
|
|
|
if (rotation_invariance) |
|
rotate_polys(rect); |
|
} |
|
} |
|
|
|
void unload() |
|
{ |
|
poly_coef.clear(); |
|
num_rows = 0; |
|
num_cols = 0; |
|
} |
|
|
|
inline size_t size ( |
|
) const { return static_cast<unsigned long>(nr()*nc()); } |
|
|
|
inline long nr ( |
|
) const { return num_rows; } |
|
|
|
inline long nc ( |
|
) const { return num_cols; } |
|
|
|
long get_num_dimensions ( |
|
) const |
|
{ |
|
if (normalize) |
|
{ |
|
|
|
return filters.size()-1; |
|
} |
|
else |
|
{ |
|
return filters.size(); |
|
} |
|
} |
|
|
|
inline const descriptor_type& operator() ( |
|
long row, |
|
long col |
|
) const |
|
{ |
|
|
|
DLIB_ASSERT( 0 <= row && row < nr() && |
|
0 <= col && col < nc(), |
|
"\t descriptor_type poly_image::operator()()" |
|
<< "\n\t invalid row or col argument" |
|
<< "\n\t row: " << row |
|
<< "\n\t col: " << col |
|
<< "\n\t nr(): " << nr() |
|
<< "\n\t nc(): " << nc() |
|
<< "\n\t this: " << this |
|
); |
|
|
|
|
|
row += border_size; |
|
col += border_size; |
|
|
|
for (long i = 0; i < des.size(); ++i) |
|
des(i) = poly_coef[i][row][col]; |
|
|
|
return des; |
|
} |
|
|
|
const rectangle get_block_rect ( |
|
long row, |
|
long col |
|
) const |
|
{ |
|
return centered_rect(Downsample*point(col+border_size, row+border_size), |
|
window_size, window_size); |
|
} |
|
|
|
const point image_to_feat_space ( |
|
const point& p |
|
) const |
|
{ |
|
return p/Downsample - point(border_size, border_size); |
|
} |
|
|
|
const rectangle image_to_feat_space ( |
|
const rectangle& rect |
|
) const |
|
{ |
|
return rectangle(image_to_feat_space(rect.tl_corner()), image_to_feat_space(rect.br_corner())); |
|
} |
|
|
|
const point feat_to_image_space ( |
|
const point& p |
|
) const |
|
{ |
|
return (p + point(border_size, border_size))*Downsample; |
|
} |
|
|
|
const rectangle feat_to_image_space ( |
|
const rectangle& rect |
|
) const |
|
{ |
|
return rectangle(feat_to_image_space(rect.tl_corner()), feat_to_image_space(rect.br_corner())); |
|
} |
|
|
|
|
|
|
|
friend void serialize (const poly_image& item, std::ostream& out) |
|
{ |
|
int version = 1; |
|
serialize(version, out); |
|
serialize(item.poly_coef, out); |
|
serialize(item.order, out); |
|
serialize(item.window_size, out); |
|
serialize(item.border_size, out); |
|
serialize(item.num_rows, out); |
|
serialize(item.num_cols, out); |
|
serialize(item.normalize, out); |
|
serialize(item.rotation_invariance, out); |
|
serialize(item.filters, out); |
|
} |
|
|
|
friend void deserialize (poly_image& item, std::istream& in ) |
|
{ |
|
int version = 0; |
|
deserialize(version, in); |
|
if (version != 1) |
|
throw dlib::serialization_error("Unexpected version found while deserializing dlib::poly_image"); |
|
|
|
deserialize(item.poly_coef, in); |
|
deserialize(item.order, in); |
|
deserialize(item.window_size, in); |
|
deserialize(item.border_size, in); |
|
deserialize(item.num_rows, in); |
|
deserialize(item.num_cols, in); |
|
deserialize(item.normalize, in); |
|
deserialize(item.rotation_invariance, in); |
|
deserialize(item.filters, in); |
|
} |
|
|
|
private: |
|
|
|
matrix<float,2,1> rotate_order_1 ( |
|
const matrix<float,2,1>& w, |
|
double cos_theta, |
|
double sin_theta |
|
) const |
|
{ |
|
const double w1 = w(0); |
|
const double w2 = w(1); |
|
matrix<double,2,2> M; |
|
M = w1, w2, |
|
w2, -w1; |
|
|
|
matrix<double,2,1> x; |
|
x = cos_theta, |
|
sin_theta; |
|
|
|
return matrix_cast<float>(M*x); |
|
} |
|
|
|
matrix<float,3,1> rotate_order_2 ( |
|
const matrix<float,3,1>& w, |
|
double cos_theta, |
|
double sin_theta |
|
) const |
|
{ |
|
const double w1 = w(0); |
|
const double w2 = w(1); |
|
const double w3 = w(2); |
|
matrix<double,3,3> M; |
|
M = w1, w2, w3, |
|
w2, (2*w3-2*w1), -w2, |
|
w3, -w2, w1; |
|
|
|
matrix<double,3,1> x; |
|
x = std::pow(cos_theta,2.0), |
|
cos_theta*sin_theta, |
|
std::pow(sin_theta,2.0); |
|
|
|
return matrix_cast<float>(M*x); |
|
} |
|
|
|
matrix<float,4,1> rotate_order_3 ( |
|
const matrix<float,4,1>& w, |
|
double cos_theta, |
|
double sin_theta |
|
) const |
|
{ |
|
const double w1 = w(0); |
|
const double w2 = w(1); |
|
const double w3 = w(2); |
|
const double w4 = w(3); |
|
matrix<double,4,4> M; |
|
M = w1, w2, w3, w4, |
|
w2, (2*w3-3*w1), (3*w4-2*w2), -w3, |
|
w3, (3*w4-2*w2), (3*w1-2*w3), w2, |
|
w4, -w3, w2, -w1; |
|
|
|
matrix<double,4,1> x; |
|
x = std::pow(cos_theta,3.0), |
|
std::pow(cos_theta,2.0)*sin_theta, |
|
cos_theta*std::pow(sin_theta,2.0), |
|
std::pow(sin_theta,3.0); |
|
|
|
return matrix_cast<float>(M*x); |
|
} |
|
|
|
matrix<float,5,1> rotate_order_4 ( |
|
const matrix<float,5,1>& w, |
|
double cos_theta, |
|
double sin_theta |
|
) const |
|
{ |
|
const double w1 = w(0); |
|
const double w2 = w(1); |
|
const double w3 = w(2); |
|
const double w4 = w(3); |
|
const double w5 = w(4); |
|
matrix<double,5,5> M; |
|
M = w1, w2, w3, w4, w5, |
|
w2, (2*w3-4*w1), (3*w4-3*w2), (4*w5-2*w3), -w4, |
|
w3, (3*w4-3*w2), (6*w1-4*w3+6*w5), (3*w2-3*w4), w3, |
|
w4, (4*w5-2*w3), (3*w2-3*w4), (2*w3-4*w1), -w2, |
|
w5, -w4, w3, -w2, w1; |
|
|
|
matrix<double,5,1> x; |
|
x = std::pow(cos_theta,4.0), |
|
std::pow(cos_theta,3.0)*sin_theta, |
|
std::pow(cos_theta,2.0)*std::pow(sin_theta,2.0), |
|
cos_theta*std::pow(sin_theta,3.0), |
|
std::pow(sin_theta,4.0); |
|
|
|
return matrix_cast<float>(M*x); |
|
} |
|
|
|
matrix<float,6,1> rotate_order_5 ( |
|
const matrix<float,6,1>& w, |
|
double cos_theta, |
|
double sin_theta |
|
) const |
|
{ |
|
const double w1 = w(0); |
|
const double w2 = w(1); |
|
const double w3 = w(2); |
|
const double w4 = w(3); |
|
const double w5 = w(4); |
|
const double w6 = w(5); |
|
matrix<double,6,6> M; |
|
M = w1, w2, w3, w4, w5, w6, |
|
w2, (2*w3-5*w1), (3*w4-4*w2), (4*w5-3*w3), (5*w6-2*w4), -w5, |
|
w3, (3*w4-4*w2), (10*w1-6*w3+6*w5), (6*w2-6*w4+10*w6), (3*w3-4*w5), w4, |
|
w4, (4*w5-3*w3), (6*w2-6*w4+10*w6), (-10*w1+6*w3-6*w5), (3*w4-4*w2), -w3, |
|
w5, (5*w6-2*w4), (3*w3-4*w5), (3*w4-4*w2), (5*w1-2*w3), w2, |
|
w6, -w5, w4, -w3, w2, -w1; |
|
|
|
matrix<double,6,1> x; |
|
x = std::pow(cos_theta,5.0), |
|
std::pow(cos_theta,4.0)*sin_theta, |
|
std::pow(cos_theta,3.0)*std::pow(sin_theta,2.0), |
|
std::pow(cos_theta,2.0)*std::pow(sin_theta,3.0), |
|
cos_theta*std::pow(sin_theta,4.0), |
|
std::pow(sin_theta,5.0); |
|
|
|
return matrix_cast<float>(M*x); |
|
} |
|
|
|
matrix<float,7,1> rotate_order_6 ( |
|
const matrix<float,7,1>& w, |
|
double cos_theta, |
|
double sin_theta |
|
) const |
|
{ |
|
const double w1 = w(0); |
|
const double w2 = w(1); |
|
const double w3 = w(2); |
|
const double w4 = w(3); |
|
const double w5 = w(4); |
|
const double w6 = w(5); |
|
const double w7 = w(6); |
|
matrix<double,7,7> M; |
|
M = w1, w2, w3, w4, w5, w6, w7, |
|
w2, (2*w3-6*w1), (3*w4-5*w2), (4*w5-4*w3), (5*w6-3*w4), (6*w7-2*w5), -w6, |
|
w3, (3*w4-5*w2), (15*w1-8*w3+ 6*w5), ( 10*w2 -9*w4+10*w6), ( 6*w3-8*w5+15*w7), (3*w4-5*w6), w5, |
|
w4, (4*w5-4*w3), (10*w2-9*w4+10*w6), (-20*w1+12*w3-12*w5+20*w7), (-10*w2+9*w4-10*w6), (4*w5-4*w3), -w4, |
|
w5, (5*w6-3*w4), ( 6*w3-8*w5+15*w7), (-10*w2 +9*w4-10*w6), ( 15*w1-8*w3 +6*w5), (5*w2-3*w4), w3, |
|
w6, (6*w7-2*w5), (3*w4-5*w6), (4*w5-4*w3), (5*w2-3*w4), (2*w3-6*w1), -w2, |
|
w7, -w6, w5, -w4, w3, -w2, w1; |
|
|
|
matrix<double,7,1> x; |
|
x = std::pow(cos_theta,6.0), |
|
std::pow(cos_theta,5.0)*sin_theta, |
|
std::pow(cos_theta,4.0)*std::pow(sin_theta,2.0), |
|
std::pow(cos_theta,3.0)*std::pow(sin_theta,3.0), |
|
std::pow(cos_theta,2.0)*std::pow(sin_theta,4.0), |
|
cos_theta*std::pow(sin_theta,5.0), |
|
std::pow(sin_theta,6.0); |
|
|
|
return matrix_cast<float>(M*x); |
|
} |
|
|
|
void rotate_polys ( |
|
const rectangle& rect |
|
) |
|
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const int off = (normalize) ? 0 : 1; |
|
|
|
for (long r = rect.top(); r <= rect.bottom(); ++r) |
|
{ |
|
for (long c = rect.left(); c <= rect.right(); ++c) |
|
{ |
|
dlib::vector<double,2> g(poly_coef[off+0][r][c], |
|
poly_coef[off+1][r][c]); |
|
|
|
const double len = g.length(); |
|
if (len != 0) |
|
{ |
|
g /= len; |
|
} |
|
else |
|
{ |
|
g.x() = 1; |
|
g.y() = 0; |
|
} |
|
|
|
const double cos_theta = g.x(); |
|
const double sin_theta = g.y(); |
|
|
|
if (order >= 1) |
|
{ |
|
matrix<float,2,1> w; |
|
w = poly_coef[off+0][r][c], |
|
poly_coef[off+1][r][c]; |
|
w = rotate_order_1(w, cos_theta, sin_theta); |
|
poly_coef[off+0][r][c] = w(0); |
|
poly_coef[off+1][r][c] = w(1); |
|
} |
|
if (order >= 2) |
|
{ |
|
matrix<float,3,1> w; |
|
w = poly_coef[off+2][r][c], |
|
poly_coef[off+3][r][c], |
|
poly_coef[off+4][r][c]; |
|
w = rotate_order_2(w, cos_theta, sin_theta); |
|
poly_coef[off+2][r][c] = w(0); |
|
poly_coef[off+3][r][c] = w(1); |
|
poly_coef[off+4][r][c] = w(2); |
|
} |
|
if (order >= 3) |
|
{ |
|
matrix<float,4,1> w; |
|
w = poly_coef[off+5][r][c], |
|
poly_coef[off+6][r][c], |
|
poly_coef[off+7][r][c], |
|
poly_coef[off+8][r][c]; |
|
w = rotate_order_3(w, cos_theta, sin_theta); |
|
poly_coef[off+5][r][c] = w(0); |
|
poly_coef[off+6][r][c] = w(1); |
|
poly_coef[off+7][r][c] = w(2); |
|
poly_coef[off+8][r][c] = w(3); |
|
} |
|
if (order >= 4) |
|
{ |
|
matrix<float,5,1> w; |
|
w = poly_coef[off+9][r][c], |
|
poly_coef[off+10][r][c], |
|
poly_coef[off+11][r][c], |
|
poly_coef[off+12][r][c], |
|
poly_coef[off+13][r][c]; |
|
w = rotate_order_4(w, cos_theta, sin_theta); |
|
poly_coef[off+9][r][c] = w(0); |
|
poly_coef[off+10][r][c] = w(1); |
|
poly_coef[off+11][r][c] = w(2); |
|
poly_coef[off+12][r][c] = w(3); |
|
poly_coef[off+13][r][c] = w(4); |
|
} |
|
if (order >= 5) |
|
{ |
|
matrix<float,6,1> w; |
|
w = poly_coef[off+14][r][c], |
|
poly_coef[off+15][r][c], |
|
poly_coef[off+16][r][c], |
|
poly_coef[off+17][r][c], |
|
poly_coef[off+18][r][c], |
|
poly_coef[off+19][r][c]; |
|
w = rotate_order_5(w, cos_theta, sin_theta); |
|
poly_coef[off+14][r][c] = w(0); |
|
poly_coef[off+15][r][c] = w(1); |
|
poly_coef[off+16][r][c] = w(2); |
|
poly_coef[off+17][r][c] = w(3); |
|
poly_coef[off+18][r][c] = w(4); |
|
poly_coef[off+19][r][c] = w(5); |
|
} |
|
if (order >= 6) |
|
{ |
|
matrix<float,7,1> w; |
|
w = poly_coef[off+20][r][c], |
|
poly_coef[off+21][r][c], |
|
poly_coef[off+22][r][c], |
|
poly_coef[off+23][r][c], |
|
poly_coef[off+24][r][c], |
|
poly_coef[off+25][r][c], |
|
poly_coef[off+26][r][c]; |
|
w = rotate_order_6(w, cos_theta, sin_theta); |
|
poly_coef[off+20][r][c] = w(0); |
|
poly_coef[off+21][r][c] = w(1); |
|
poly_coef[off+22][r][c] = w(2); |
|
poly_coef[off+23][r][c] = w(3); |
|
poly_coef[off+24][r][c] = w(4); |
|
poly_coef[off+25][r][c] = w(5); |
|
poly_coef[off+26][r][c] = w(6); |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
template <typename image_type> |
|
rectangle filter_image ( |
|
const image_type& img, |
|
array2d<float>& out, |
|
const std::vector<separable_filter_type>& filter |
|
) const |
|
{ |
|
rectangle rect = spatially_filter_image_separable_down(downsample, img, out, filter[0].first, filter[0].second); |
|
for (unsigned long i = 1; i < filter.size(); ++i) |
|
{ |
|
spatially_filter_image_separable_down(downsample, img, out, filter[i].first, filter[i].second, 1, false, true); |
|
} |
|
return rect; |
|
} |
|
|
|
|
|
|
|
std::vector<std::vector<separable_filter_type> > filters; |
|
|
|
dlib::array<array2d<float> > poly_coef; |
|
long order; |
|
long window_size; |
|
long border_size; |
|
long num_rows; |
|
long num_cols; |
|
|
|
bool normalize; |
|
bool rotation_invariance; |
|
|
|
mutable descriptor_type des; |
|
}; |
|
|
|
|
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|