00001 /*!@file Image/Rectangle.H A basic rectangle class */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Image/Rectangle.H $ 00035 // $Id: Rectangle.H 13892 2010-09-08 16:49:40Z lior $ 00036 // 00037 00038 #ifndef RECTANGLE_H_DEFINED 00039 #define RECTANGLE_H_DEFINED 00040 00041 #include "Image/Dims.H" 00042 #include "Image/Point2D.H" 00043 #include "Util/Assert.H" 00044 00045 #include <algorithm> 00046 #include <string> // for string conversions 00047 00048 //! A basic rectangle class 00049 /*! This is a shorthand class to represent a 2D rectangle, as defined 00050 by integer top, left, bottom and right coordinates. The goal of 00051 this object is not to have to check if left < right, etc... for 00052 each function using a Rectangle. The object checks for that at its 00053 creation, and is always internally coherent. Image convention: 00054 top < bottom, left < right. 00055 00056 There is often some ambiguity about how to represent the 00057 left-right and top-bottom ranges involved in a rectangle. Imagine 00058 a pixel grid like this: 00059 00060 +---+---+---+---+ 00061 | | | | | 00062 | | | | | 00063 +---+---+---+---+ 00064 | | | | | 00065 | | | | | 00066 +---+---+---+---+ 00067 | | | | | 00068 | | | | | 00069 +---+---+---+---+ 00070 | | | | | 00071 | | | | | 00072 +---+---+---+---+ 00073 00074 00075 If we want a Rectangle that covers this whole 4x4 grid, how should 00076 we represent the coordinates? Clearly the upper left corner is 00077 (0,0), but what about the bottom right corner? One answer would be 00078 that the lower right corner is (4,4), in which we imagine the 00079 coordinate numbers falling between the cracks of the pixels: 00080 00081 . 0...1...2...3...4 00082 0 +---+---+---+---+ 00083 . | | | | | 00084 . | | | | | 00085 1 +---+---+---+---+ 00086 . | | | | | 00087 . | | | | | 00088 2 +---+---+---+---+ 00089 . | | | | | 00090 . | | | | | 00091 3 +---+---+---+---+ 00092 . | | | | | 00093 . | | | | | 00094 4 +---+---+---+---+ 00095 00096 Another answer is that the lower right corner should be labeled as 00097 (3,3), if we envision the coordinate numbers labeling the pixel 00098 centers, and this approach is more natural if we want to draw a 00099 line just inside the border of the rectangle as shown by the '*' 00100 here: 00101 00102 . ..0...1...2...3.. 00103 . +---+---+---+---+ 00104 0 |***|***|***|***| 00105 . |***|***|***|***| 00106 . +---+---+---+---+ 00107 1 |***| | |***| 00108 . |***| | |***| 00109 . +---+---+---+---+ 00110 2 |***| | |***| 00111 . |***| | |***| 00112 . +---+---+---+---+ 00113 3 |***|***|***|***| 00114 . |***|***|***|***| 00115 . +---+---+---+---+ 00116 00117 In the Rectangle class we support both approaches; the (4,4) 00118 coords are called 'outer' coordinates and corresponding functions 00119 have a 'O' suffix (bottomO(), right(), and tlbrO()), while the 00120 (3,3) coords are called 'inner' coordinates and corresponding 00121 functions have an 'I' suffix (bottomI(), rightI(), and tlbrI()). 00122 00123 The internal representation of Rectangle is based on outer 00124 coordinates, but this has no effect on users of the class who are 00125 free to user whichever of inner or outer coords are more 00126 convenient for their purposes. 00127 00128 Outer coordinates are more natural for uniformly scaling a 00129 rectangle up or down by a factor. For example, if we want to scale 00130 the 4x4 rect up by a factor of 2 giving an 8x8 rect, then in outer 00131 coords we just do newright=right*2=4*2=8 and 00132 newbottom=bottom*2=4*2=8. But with inner coords we can't just 00133 multiply by 2: the inner bottom-right coords are (3,3), and if we 00134 scale those by 2 we get (6,6) which represents a 7x7 rectangle 00135 rather than the correct 8x8 result. 00136 00137 HISTORICAL NOTE (2007-Mar-20): The Rectangle class previously used 00138 only inner coordinates both for its implementation and in its 00139 public interface. This lead to clunky code in some places where 00140 many -1 or +1 offsets were needed to handle rectangle coordinates. 00141 */ 00142 00143 class Rectangle { 00144 00145 //! Private constructor; use the tlbrO() pseudo-constructor if you want to call this publicly 00146 inline Rectangle(int tt, int ll, int bb1, int rr1); 00147 00148 public: 00149 // ############################################################ 00150 // ##### Constructors & Destructors: 00151 // ############################################################ 00152 00153 //! Build from 4 coordinates (top, left, inner-bottom, inner-right) 00154 static inline Rectangle tlbrI(int tt, int ll, int bb, int rr); 00155 00156 //! Build from 4 coordinates (top, left, outer-bottom, outer-right) 00157 static inline Rectangle tlbrO(int tt, int ll, int bb, int rr); 00158 00159 //! Build from center loc and dims 00160 static inline Rectangle centerDims(const Point2D<int>& center, const Dims& dims); 00161 00162 //! Build from a top-left corner and some dims 00163 inline Rectangle(const Point2D<int>& topleft, const Dims& dims); 00164 00165 //! Copy constructor 00166 inline Rectangle(const Rectangle& rect); 00167 00168 //! Uninitialized constructor (useful for arrays). 00169 inline Rectangle(); 00170 00171 // Default assignment operator ok 00172 00173 // ############################################################ 00174 // ##### Access functions: 00175 // ############################################################ 00176 00177 //! get overlap between this rectangle and passed in rectangle 00178 inline Rectangle getOverlap(const Rectangle& r2) const; 00179 00180 //! compute overlap as area of intersection / area of union 00181 inline double getOverlapRatio(const Rectangle& r2) const; 00182 00183 //! check if point p is within rectangle 00184 /*! CAUTION: boundaries are included. */ 00185 inline bool contains(const Point2D<int>& p) const; 00186 00187 //! check if this rect contains another 00188 /*! The boundaries are allowed to coincide, such that by this 00189 definition a rectangle "contains" itself. */ 00190 inline bool contains(const Rectangle& r) const; 00191 00192 inline int top() const; //!< get (inner) top coordinate 00193 inline int bottomI() const; //!< get inner bottom coordinate (=top+height-1) 00194 inline int bottomO() const; //!< get outer bottom coordinate (=top+height) 00195 inline int left() const; //!< get (inner) left coordinate 00196 inline int rightI() const; //!< get inner right coordinate (=left+width-1) 00197 inline int rightO() const; //!< get outer right coordinate (=left+width) 00198 00199 inline Point2D<int> topLeft() const; //!< Get (outer) top left corner point 00200 inline Point2D<int> topRight() const; //!< Get (outer) top right corner point 00201 inline Point2D<int> bottomLeft() const; //!< Get (outer) bottom left corner point 00202 inline Point2D<int> bottomRight() const; //!< Get (outer) bottom right corner point 00203 00204 //! Get the rectangle's width, height 00205 inline Dims dims() const; 00206 00207 //! Access some metric info 00208 inline int width() const; 00209 //! Access some metric info 00210 inline int height() const; 00211 //! Access some metric info 00212 inline int area() const; 00213 //! Access some metric info 00214 inline float aspect() const; 00215 00216 //!The center of the rectangle 00217 inline Point2D<int> center() const; 00218 00219 //! whether the rectangle is valid 00220 inline bool isValid() const; 00221 00222 private: 00223 inline void tlbrInitOuter(int tt, int ll, int bb1, int rr1); 00224 00225 bool valid; //!< True if Rectangle contains something coherent 00226 int t, b1, l, r1; //!< top, bottom, left, right 00227 }; 00228 00229 00230 // ###################################################################### 00231 // Rectangle FREE FUNCTIONS: 00232 // ###################################################################### 00233 00234 // ###################################################################### 00235 //! Rectangle overload: format is "<int>,<int>,<int>,<int>" 00236 /*! Format corresponds to "xtopleft,ytopleft,xbottomright,ybottomright" */ 00237 std::string convertToString(const Rectangle& val); 00238 00239 // ###################################################################### 00240 //! Rectangle overload: format is "<int>,<int>,<int>,<int>" 00241 void convertFromString(const std::string& str, Rectangle& val); 00242 00243 00244 // ###################################################################### 00245 //! Return a new rectangle that is a constrained version of the input. 00246 /*! @param in The original rectangle on which to base the constrained 00247 output. 00248 00249 @param bounds The output rectangle will be constrained to be 00250 contained within these bounds. 00251 00252 @param minw The minimum width for the output rectangle. Caller 00253 must ensure minw>=0. If the output rectangle would naturally be 00254 narrower than this width, then it will be widened to the smaller 00255 of minw and bounds.width(). 00256 00257 @param maxw The maximum width for the output rectangle. Caller 00258 must ensure maxw>=minw. If the output rectangle would naturally be 00259 wider than this width, then it will be narrowed to the smaller of 00260 maxw and bounds.width(). 00261 00262 @param minh The minimum height for the output rectangle. Caller 00263 must ensure minh>=0. If the output rectangle would naturally be 00264 shorter than this height, then it will be lengthened to the 00265 smaller of minh and bounds.height(). 00266 00267 @param maxh The maximum height for the output rectangle. Caller 00268 must ensure maxh>=minh. If the output rectangle would naturally be 00269 taller than this height, then it will be shortened to the smaller 00270 of maxh and bounds.height(). 00271 00272 @return The result is guaranteed to be such that the following 00273 conditions are all true: 00274 \code 00275 bounds.contains(result); 00276 result.width() >= std::min(minw, bounds.width()); 00277 result.width() <= std::min(maxw, bounds.width()); 00278 result.height() >= std::min(minh, bounds.height()); 00279 result.height() <= std::min(maxh, bounds.height()); 00280 \endcode 00281 */ 00282 Rectangle constrainRect(const Rectangle& in, 00283 const Rectangle& bounds, 00284 int minw, int maxw, 00285 int minh, int maxh); 00286 00287 00288 // ###################################################################### 00289 //! Like the main constrainRect(), but use the same limits for width as for height 00290 inline Rectangle constrainRect(const Rectangle& in, 00291 const Rectangle& bounds, 00292 int minsz, int maxsz) 00293 { 00294 return constrainRect(in, bounds, minsz, maxsz, minsz, maxsz); 00295 } 00296 00297 // ###################################################################### 00298 inline bool operator==(const Rectangle& r1, const Rectangle& r2) 00299 { 00300 if (!r1.isValid() || !r2.isValid()) return r1.isValid() == r2.isValid(); 00301 00302 return (r1.top() == r2.top() && 00303 r1.bottomO() == r2.bottomO() && 00304 r1.left() == r2.left() && 00305 r1.rightO() == r2.rightO()); 00306 } 00307 00308 // ###################################################################### 00309 inline Rectangle operator*(const Rectangle& r, const double s) 00310 { 00311 if (!r.isValid()) 00312 return Rectangle(); 00313 else 00314 return Rectangle::tlbrO(int(r.top() * s), 00315 int(r.left() * s), 00316 int(r.bottomO() * s), 00317 int(r.rightO() * s)); 00318 } 00319 00320 // ###################################################################### 00321 inline Rectangle operator*(const Rectangle& r, const int s) 00322 { 00323 if (!r.isValid()) 00324 return Rectangle(); 00325 else 00326 return Rectangle::tlbrO(r.top() * s, 00327 r.left() * s, 00328 r.bottomO() * s, 00329 r.rightO() * s); 00330 } 00331 00332 // ###################################################################### 00333 inline Rectangle operator/(const Rectangle& r, const int div) 00334 { 00335 if (!r.isValid() || div == 0) 00336 return Rectangle(); 00337 else 00338 return Rectangle::tlbrO(r.top() / div, 00339 r.left() / div, 00340 r.bottomO() / div, 00341 r.rightO() / div); 00342 } 00343 00344 // ###################################################################### 00345 inline Rectangle operator+(const Rectangle& r, const Point2D<int>& p) 00346 { 00347 return Rectangle::tlbrO(r.top() + p.j, 00348 r.left() + p.i, 00349 r.bottomO() + p.j, 00350 r.rightO() + p.i); 00351 } 00352 00353 // ###################################################################### 00354 inline Rectangle& operator+=(Rectangle& r, const Point2D<int>& p) 00355 { 00356 return (r = r + p); 00357 } 00358 00359 // ###################################################################### 00360 inline Rectangle operator-(const Rectangle& r, const Point2D<int>& p) 00361 { 00362 return Rectangle::tlbrO(r.top() - p.j, 00363 r.left() - p.i, 00364 r.bottomO() - p.j, 00365 r.rightO() - p.i); 00366 } 00367 00368 // ###################################################################### 00369 inline Rectangle& operator-=(Rectangle& r, const Point2D<int>& p) 00370 { 00371 return (r = r - p); 00372 } 00373 00374 // ###################################################################### 00375 // Rectangle INLINE MEMBER FUNCTIONS: 00376 // ###################################################################### 00377 00378 // ###################################################################### 00379 inline Rectangle::Rectangle(int tt, int ll, int bb1, int rr1) 00380 { tlbrInitOuter(tt, ll, bb1, rr1); } 00381 00382 // ###################################################################### 00383 inline Rectangle Rectangle::tlbrI(int tt, int ll, int bb, int rr) 00384 { return Rectangle(tt, ll, bb+1, rr+1); } 00385 00386 // ###################################################################### 00387 inline Rectangle Rectangle::tlbrO(int tt, int ll, int bb, int rr) 00388 { return Rectangle(tt, ll, bb, rr); } 00389 00390 // ###################################################################### 00391 inline Rectangle Rectangle::centerDims(const Point2D<int>& center, const Dims& d) 00392 { return Rectangle(Point2D<int>(center.i - d.w()/2, center.j - d.h()/2), d); } 00393 00394 // ###################################################################### 00395 inline Rectangle::Rectangle(const Point2D<int>& topleft, const Dims& d) 00396 : valid(true), 00397 t(topleft.j), b1(topleft.j+d.h()), 00398 l(topleft.i), r1(topleft.i+d.w()) 00399 {} 00400 00401 // ###################################################################### 00402 inline Rectangle::Rectangle(const Rectangle& rect) 00403 : valid(rect.valid), t(rect.t), b1(rect.b1), l(rect.l), r1(rect.r1) 00404 {} 00405 00406 // ###################################################################### 00407 inline Rectangle::Rectangle() 00408 : valid(false), t(-1), b1(-1), l(-1), r1(-1) 00409 {} 00410 00411 // ###################################################################### 00412 inline void Rectangle::tlbrInitOuter(int tt, int ll, int bb1, int rr1) 00413 { 00414 if (bb1 - tt < 0) { t = bb1; b1 = tt; } else { t = tt; b1 = bb1; } 00415 if (rr1 - ll < 0) { l = rr1; r1 = ll; } else { l = ll; r1 = rr1; } 00416 valid = true; 00417 } 00418 00419 // ###################################################################### 00420 inline Rectangle Rectangle::getOverlap(const Rectangle& r2) const 00421 { 00422 if (!this->isValid() || !r2.isValid()) return Rectangle(); 00423 00424 const int le = std::max(l, r2.left()); 00425 const int ri = std::min(r1, r2.rightO()); 00426 const int to = std::max(t, r2.top()); 00427 const int bo = std::min(b1, r2.bottomO()); 00428 00429 if (le >= ri || to >= bo) return Rectangle(); 00430 else return Rectangle::tlbrO(to, le, bo, ri); 00431 } 00432 00433 // ###################################################################### 00434 inline double Rectangle::getOverlapRatio(const Rectangle& r2) const 00435 { 00436 00437 Rectangle ovR = getOverlap(r2); 00438 00439 double ov = 0; 00440 if (ovR.isValid()) 00441 { 00442 //compute overlap as area of intersection / area of union 00443 double ua = (area()+1) + (r2.area()+1) - ovR.area(); 00444 ov = (double)ovR.area()/ua; 00445 } 00446 00447 return ov; 00448 } 00449 00450 // ###################################################################### 00451 inline int Rectangle::area() const 00452 { 00453 return width() * height(); 00454 } 00455 00456 // ###################################################################### 00457 inline float Rectangle::aspect() const 00458 { 00459 return (float)width() / (float)height(); 00460 } 00461 00462 // ###################################################################### 00463 inline Point2D<int> Rectangle::center() const 00464 { 00465 return Point2D<int>(l+(r1-l)/2, t+(b1-t)/2); 00466 //int t, b1, l, r1; //!< top, bottom, left, right 00467 } 00468 00469 // ###################################################################### 00470 inline bool Rectangle::contains(const Point2D<int>& p) const 00471 { 00472 return (p.i >= l && p.i < r1 && p.j >= t && p.j < b1); 00473 } 00474 00475 // ###################################################################### 00476 inline bool Rectangle::contains(const Rectangle& r) const 00477 { 00478 return ( r.l >= this->l 00479 && r.t >= this->t 00480 && r.r1 <= this->r1 00481 && r.b1 <= this->b1); 00482 } 00483 00484 // ###################################################################### 00485 inline int Rectangle::top() const 00486 { ASSERT(valid); return t; } 00487 00488 inline int Rectangle::bottomI() const 00489 { ASSERT(valid); return b1-1; } 00490 00491 inline int Rectangle::bottomO() const 00492 { ASSERT(valid); return b1; } 00493 00494 inline int Rectangle::left() const 00495 { ASSERT(valid); return l; } 00496 00497 inline int Rectangle::rightI() const 00498 { ASSERT(valid); return r1-1; } 00499 00500 inline int Rectangle::rightO() const 00501 { ASSERT(valid); return r1; } 00502 00503 inline Point2D<int> Rectangle::topLeft() const 00504 { ASSERT(valid); return Point2D<int>(l, t); } 00505 00506 inline Point2D<int> Rectangle::topRight() const 00507 { ASSERT(valid); return Point2D<int>(r1, t); } 00508 00509 inline Point2D<int> Rectangle::bottomLeft() const 00510 { ASSERT(valid); return Point2D<int>(l, b1); } 00511 00512 inline Point2D<int> Rectangle::bottomRight() const 00513 { ASSERT(valid); return Point2D<int>(r1, b1); } 00514 00515 inline Dims Rectangle::dims() const 00516 { 00517 ASSERT(valid); 00518 return Dims(r1 - l, b1 - t); // *** CAUTION: BOUNDARIES ARE INCLUDED 00519 } 00520 00521 inline int Rectangle::width() const 00522 { ASSERT(valid); return r1 - l; } // *** CAUTION: BOUNDARIES ARE INCLUDED 00523 00524 inline int Rectangle::height() const 00525 { ASSERT(valid); return b1 - t; } // *** CAUTION: BOUNDARIES ARE INCLUDED 00526 00527 inline bool Rectangle::isValid() const 00528 { return valid; } 00529 00530 #endif 00531 00532 // ###################################################################### 00533 /* So things look consistent in everyone's emacs... */ 00534 /* Local Variables: */ 00535 /* indent-tabs-mode: nil */ 00536 /* End: */