Home || Architecture || Video Search || Visual Search || Scripts || Applications || Important Messages || OGL || Src

ImageCache.h

Go to the documentation of this file.
00001 #ifndef VideoExcel_ImageCache_h
00002 #define VideoExcel_ImageCache_h
00003 
00004 #ifndef RICHARD
00005 // #define USE_BOOST_THREADS
00006 #endif
00007 
00008 #include "OglGui/OglImageCache.h"
00009 
00010 #include "TableDataSource.h"
00011 #include "Visualization/RgbOglImage.h"
00012 
00013 #ifdef USE_BOOST_THREADS
00014 
00015 // use this version for boost v. 34 (as on UvA FC8)
00016 #include "threadpool23/threadpool.hpp"
00017 // use this version for boost >= 37 (as on other machines)
00018 // #include "threadpool25/threadpool.hpp"
00019 
00020 #endif
00021 
00022 
00023 namespace Impala {
00024 namespace Application {
00025 namespace VideoExcel {
00026 
00027 
00028 // RvB: BRAINSTORM!!
00029 //      Als we een interface meegeven met een member functie:
00030 //      OGLIMAGE* GetImageByID( String, id)
00031 //      en niet meer een source meegeven. Dan zou dit een meer algemene
00032 //      OGLIMAGE cache kunnen zijn. Preload moet er dan ook uit, maar
00033 //      kan makkelijk aan de app kant worden geimplementeerd
00034 
00035 
00036 /* A short note on how ImageCache and TableViewCache objects relate:
00037 
00038  Specifications:
00039   - there shall be only one application wide instance of an OGLIMAGE
00040     for a specified image
00041   - there may be more than one view associated with that OGLIMAGE
00042   - those views may also be cached
00043 
00044  Implementation:
00045 
00046  There is only one ImageCache instance, which handles a cached set of OGLIMAGEs
00047 
00048  For each Window where Views are needed there is one TableViewCache.
00049 
00050  TableViewCache: a cache of views with associated (refcounted) OGLIMAGE objects
00051  TableViewCache retrieves new OGLIMAGEs by asking ImageCache, which will get
00052  the image from cache if possible, or load and cache it if it's not there.
00053 
00054  ImageCache therefore keeps it's own (refcounted) set of OGLIMAGEs, which it
00055  will deref only when the cache is full.
00056 
00057  ImageCache::mCacheSize MUST BE larger than those of the largest TableViewCache.
00058 
00059  The idea is that, if a window wants to draw a specific view for an OGLIMAGE
00060  already displayed in another window, the OGLIMAGE does not get loaded twice.
00061 
00062  If a specific window scrolls trough it's own views, then those will be cached
00063  locally before it needs to revert to the global ImageCache cache.
00064 
00065  Eventually we might want to remove TableViewCache, or at least the
00066  caching bit in there
00067 
00068  Ork
00069 */
00070 
00071 class ImageCache
00072 {
00073 public:
00074     typedef Impala::Core::Array::Array2dVec3UInt8   Array2dVec3UInt8;
00075     typedef std::pair<int, OGLIMAGE*>               ImageWithID;
00076 
00077     static void InitializeCache(TableDataSource *source, int size)
00078     {
00079         if (sInstance)
00080         {
00081             ILOG_WARN("ImageCache already initialized. Skipping.");
00082             return;
00083         }
00084         sInstance = new ImageCache(source, size);
00085     }
00086 
00087     static ImageCache* GetInstance()
00088     {
00089         if (!sInstance)
00090         {
00091             ILOG_ERROR("You forgot to initialize ImageCache with a source.");
00092             return 0;
00093         }
00094         return sInstance;
00095     }
00096 
00097     // RvB: Not being used
00098     OGLIMAGE* GetImage(int id, String column)
00099     {
00100         OGLIMAGE *i = GetImageFromCache(id, column);
00101         if (!i)
00102             i = LoadImage(id, column);
00103         return i;
00104     }
00105 
00106     OGLIMAGE* GetImageFromCache(int id, String column)
00107     {
00108         /* todo:
00109         currently images are cached by row, assumption is only one image
00110         per row, we should also cache-verify the column.
00111         */
00112         for (int i=0; i<mImages.size(); i++)
00113         {
00114             if (id == mImages[i]->first) {
00115                 //ILOG_DEBUG("cache hit: " << id);
00116                 UpdateLRU(i);
00117                 return mImages[i]->second;
00118             }
00119         }
00120         return 0;
00121     }
00122 
00123 #ifndef USE_BOOST_THREADS
00124     OGLIMAGE* LoadImage(int id, String column)
00125     {
00126         // image not in cache, load and place in cache:
00127         // ILOG_DEBUG("cache miss: retrieving " << id << "...");
00128         Array2dVec3UInt8* ar = mSource->GetImageDataByID(column, id);
00129         if (ar == 0)
00130             return 0;
00131 
00132         ImageWithID *p = GetLeastUsedImageFromCache();
00133         if (p->second)
00134         {
00135             ReleaseOglImage(p->second);
00136             ILOG_DEBUG("Refcount for least used image now " <<
00137                        p->second->refCount);
00138         }
00139         else
00140             ILOG_DEBUG("Cache not yet grown to max size, growing...");
00141 
00142         p->second = Visualization::RgbOglImage::OglImage(ar);
00143         p->first = id;
00144         return p->second;
00145     }
00146 #else
00147     OGLIMAGE* LoadImage(int id, String column)
00148     {
00149         // image not in cache, create a new dummy OGLIMAGE and place
00150         // this in the cache, and start a worker thread to load the
00151         // actual image, which will flip the changed bit on the OGLIMAGE
00152         // so OGL redraws it.
00153 
00154         // ILOG_DEBUG("cache miss: retrieving " << id << "...");
00155 
00156         ImageWithID *p = GetLeastUsedImageFromCache();
00157         if (p->second)
00158         {
00159             ReleaseOglImage(p->second);
00160             ILOG_DEBUG("Refcount for least used image now " <<
00161                        p->second->refCount);
00162         }
00163         else
00164             ILOG_DEBUG("Cache not yet grown to max size, growing...");
00165 
00166         p->second = Visualization::RgbOglImage::OglImage(mEmptyArray);
00167         p->first = id;
00168 
00169         // initialize and launch thread:
00170         // note: todo - upgrade this to use a threadpool instead of firing
00171         //       a new thread for every image.
00172         OglImageLoader loader(mSource, column, id, p->second);
00173         //boost::thread work(loader);
00174         mThreadPool->schedule(loader);
00175 
00176         mActiveTasks = true;
00177 
00178         return p->second;
00179     }
00180 #endif
00181 
00182 #ifndef USE_BOOST_THREADS
00183     bool
00184     IsLoadingImages()
00185     {
00186         return false;
00187     }
00188 #else
00189     bool
00190     IsLoadingImages()
00191     {
00192         if (!mActiveTasks)
00193             return false;
00194 
00195         ILOG_DEBUG("TP check: Size " << mThreadPool->size() << " Active tasks " << mThreadPool->active() << " Pending tasks " << mThreadPool->pending());
00196 
00197         if (mThreadPool->active() == 0)
00198             mActiveTasks = false;
00199 
00200         return true;
00201     }
00202 #endif
00203 
00204 
00205     int GetImageCacheMaxSize()
00206     {
00207         return mCacheSize;
00208     }
00209 
00210     // speedup function which might be nice for demos:
00211     void PreloadCache()
00212     {
00213         // only works if there is one image column in the DataSource
00214         int preload = mCacheSize;
00215         if (mSource->GetTotalRows() < mCacheSize)
00216             preload = mSource->GetTotalRows();
00217         ILOG_INFO("Preloading " << preload << " images...");
00218         for (int i=0; i<preload; i++)
00219         {
00220             if (i%100==0)
00221                 ILOG_DEBUG("Preloading image " << i << " of " << preload);
00222             LoadImage(i, "");
00223         }
00224     }
00225 
00226 private:
00227     ImageCache(TableDataSource* source, int size) // constructor: private
00228     {
00229         Init(source, size);
00230     }
00231 
00232     ImageCache(ImageCache const&){};             // copy constructor: private
00233     ImageCache& operator=(ImageCache const&){};  // assignment operator: private
00234 
00235     ImageWithID* GetLeastUsedImageFromCache()
00236     {
00237         int item = mLRU.front();
00238         UpdateLRU(item);
00239         return mImages[item];
00240     }
00241 
00242     void UpdateLRU(int index)
00243     {
00244         mLRU.remove(index);
00245         mLRU.push_back(index);
00246     }
00247 
00248     void Init(TableDataSource* source, int size)
00249     {
00250         mCacheSize = size;
00251         ILOG_DEBUG("Initializing image cache for "<< mCacheSize <<" OGLIMAGEs");
00252 
00253 #ifdef USE_BOOST_THREADS
00254         mEmptyArray = Core::Array::ArrayCreate<Core::Array::Array2dVec3UInt8>(0, 0, 0, 0);
00255         mThreadPool = new boost::threadpool::pool();
00256         mThreadPool->size_controller().resize(8);
00257         mActiveTasks = false;
00258 #endif
00259 
00260         mSource = source;
00261 
00262         for (int i=0; i<mCacheSize; i++)
00263         {
00264             mImages.push_back(new ImageWithID(-1, 0));
00265             mLRU.push_back(i);
00266         }
00267     }
00268 
00269 #ifdef USE_BOOST_THREADS
00270     class OglImageLoader {
00271         public:
00272             OglImageLoader(TableDataSource *s, std::string column, int row, OGLIMAGE *result)
00273             {
00274                 mSource = s;
00275                 mColumn = column;
00276                 mRow = row;
00277                 mImage = result;
00278             }
00279 
00280         void operator()() {
00281             ILOG_DEBUG("Worker thread retrieving " << mColumn << "x" << mRow);
00282             LoadImage();
00283         }
00284 
00285         int LoadImage()
00286         {
00287             Array2dVec3UInt8* ar = mSource->GetImageDataByID(mColumn, mRow);
00288             if (ar == 0)
00289                 return 0;
00290 
00291             // delay loop for testing:
00292 /*            boost::xtime xt;
00293             boost::xtime_get(&xt, boost::TIME_UTC);
00294             xt.nsec += 250 * 100000 * 4;
00295             boost::thread::sleep(xt);
00296 */
00297 
00298             mImage->imageHandle = ar;
00299             mImage->h = ar->CH();
00300             mImage->w = ar->CW();
00301             mImage->changed = 1;
00302             ILOG_DEBUG("Loaded image " << mColumn << "x" << mRow << " as " << mImage->w << "x" << mImage->h <<" pixels in " << mImage);
00303     
00304             return 1;
00305         }
00306     
00307         private:
00308         TableDataSource *mSource;
00309         std::string      mColumn;
00310         int              mRow;
00311         OGLIMAGE*        mImage;
00312     };
00313 #endif
00314 
00315 
00316     int                         mCacheSize;
00317 
00318     std::list<int>              mLRU;
00319     std::vector<ImageWithID*>   mImages;
00320     TableDataSource*            mSource;
00321 
00322     static ImageCache*          sInstance;
00323 
00324 #ifdef USE_BOOST_THREADS
00325     Core::Array::Array2dVec3UInt8* mEmptyArray;
00326     boost::threadpool::pool     *mThreadPool;
00327     bool                         mActiveTasks;
00328 #endif
00329 
00330     ILOG_VAR_DEC;
00331 };
00332 
00333 ImageCache* ImageCache::sInstance = 0;
00334 
00335 ILOG_VAR_INIT(ImageCache, Application.VideoExcel);
00336 
00337 } // namespace VideoExcel
00338 } // namespace Application
00339 } // namespace Impala
00340 #endif // ImageCache_h
00341 
00342 
00343 

Generated on Fri Mar 19 09:30:32 2010 for ImpalaSrc by  doxygen 1.5.1