00001 // -*- mode: text; fill-column: 70; indent-tabs-mode: nil -*- 00002 // //////////////////////////////////////////////////////////////////// // 00003 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00004 // University of Southern California (USC) and the iLab at USC. // 00005 // See http://iLab.usc.edu for information about this project. // 00006 // //////////////////////////////////////////////////////////////////// // 00007 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00008 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00009 // in Visual Environments, and Applications'' by Christof Koch and // 00010 // Laurent Itti, California Institute of Technology, 2001 (patent // 00011 // pending; filed July 23, 2001, following provisional applications // 00012 // No. 60/274,674 filed March 8, 2001 and 60/288,724 filed May 4, 2001).// 00013 // //////////////////////////////////////////////////////////////////// // 00014 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00015 // // 00016 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00017 // redistribute it and/or modify it under the terms of the GNU General // 00018 // Public License as published by the Free Software Foundation; either // 00019 // version 2 of the License, or (at your option) any later version. // 00020 // // 00021 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00022 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00023 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00024 // PURPOSE. See the GNU General Public License for more details. // 00025 // // 00026 // You should have received a copy of the GNU General Public License // 00027 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00028 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00029 // Boston, MA 02111-1307 USA. // 00030 // //////////////////////////////////////////////////////////////////// // 00031 // 00032 // Primary maintainer for this file: Rob Peters <rjpeters@klab.caltech.edu> 00033 // $Id: about-image.dxy 6359 2006-03-13 01:47:10Z rjpeters $ 00034 // 00035 00036 // ###################################################################### 00037 // ###################################################################### 00038 00039 /*! 00040 00041 \page about-image About the Image class 00042 00043 \note Some of this documentation was originally written at the time 00044 that we converted the Image class to have a ref-counted interface and 00045 moved functionality from member functions to free functions; this is 00046 the source of references to "new" and "old" styles. 00047 00048 <!--############################################################--> 00049 <!--############################################################--> 00050 <!--############################################################--> 00051 00052 \section refcounting Ref-counting and copy-on-write 00053 00054 \subsection refcounting-overview How ref-counting works, in four parts: 00055 00056 - <tt>Dims</tt> represents the width and height dimensions: 00057 \code 00058 struct Dims 00059 { 00060 int const ww; // the width 00061 int const hh; // the height 00062 }; 00063 \endcode 00064 00065 - <tt>ArrayData</tt> encapsulates the management of a 2-D data array 00066 \code 00067 template <class T> 00068 class ArrayData 00069 { 00070 long itsRefCount; // reference count, is managed by ArrayHandle 00071 Dims const itsDims; // the width and height, fixed at construction 00072 T* const itsData; // the data array, fixed at construction 00073 00074 public: 00075 // ... constructors/destructor omitted 00076 00077 const T* data() const { return itsData; } // const version 00078 T* dataw() { return itsData; } // non-const version 00079 00080 ArrayData* clone() const { return new ArrayData(*this); } 00081 00082 void incrRefCount() const { ++itsRefCount; } 00083 00084 long decrRefCount() const { return (--itsRefCount); } 00085 00086 bool isShared() const { return (itsRefCount > 1); } 00087 }; 00088 \endcode 00089 00090 - <tt>ArrayHandle</tt> offers ref-counted, copy-on-write access to ArrayData 00091 \code 00092 template <class T> 00093 class ArrayHandle 00094 { 00095 ArrayData<T>* px; // pointer to our data 00096 00097 public: 00098 // ... constructors/destructor omitted 00099 00100 // REF-COUNTING: 00101 00102 ArrayHandle(const ArrayHandle& other) : 00103 px(other.px) 00104 { 00105 px->incrRefCount(); // just share other's data, and bump the ref-count 00106 } 00107 00108 ~ArrayHandle() 00109 { 00110 if (px->decrRefCount() == 0) // drop the ref-count, and if we were the 00111 // last user, then delete the ArrayData 00112 delete px; 00113 } 00114 00115 // COPY-ON-WRITE: 00116 00117 const ArrayData<T>& get() const 00118 { 00119 return *px; // const version just returns the ArrayData 00120 } 00121 00122 ArrayData<T>& uniq() 00123 { 00124 if (px->isShared()) // non-const version checks if the ArrayData 00125 // is shared, and makes a fresh copy if needed 00126 { 00127 *this = ArrayHandle(px->clone()); 00128 } 00129 00130 return *px; 00131 } 00132 }; 00133 \endcode 00134 00135 - Image wraps an ArrayHandle, and offers an expanded interface: 00136 \code 00137 template <class T> 00138 class Image 00139 { 00140 ArrayHandle<T> itsHdl; 00141 00142 const ArrayData<T>& impl() const { return itsHdl.get(); } 00143 ArrayData<T>& uniq() { return itsHdl.uniq(); } 00144 00145 public: 00146 int getWidth() const { return impl().w(); } 00147 int getHeight() const { return impl().h(); } 00148 00149 void setVal(int index, const T value) 00150 { 00151 uniq().dataw()[index] = value; 00152 } 00153 }; 00154 \endcode 00155 00156 \subsection refcounting-syntax Ref-counting usage syntax 00157 00158 - Since copying is cheap, an Image<T> can be the return value of a 00159 function... 00160 \code 00161 template <class T>void coolFilter(const Image<T>& src, Image<T>& result); 00162 00163 template <class T> 00164 Image<T> hotFilter(const Image<T>& src); 00165 00166 template <class T> 00167 void foo(const Image<T>& x) 00168 { 00169 Image<T> coolResult; 00170 coolFilter(x, coolResult); 00171 00172 Image<T> hotResult = hotFilter(x); 00173 } 00174 \endcode 00175 00176 - ...and chaining operations is easier: 00177 \code 00178 template <class T> 00179 void foo(const Image<T>& x) 00180 { 00181 Image<T> tmpResult, coolResult; 00182 coolFilter1(x, tmpResult); 00183 coolFilter2(tmpResult, coolResult); 00184 00185 Image<T> hotResult = hotFilter2(hotFilter1(x)); 00186 } 00187 00188 \endcode 00189 00190 \subsection refcounting-avoid-copies Ref-counting helps avoid copies 00191 - We only have to deep copy the data when absolutely necessary. 00192 For instance, if we are converting an Image<T> of unknown type to Image<byte>: 00193 \code 00194 template <class T> 00195 void process(const Image<T>& x) 00196 { 00197 Image<byte> xbyte = x; // without ref-counting, this always does a deep copy 00198 // with ref-counting, avoids copying if T is byte 00199 } 00200 \endcode 00201 00202 \subsection refcounting-reduce-bugs Ref-counting helps reduce bugs 00203 - Since memory management is encapsulated 00204 within ArrayData and ArrayHandle, "ordinary" functions should not have 00205 to call <tt>new[]</tt>/<tt>delete[]</tt> to manage temporary storage 00206 \code 00207 template <class T> 00208 void Image<T>::coolDraw() 00209 { 00210 T* buf = new T[w*h]; // get temporary storage 00211 00212 // ... fill in buf ... 00213 00214 T* old = a; // clean up 00215 a = buf; 00216 delete [] old; 00217 } 00218 00219 template <class T> 00220 void Image<T>::hotDraw() 00221 { 00222 Image<T> tmp; 00223 00224 // ... fill in tmp ... 00225 00226 this->swap(tmp); 00227 // or 00228 *this = tmp; 00229 } 00230 00231 \endcode 00232 00233 \subsection refcounting-exception-safety Ref-counting helps with exception safety 00234 - if an exception is thrown while filling in <tt>buf</tt> in 00235 <tt>coolDraw()</tt>, then we leak memory 00236 - but if an exception is thrown while filling in <tt>tmp</tt> in 00237 <tt>hotDraw()</tt>, then <tt>~Image()</tt> will clean up <tt>tmp</tt> for us 00238 00239 \subsection refcounting-gotchas Ref-counting "gotchas" 00240 - non-const functions must check uniqueness each time they are called 00241 - in particular, it is not especially efficient to call Image::setVal() in 00242 the middle of some inner loop, since 00243 - Image::setVal() must check Image::coordsOk() each time it is called, and 00244 - Image::setVal() must check uniqueness each time it is called 00245 - therefore, in time-critical loops, instead of this: 00246 \code 00247 Image<float> a; 00248 for (int y = 0; y < a.getHeight(); ++y) 00249 for (int x = 0; x < a.getWidth(); ++x) 00250 a.setVal(x,y,42); 00251 \endcode 00252 - try this if you like <tt>while</tt> loops: 00253 \code 00254 Image<float>::iterator itr = a.beginw(), stop = a.endw(); 00255 while (itr != stop) 00256 *itr++ = 42; 00257 \endcode 00258 - or this if you like <tt>for</tt> loops: 00259 \code 00260 for(Image<float>::iterator itr = a.beginw(), stop = a.endw(); 00261 itr != stop; 00262 ++itr) 00263 { 00264 *itr = 42; 00265 } 00266 \endcode 00267 00268 00269 00270 00271 <!--############################################################--> 00272 <!--############################################################--> 00273 <!--############################################################--> 00274 00275 \section iterators Image iterators 00276 00277 \subsection iterators-review Quick iterator review 00278 00279 - Iterators act like pointers 00280 - All standard library containers provide iterators 00281 \code 00282 #include <vector> 00283 #include <iostream> 00284 00285 void foo() 00286 { 00287 std::vector<int> myvec; 00288 00289 // fill in with some data 00290 for (int i = 0; i < 10; ++i) 00291 myvec.push_back(i); 00292 00293 // print out the contents using iterators 00294 for (std::vector<int>::const_iterator 00295 itr = myvec.begin(), 00296 stop = myvec.end(); 00297 itr != stop; 00298 ++itr) 00299 { 00300 std::cout << *itr << std::endl; 00301 } 00302 } 00303 \endcode 00304 00305 - Iterators can be used with generic algorithms: 00306 \code 00307 template <class Iterator> 00308 void printContents(Iterator itr, Iterator stop) 00309 { 00310 while (itr != stop) 00311 std::cout << *itr++ << std::endl; 00312 } 00313 00314 void foo() 00315 { 00316 std::vector<int> myvec; 00317 00318 // ... fill in with some data ... 00319 00320 // print the contents 00321 printContents(myvec.begin(), myvec.end()); 00322 } 00323 00324 \endcode 00325 00326 \subsection image-iterators-version Image iterators: normal and debug versions 00327 00328 - Normal (non-debug) iterators are just pointers: 00329 \code 00330 template <class T> 00331 class Image 00332 { 00333 public: 00334 #ifndef INVT_MEM_DEBUG 00335 00336 typedef T* iterator; 00337 typedef const T* const_iterator; 00338 00339 const_iterator begin() const { return impl().data(); } 00340 const_iterator end() const { return impl().end(); } 00341 00342 iterator beginw() { return uniq().dataw(); } 00343 iterator endw() { return uniq().endw(); } 00344 #endif 00345 }; 00346 \endcode 00347 00348 - Debug iterators are objects that act like pointers, but check that the 00349 pointer is in bounds every time it is dereferenced. 00350 - To use debug iterators, first <tt>make clean</tt>, then re-run 00351 <tt>./configure</tt> with <tt>--enable-mem-debug</tt>, then re-run 00352 <tt>make all</tt>. 00353 00354 \code 00355 template <class T> 00356 class CheckedIterator // a checked iterator class 00357 { 00358 TT* ptr; 00359 TT* start; 00360 TT* stop; 00361 00362 // ... ++, --, <, >, <=, >=, +=, -=, all those goodies ... 00363 00364 TT* operator->() const 00365 { 00366 CheckedIteratorAux::ck_range_helper(ptr, start, stop); 00367 return ptr; 00368 } 00369 TT& operator*() const 00370 { 00371 CheckedIteratorAux::ck_range_helper(ptr, start, stop); 00372 return *ptr; 00373 } 00374 TT& operator[](diff_t d) const 00375 { 00376 CheckedIteratorAux::ck_range_helper(ptr+d, start, stop); 00377 return ptr[d]; 00378 } 00379 }; 00380 00381 template <class T> 00382 class Image 00383 { 00384 public: 00385 #ifndef INVT_MEM_DEBUG 00386 // ... normal iterators ... 00387 #else 00388 00389 typedef CheckedIterator<T> iterator; 00390 typedef CheckedIterator<const T> const_iterator; 00391 00392 const_iterator begin() const 00393 { return const_iterator(impl().data(), impl().end()); } 00394 00395 const_iterator end() const 00396 { return const_iterator(impl().end(), impl().end()); } 00397 00398 iterator beginw() { return iterator(uniq().dataw(), uniq().endw()); } 00399 iterator endw() { return iterator(uniq().endw(), uniq().endw()); } 00400 00401 #endif 00402 } 00403 \endcode 00404 00405 - Iterators in use with handmade loops: 00406 \code 00407 template<class T> 00408 void Image<T>::clear(const T& val) 00409 { 00410 for (Image<T>::iterator itr = this->beginw(), stop = this->endw(); 00411 itr != stop; ++itr) 00412 { 00413 *itr = val; 00414 } 00415 } 00416 \endcode 00417 00418 - Iterators in use with STL algorithms: 00419 \code 00420 #include <algorithm> // for std::count(), etc. 00421 #include <numeric> // for std::accumulate(), etc. 00422 00423 template <class T> 00424 int emptyArea(const Image<T>& img) 00425 { 00426 return std::count(img.begin(), img.end(), T()); 00427 } 00428 00429 template<class T> 00430 double getMean(const Image<T>& img) 00431 { 00432 const double sum = std::accumulate(img.begin(), img.end(), 0.0); 00433 00434 return sum / double(img.getSize()); 00435 } 00436 00437 \endcode 00438 00439 00440 00441 00442 <!--############################################################--> 00443 <!--############################################################--> 00444 <!--############################################################--> 00445 00446 \section free-functions Free functions for image operations 00447 00448 - Almost all image-related functions are free functions, rather than 00449 member functions, defined in separate <tt>.H</tt> files. Why do 00450 this? 00451 00452 - The best example is to see the Image class in analogy to the 00453 std::vector class. The std::vector class defines only the complete 00454 but minimal set of operations needed to use std::vector as a 00455 container. Any functions for application-specific <i>uses</i> of 00456 the std::vector class would be written as free functions. Likewise, 00457 the Image class provides the complete but minimal set of 00458 operations needed to use Image as a two-dimensional array of 00459 values; any domain-specific <i>uses</i> of the Image class (such 00460 as filtering, rescaling, concatenating, arithmetic) are written as 00461 free functions. 00462 - Since all operations can be coded in terms of iterators, most Image 00463 algorithms do not need access to the <tt>private</tt> parts of Image. 00464 - In some cases free functions offer a more natural mathematical syntax than 00465 member functions. 00466 - Keeping unrelated functions in separate files improves logical 00467 encapsulation and decreases compile-time dependencies. 00468 00469 <table> 00470 <tr> <td> <b>File name</b> <td><b>Contents</b> 00471 <tr> <td> <tt>Image/All.H</tt> <td> <tt>\#include</tt>s all other Image-related files 00472 <tr> <td> <tt>Image/ColorOps.H</tt> <td> color operations: get/set components, luminance, RGB-YIQ conversions, etc. 00473 <tr> <td> <tt>Image/FilterOps.H</tt> <td> <tt>lowPass*(), convolve*(), sepFilter*(),</tt> etc. 00474 <tr> <td> <tt>Image/MathOps.H</tt> <td> <tt>absDiff(), average(), takeMax(), RMSerr(),</tt> etc. 00475 <tr> <td> <tt>Image/Omni.H</tt> <td> all omni-directional related operations 00476 <tr> <td> <tt>Image/ShapeOps.H</tt> <td> <tt>rescale(), downSize(), concat*(), dec*(), int*(), crop()</tt> 00477 <tr> <td> <tt>Image/Transforms.H</tt> <td> <tt>segmentObject(), dct(), infoFFT(),</tt> etc. 00478 </table> 00479 00480 \subsection free-functions-syntax Free function syntax examples 00481 00482 \code 00483 void oldFoo(const Image<float>& x) 00484 { 00485 Image<float> filtered = x; 00486 filtered.lowPass3(true, true); 00487 } 00488 00489 #include "Image/FilterOps.H" 00490 00491 void newFoo(const Image<float>& x) 00492 { 00493 Image<float> filtered = lowPass3(x, true, true); 00494 } 00495 \endcode 00496 00497 \code 00498 void oldFoo(const Image<PixRGB<byte> >& x) 00499 { 00500 Image<float> lum; 00501 x.luminance(lum); 00502 } 00503 00504 #include "Image/ColorOps.H" 00505 00506 void newFoo(const Image<PixRGB<byte> >& x) 00507 { 00508 Image<float> lum = luminance(x); 00509 } 00510 \endcode 00511 00512 \code 00513 void oldFoo(const Image<byte>& x) 00514 { 00515 Image<float> smaller = x; 00516 smaller.rescale(x.getWidth()/2, x.getHeight()/2); 00517 } 00518 00519 #include "Image/ShapeOps.H" 00520 00521 void newFoo(const Image<byte>& x) 00522 { 00523 Image<float> smaller = rescale(x, x.getWidth()/2, x.getHeight()/2); 00524 } 00525 00526 \endcode 00527 00528 00529 00530 00531 <!--############################################################--> 00532 <!--############################################################--> 00533 <!--############################################################--> 00534 00535 \section numericlimits Use of std::numeric_limits 00536 00537 - old: 00538 \code 00539 #define BYTEMIN 0 00540 #define BYTEMAX 255 00541 #define INT16MIN (-32768) 00542 #define INT16MAX 32767 00543 #define INT32MIN (-2147483647) 00544 #define INT32MAX 2147483647 00545 #define FLOATMIN -3.40e+38F 00546 #define FLOATMAX 3.40282347e+38F 00547 00548 void foo(Image<float>& x) 00549 { 00550 x.normalize(BYTEMIN, BYTEMAX); 00551 } 00552 \endcode 00553 00554 - new: 00555 \code 00556 #include <limits> 00557 00558 void foo(Image<float>& x) 00559 { 00560 x.normalize(std::numeric_limits<byte>::max(), 00561 std::numeric_limits<byte>::min()); 00562 } 00563 \endcode 00564 00565 - Why? It's standard, it's portable, it offers more 00566 - <i>can be specialized for user-defined types</i> 00567 - get the number of bits used to represent the type 00568 - get the range of the exponent for floating-point types 00569 \code 00570 #include <limits> 00571 00572 template <class T> 00573 void foo(const Image<T>& x) 00574 { 00575 if (std::numeric_limits<T>::is_signed) 00576 { 00577 // do something for a signed type 00578 } 00579 else 00580 { 00581 // do something for an unsigned type 00582 } 00583 } 00584 00585 template <class T> 00586 void bar(const Image<T>& x) 00587 { 00588 if (std::numeric_limits<T>::is_integer) 00589 { 00590 // do something for an integral type 00591 } 00592 else 00593 { 00594 // do something for a floating-point type 00595 } 00596 } 00597 \endcode 00598 00599 */