|
|
|
|
|
#ifndef DLIB_RECTANGLe_ |
|
#define DLIB_RECTANGLe_ |
|
|
|
#include "rectangle_abstract.h" |
|
#include "../algs.h" |
|
#include <algorithm> |
|
#include <iostream> |
|
#include "../serialize.h" |
|
#include "vector.h" |
|
#include "../image_processing/generic_image.h" |
|
|
|
namespace dlib |
|
{ |
|
|
|
|
|
|
|
class rectangle |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
rectangle ( |
|
long l_, |
|
long t_, |
|
long r_, |
|
long b_ |
|
) : |
|
l(l_), |
|
t(t_), |
|
r(r_), |
|
b(b_) |
|
{} |
|
|
|
rectangle ( |
|
unsigned long w, |
|
unsigned long h |
|
) : |
|
l(0), |
|
t(0), |
|
r(static_cast<long>(w)-1), |
|
b(static_cast<long>(h)-1) |
|
{ |
|
DLIB_ASSERT((w > 0 && h > 0) || (w == 0 && h == 0), |
|
"\trectangle(width,height)" |
|
<< "\n\twidth and height must be > 0 or both == 0" |
|
<< "\n\twidth: " << w |
|
<< "\n\theight: " << h |
|
<< "\n\tthis: " << this |
|
); |
|
} |
|
|
|
rectangle ( |
|
const point& p |
|
) : |
|
l(p.x()), |
|
t(p.y()), |
|
r(p.x()), |
|
b(p.y()) |
|
{ |
|
} |
|
|
|
rectangle ( |
|
const point& p1, |
|
const point& p2 |
|
) |
|
{ |
|
*this = rectangle(p1) + rectangle(p2); |
|
} |
|
|
|
template <typename T> |
|
rectangle ( |
|
const vector<T,2>& p1, |
|
const vector<T,2>& p2 |
|
) |
|
{ |
|
*this = rectangle(p1) + rectangle(p2); |
|
} |
|
|
|
rectangle ( |
|
) : |
|
l(0), |
|
t(0), |
|
r(-1), |
|
b(-1) |
|
{} |
|
|
|
long top ( |
|
) const { return t; } |
|
|
|
long& top ( |
|
) { return t; } |
|
|
|
void set_top ( |
|
long top_ |
|
) { t = top_; } |
|
|
|
long left ( |
|
) const { return l; } |
|
|
|
long& left ( |
|
) { return l; } |
|
|
|
void set_left ( |
|
long left_ |
|
) { l = left_; } |
|
|
|
long right ( |
|
) const { return r; } |
|
|
|
long& right ( |
|
) { return r; } |
|
|
|
void set_right ( |
|
long right_ |
|
) { r = right_; } |
|
|
|
long bottom ( |
|
) const { return b; } |
|
|
|
long& bottom ( |
|
) { return b; } |
|
|
|
void set_bottom ( |
|
long bottom_ |
|
) { b = bottom_; } |
|
|
|
const point tl_corner ( |
|
) const { return point(left(), top()); } |
|
|
|
const point bl_corner ( |
|
) const { return point(left(), bottom()); } |
|
|
|
const point tr_corner ( |
|
) const { return point(right(), top()); } |
|
|
|
const point br_corner ( |
|
) const { return point(right(), bottom()); } |
|
|
|
unsigned long width ( |
|
) const |
|
{ |
|
if (is_empty()) |
|
return 0; |
|
else |
|
return r - l + 1; |
|
} |
|
|
|
unsigned long height ( |
|
) const |
|
{ |
|
if (is_empty()) |
|
return 0; |
|
else |
|
return b - t + 1; |
|
} |
|
|
|
unsigned long area ( |
|
) const |
|
{ |
|
return width()*height(); |
|
} |
|
|
|
bool is_empty ( |
|
) const { return (t > b || l > r); } |
|
|
|
rectangle operator + ( |
|
const rectangle& rhs |
|
) const |
|
{ |
|
if (rhs.is_empty()) |
|
return *this; |
|
else if (is_empty()) |
|
return rhs; |
|
|
|
return rectangle ( |
|
std::min(l,rhs.l), |
|
std::min(t,rhs.t), |
|
std::max(r,rhs.r), |
|
std::max(b,rhs.b) |
|
); |
|
} |
|
|
|
rectangle intersect ( |
|
const rectangle& rhs |
|
) const |
|
{ |
|
return rectangle ( |
|
std::max(l,rhs.l), |
|
std::max(t,rhs.t), |
|
std::min(r,rhs.r), |
|
std::min(b,rhs.b) |
|
); |
|
} |
|
|
|
bool contains ( |
|
const point& p |
|
) const |
|
{ |
|
if (p.x() < l || p.x() > r || p.y() < t || p.y() > b) |
|
return false; |
|
return true; |
|
} |
|
|
|
bool contains ( |
|
long x, |
|
long y |
|
) const |
|
{ |
|
if (x < l || x > r || y < t || y > b) |
|
return false; |
|
return true; |
|
} |
|
|
|
bool contains ( |
|
const rectangle& rect |
|
) const |
|
{ |
|
return (rect + *this == *this); |
|
} |
|
|
|
rectangle& operator+= ( |
|
const point& p |
|
) |
|
{ |
|
*this = *this + rectangle(p); |
|
return *this; |
|
} |
|
|
|
rectangle& operator+= ( |
|
const rectangle& rect |
|
) |
|
{ |
|
*this = *this + rect; |
|
return *this; |
|
} |
|
|
|
bool operator== ( |
|
const rectangle& rect |
|
) const |
|
{ |
|
return (l == rect.l) && (t == rect.t) && (r == rect.r) && (b == rect.b); |
|
} |
|
|
|
bool operator!= ( |
|
const rectangle& rect |
|
) const |
|
{ |
|
return !(*this == rect); |
|
} |
|
|
|
inline bool operator< (const dlib::rectangle& b) const |
|
{ |
|
if (left() < b.left()) return true; |
|
else if (left() > b.left()) return false; |
|
else if (top() < b.top()) return true; |
|
else if (top() > b.top()) return false; |
|
else if (right() < b.right()) return true; |
|
else if (right() > b.right()) return false; |
|
else if (bottom() < b.bottom()) return true; |
|
else if (bottom() > b.bottom()) return false; |
|
else return false; |
|
} |
|
|
|
private: |
|
long l; |
|
long t; |
|
long r; |
|
long b; |
|
}; |
|
|
|
|
|
|
|
inline void serialize ( |
|
const rectangle& item, |
|
std::ostream& out |
|
) |
|
{ |
|
try |
|
{ |
|
serialize(item.left(),out); |
|
serialize(item.top(),out); |
|
serialize(item.right(),out); |
|
serialize(item.bottom(),out); |
|
} |
|
catch (serialization_error& e) |
|
{ |
|
throw serialization_error(e.info + "\n while serializing an object of type rectangle"); |
|
} |
|
} |
|
|
|
inline void deserialize ( |
|
rectangle& item, |
|
std::istream& in |
|
) |
|
{ |
|
try |
|
{ |
|
deserialize(item.left(),in); |
|
deserialize(item.top(),in); |
|
deserialize(item.right(),in); |
|
deserialize(item.bottom(),in); |
|
} |
|
catch (serialization_error& e) |
|
{ |
|
throw serialization_error(e.info + "\n while deserializing an object of type rectangle"); |
|
} |
|
} |
|
|
|
inline std::ostream& operator<< ( |
|
std::ostream& out, |
|
const rectangle& item |
|
) |
|
{ |
|
out << "[(" << item.left() << ", " << item.top() << ") (" << item.right() << ", " << item.bottom() << ")]"; |
|
return out; |
|
} |
|
|
|
inline std::istream& operator>>( |
|
std::istream& in, |
|
rectangle& item |
|
) |
|
{ |
|
|
|
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n') |
|
in.get(); |
|
|
|
if (in.get() != '[') |
|
{ |
|
in.setstate(in.rdstate() | std::ios::failbit); |
|
return in; |
|
} |
|
|
|
point p1, p2; |
|
in >> p1; |
|
in >> p2; |
|
item = rectangle(p1) + rectangle(p2); |
|
|
|
|
|
while (in.peek() == ' ' || in.peek() == '\t' || in.peek() == '\r' || in.peek() == '\n') |
|
in.get(); |
|
|
|
if (in.get() != ']') |
|
{ |
|
in.setstate(in.rdstate() | std::ios::failbit); |
|
} |
|
return in; |
|
} |
|
|
|
|
|
|
|
inline const rectangle centered_rect ( |
|
long x, |
|
long y, |
|
unsigned long width, |
|
unsigned long height |
|
) |
|
{ |
|
rectangle result; |
|
result.set_left ( x - static_cast<long>(width) / 2 ); |
|
result.set_top ( y - static_cast<long>(height) / 2 ); |
|
result.set_right ( result.left() + width - 1 ); |
|
result.set_bottom ( result.top() + height - 1 ); |
|
return result; |
|
} |
|
|
|
|
|
|
|
inline rectangle intersect ( |
|
const rectangle& a, |
|
const rectangle& b |
|
) { return a.intersect(b); } |
|
|
|
|
|
|
|
inline unsigned long area ( |
|
const rectangle& a |
|
) { return a.area(); } |
|
|
|
|
|
|
|
inline point center ( |
|
const dlib::rectangle& rect |
|
) |
|
{ |
|
point temp(rect.left() + rect.right() + 1, |
|
rect.top() + rect.bottom() + 1); |
|
|
|
if (temp.x() < 0) |
|
temp.x() -= 1; |
|
|
|
if (temp.y() < 0) |
|
temp.y() -= 1; |
|
|
|
return temp/2; |
|
} |
|
|
|
|
|
|
|
inline dlib::vector<double,2> dcenter ( |
|
const dlib::rectangle& rect |
|
) |
|
{ |
|
dlib::vector<double,2> temp(rect.left() + rect.right(), |
|
rect.top() + rect.bottom()); |
|
|
|
return temp/2.0; |
|
} |
|
|
|
|
|
|
|
inline long distance_to_rect_edge ( |
|
const rectangle& rect, |
|
const point& p |
|
) |
|
{ |
|
using std::max; |
|
using std::min; |
|
using std::abs; |
|
|
|
const long dist_x = min(abs(p.x()-rect.left()), abs(p.x()-rect.right())); |
|
const long dist_y = min(abs(p.y()-rect.top()), abs(p.y()-rect.bottom())); |
|
|
|
if (rect.contains(p)) |
|
return min(dist_x,dist_y); |
|
else if (rect.left() <= p.x() && p.x() <= rect.right()) |
|
return dist_y; |
|
else if (rect.top() <= p.y() && p.y() <= rect.bottom()) |
|
return dist_x; |
|
else |
|
return dist_x + dist_y; |
|
} |
|
|
|
|
|
|
|
template <typename T> |
|
inline const dlib::vector<T,2> nearest_point ( |
|
const rectangle& rect, |
|
const dlib::vector<T,2>& p |
|
) |
|
{ |
|
dlib::vector<T,2> temp(p); |
|
if (temp.x() < rect.left()) |
|
temp.x() = rect.left(); |
|
else if (temp.x() > rect.right()) |
|
temp.x() = rect.right(); |
|
|
|
if (temp.y() < rect.top()) |
|
temp.y() = rect.top(); |
|
else if (temp.y() > rect.bottom()) |
|
temp.y() = rect.bottom(); |
|
|
|
return temp; |
|
} |
|
|
|
|
|
|
|
inline size_t nearest_rect ( |
|
const std::vector<rectangle>& rects, |
|
const point& p |
|
) |
|
{ |
|
DLIB_ASSERT(rects.size() > 0); |
|
size_t idx = 0; |
|
double best_dist = std::numeric_limits<double>::infinity(); |
|
|
|
for (size_t i = 0; i < rects.size(); ++i) |
|
{ |
|
if (rects[i].contains(p)) |
|
{ |
|
return i; |
|
} |
|
else |
|
{ |
|
double dist = (nearest_point(rects[i],p)-p).length(); |
|
if (dist < best_dist) |
|
{ |
|
best_dist = dist; |
|
idx = i; |
|
} |
|
} |
|
} |
|
return idx; |
|
} |
|
|
|
|
|
|
|
template <typename T> |
|
inline void clip_line_to_rectangle ( |
|
const rectangle& box, |
|
dlib::vector<T,2>& p1, |
|
dlib::vector<T,2>& p2 |
|
) |
|
{ |
|
|
|
if (p1.x() == p2.x()) |
|
{ |
|
if (!box.contains(p1)) |
|
p1.y() = box.top(); |
|
if (!box.contains(p2)) |
|
p2.y() = box.bottom(); |
|
} |
|
else if (p1.y() == p2.y()) |
|
{ |
|
if (!box.contains(p1)) |
|
p1.x() = box.left(); |
|
if (!box.contains(p2)) |
|
p2.x() = box.right(); |
|
} |
|
else |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const dlib::vector<T,2> d = p1-p2; |
|
double alpha1 = (box.left() -p2.x())/(double)d.x(); |
|
double alpha2 = (box.right() -p2.x())/(double)d.x(); |
|
double alpha3 = (box.top() -p2.y())/(double)d.y(); |
|
double alpha4 = (box.bottom()-p2.y())/(double)d.y(); |
|
|
|
const dlib::vector<T,2> c1 = alpha1*d + p2; |
|
const dlib::vector<T,2> c2 = alpha2*d + p2; |
|
const dlib::vector<T,2> c3 = alpha3*d + p2; |
|
const dlib::vector<T,2> c4 = alpha4*d + p2; |
|
|
|
if (!box.contains(p1)) |
|
p1 = c1; |
|
if (!box.contains(p2)) |
|
p2 = c2; |
|
if (box.contains(c3)) |
|
{ |
|
if (!box.contains(p2)) |
|
p2 = c3; |
|
else if (!box.contains(p1)) |
|
p1 = c3; |
|
} |
|
if (box.contains(c4)) |
|
{ |
|
if (!box.contains(p2)) |
|
p2 = c4; |
|
else if (!box.contains(p1)) |
|
p1 = c4; |
|
} |
|
} |
|
|
|
p1 = nearest_point(box, p1); |
|
p2 = nearest_point(box, p2); |
|
} |
|
|
|
|
|
|
|
inline const rectangle centered_rect ( |
|
const point& p, |
|
unsigned long width, |
|
unsigned long height |
|
) |
|
{ |
|
return centered_rect(p.x(),p.y(),width,height); |
|
} |
|
|
|
|
|
|
|
inline std::vector<rectangle> centered_rects ( |
|
const std::vector<point>& pts, |
|
unsigned long width, |
|
unsigned long height |
|
) |
|
{ |
|
std::vector<rectangle> tmp; |
|
tmp.reserve(pts.size()); |
|
for (auto& p : pts) |
|
tmp.emplace_back(centered_rect(p, width, height)); |
|
return tmp; |
|
} |
|
|
|
|
|
|
|
inline const rectangle centered_rect ( |
|
const rectangle& rect, |
|
unsigned long width, |
|
unsigned long height |
|
) |
|
{ |
|
return centered_rect((rect.left()+rect.right())/2, (rect.top()+rect.bottom())/2, width, height); |
|
} |
|
|
|
|
|
|
|
inline const rectangle shrink_rect ( |
|
const rectangle& rect, |
|
long num |
|
) |
|
{ |
|
return rectangle(rect.left()+num, rect.top()+num, rect.right()-num, rect.bottom()-num); |
|
} |
|
|
|
|
|
|
|
inline const rectangle grow_rect ( |
|
const rectangle& rect, |
|
long num |
|
) |
|
{ |
|
return shrink_rect(rect, -num); |
|
} |
|
|
|
|
|
|
|
inline const rectangle shrink_rect ( |
|
const rectangle& rect, |
|
long width, |
|
long height |
|
) |
|
{ |
|
return rectangle(rect.left()+width, rect.top()+height, rect.right()-width, rect.bottom()-height); |
|
} |
|
|
|
|
|
|
|
inline const rectangle grow_rect ( |
|
const rectangle& rect, |
|
long width, |
|
long height |
|
) |
|
{ |
|
return shrink_rect(rect, -width, -height); |
|
} |
|
|
|
|
|
|
|
inline const rectangle scale_rect ( |
|
const rectangle& rect, |
|
double scale |
|
) |
|
{ |
|
DLIB_ASSERT(scale > 0, "scale factor must be > 0"); |
|
|
|
long l = (long)std::round(rect.left()*scale); |
|
long t = (long)std::round(rect.top()*scale); |
|
long r = (long)std::round(rect.right()*scale); |
|
long b = (long)std::round(rect.bottom()*scale); |
|
return rectangle(l, t, r, b); |
|
} |
|
|
|
|
|
|
|
inline const rectangle translate_rect ( |
|
const rectangle& rect, |
|
const point& p |
|
) |
|
{ |
|
rectangle result; |
|
result.set_top ( rect.top() + p.y() ); |
|
result.set_bottom ( rect.bottom() + p.y() ); |
|
result.set_left ( rect.left() + p.x() ); |
|
result.set_right ( rect.right() + p.x() ); |
|
return result; |
|
} |
|
|
|
|
|
|
|
inline const rectangle translate_rect ( |
|
const rectangle& rect, |
|
long x, |
|
long y |
|
) |
|
{ |
|
rectangle result; |
|
result.set_top ( rect.top() + y ); |
|
result.set_bottom ( rect.bottom() + y ); |
|
result.set_left ( rect.left() + x ); |
|
result.set_right ( rect.right() + x ); |
|
return result; |
|
} |
|
|
|
|
|
|
|
inline const rectangle resize_rect ( |
|
const rectangle& rect, |
|
unsigned long width, |
|
unsigned long height |
|
) |
|
{ |
|
return rectangle(rect.left(),rect.top(), |
|
rect.left()+width-1, |
|
rect.top()+height-1); |
|
} |
|
|
|
|
|
|
|
inline const rectangle resize_rect_width ( |
|
const rectangle& rect, |
|
unsigned long width |
|
) |
|
{ |
|
return rectangle(rect.left(),rect.top(), |
|
rect.left()+width-1, |
|
rect.bottom()); |
|
} |
|
|
|
|
|
|
|
inline const rectangle resize_rect_height ( |
|
const rectangle& rect, |
|
unsigned long height |
|
) |
|
{ |
|
return rectangle(rect.left(),rect.top(), |
|
rect.right(), |
|
rect.top()+height-1); |
|
} |
|
|
|
|
|
|
|
inline const rectangle move_rect ( |
|
const rectangle& rect, |
|
const point& p |
|
) |
|
{ |
|
return rectangle(p.x(), p.y(), p.x()+rect.width()-1, p.y()+rect.height()-1); |
|
} |
|
|
|
|
|
|
|
inline const rectangle move_rect ( |
|
const rectangle& rect, |
|
long x, |
|
long y |
|
) |
|
{ |
|
return rectangle(x, y, x+rect.width()-1, y+rect.height()-1); |
|
} |
|
|
|
|
|
|
|
inline rectangle set_rect_area ( |
|
const rectangle& rect, |
|
unsigned long area |
|
) |
|
{ |
|
DLIB_ASSERT(area > 0); |
|
|
|
if (rect.area() == 0) |
|
{ |
|
|
|
|
|
unsigned long scale = std::round(std::sqrt(area)); |
|
return centered_rect(rect, scale, scale); |
|
} |
|
else |
|
{ |
|
double scale = std::sqrt(area/(double)rect.area()); |
|
return centered_rect(rect, (long)std::round(rect.width()*scale), (long)std::round(rect.height()*scale)); |
|
} |
|
} |
|
|
|
|
|
|
|
inline rectangle set_aspect_ratio ( |
|
const rectangle& rect, |
|
double ratio |
|
) |
|
{ |
|
DLIB_ASSERT(ratio > 0, |
|
"\t rectangle set_aspect_ratio()" |
|
<< "\n\t ratio: " << ratio |
|
); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (ratio >= 1) |
|
{ |
|
const long h = static_cast<long>(std::sqrt(rect.area()/ratio) + 0.5); |
|
const long w = static_cast<long>(h*ratio + 0.5); |
|
return centered_rect(rect, w, h); |
|
} |
|
else |
|
{ |
|
const long w = static_cast<long>(std::sqrt(rect.area()*ratio) + 0.5); |
|
const long h = static_cast<long>(w/ratio + 0.5); |
|
return centered_rect(rect, w, h); |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename T |
|
> |
|
inline const rectangle get_rect ( |
|
const T& m |
|
) |
|
{ |
|
return rectangle(0, 0, num_columns(m)-1, num_rows(m)-1); |
|
} |
|
|
|
|
|
|
|
inline rectangle operator+ ( |
|
const rectangle& r, |
|
const point& p |
|
) |
|
{ |
|
return r + rectangle(p); |
|
} |
|
|
|
|
|
|
|
inline rectangle operator+ ( |
|
const point& p, |
|
const rectangle& r |
|
) |
|
{ |
|
return r + rectangle(p); |
|
} |
|
|
|
|
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|