ImageCache.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 4 -*-
00002 
00027 #include "ImageCache.h"
00028 
00029 #include <iostream>
00030 #include <boost/thread/thread.hpp>
00031 #include <vigra/inspectimage.hxx>
00032 #include <vigra/accessor.hxx>
00033 #include <vigra/functorexpression.hxx>
00034 #include <vigra/sized_int.hxx>
00035 #include <vigra_ext/utils.h>
00036 #include <vigra_ext/impexalpha.hxx>
00037 #include <vigra_ext/Pyramid.h>
00038 #include <vigra_ext/FunctorAccessor.h>
00039 
00040 
00041 
00042 namespace HuginBase {
00043     
00044 using namespace std;
00045 using namespace vigra::functor;
00046 
00047 
00048 template <class T1>
00049 class GetRange
00050 {
00051     public:
00052         static T1 min();
00053         static T1 max();
00054 };
00055 
00056 // ImageCache::GetRange implementation
00057 #define VIGRA_EXT_GETRANGE(T1, MI,MA) \
00058 template<> \
00059 T1 GetRange<T1>::min() \
00060 { \
00061         return MI; \
00062 } \
00063 template<> \
00064 T1 GetRange<T1>::max() \
00065 { \
00066         return MA; \
00067 } \
00068 
00069 VIGRA_EXT_GETRANGE(vigra::UInt8,  0, 255);
00070 VIGRA_EXT_GETRANGE(vigra::Int16,  0, 32767);
00071 VIGRA_EXT_GETRANGE(vigra::UInt16, 0, 65535);
00072 VIGRA_EXT_GETRANGE(vigra::Int32,  0, 2147483647);
00073 VIGRA_EXT_GETRANGE(vigra::UInt32, 0, 4294967295u);
00074 VIGRA_EXT_GETRANGE(float,  0, 1.0f);
00075 VIGRA_EXT_GETRANGE(double, 0, 1.0);
00076 
00077 #undef VIGRA_EXT_GETRANGE
00078 
00079 
00080 template <class SrcIMG>
00081 void convertTo8Bit(SrcIMG & src, const std::string & origType, vigra::BRGBImage & dest)
00082 {
00083     // code to apply the mapping to 8 bit
00084     // always scaled from 0..1 for integer images.
00085     
00086     dest.resize(src.size());
00087     
00088     double min=0;
00089     double max=vigra_ext::getMaxValForPixelType(origType);
00090     ;
00091     
00092     int mapping = HUGIN_IMGCACHE_MAPPING_INTEGER;
00093     
00094     // float needs to be from min ... max.
00095     if (origType == "FLOAT" || origType == "DOUBLE")
00096     {
00097         vigra::RGBToGrayAccessor<vigra::RGBValue<float> > ga;
00098         vigra::FindMinMax<float> minmax;   // init functor
00099         vigra::inspectImage(srcImageRange(src, ga),
00100                             minmax);
00101         min = minmax.min;
00102         max = minmax.max;
00103         mapping = HUGIN_IMGCACHE_MAPPING_FLOAT;
00104     }
00105     vigra_ext::applyMapping(srcImageRange(src), destImage(dest), min, max, mapping);
00106 }
00107 
00108 
00109 
00110 ImageCache::ImageCacheRGB8Ptr ImageCache::Entry::get8BitImage()
00111 {
00112     if (image8->width() > 0) {
00113         return image8;
00114     } else if (image16->width() > 0) {
00115         convertTo8Bit(*image16,
00116                       origType,
00117                       *image8);
00118     } else if (imageFloat->width() > 0) {
00119         convertTo8Bit(*imageFloat,
00120                       origType,
00121                       *image8);
00122     }
00123     return image8;
00124 }
00125 
00126 ImageCache * ImageCache::instance = NULL;
00127 
00128 
00129 void ImageCache::removeImage(const std::string & filename)
00130 {
00131     map<string, EntryPtr>::iterator it = images.find(filename);
00132     if (it != images.end()) {
00133         images.erase(it);
00134     }
00135 
00136     string sfilename = filename + string("_small");
00137     it = images.find(sfilename);
00138     if (it != images.end()) {
00139         images.erase(it);
00140     }
00141 
00142     int level = 0;
00143     bool found = true;
00144     do {
00145         // found. xyz
00146         PyramidKey key(filename,level);
00147         map<string, vigra::BImage*>::iterator it = pyrImages.find(key.toString());
00148         found = (it != pyrImages.end());
00149         if (found) {
00150             delete it->second;
00151             pyrImages.erase(it);
00152         }
00153         level++;
00154     } while (found);
00155 }
00156 
00157 
00158 void ImageCache::flush()
00159 {
00160     images.clear();
00161 
00162     for (map<string, vigra::BImage*>::iterator it = pyrImages.begin();
00163          it != pyrImages.end();
00164          ++it)
00165     {
00166         delete it->second;
00167     }
00168     pyrImages.clear();
00169 }
00170 
00171 void ImageCache::softFlush()
00172 {
00173     if(upperBound==0l)
00174         upperBound = 100 * 1024 * 1024l;
00175     long purgeToSize = long(0.75 * upperBound);
00176 
00177     // calculate used memory
00178     long imgMem = 0;
00179 
00180     std::map<std::string, EntryPtr>::iterator imgIt;
00181     for(imgIt=images.begin(); imgIt != images.end(); imgIt++) {
00182 #ifdef DEBUG
00183         cout << "Image: " << imgIt->first << std::endl;
00184         cout << "CacheEntry: " << imgIt->second.use_count() << "last access: " << imgIt->second->lastAccess;
00185 #endif
00186         if (imgIt->second->image8) {
00187             imgMem += imgIt->second->image8->width() * imgIt->second->image8->height() * 3;
00188 #ifdef DEBUG
00189             cout << " 8bit: " << imgIt->second->image8.use_count();
00190 #endif
00191         }
00192         if (imgIt->second->image16) {
00193             imgMem += imgIt->second->image16->width() * imgIt->second->image16->height() * 3*2;
00194 #ifdef DEBUG
00195             cout << " 16bit: " << imgIt->second->image8.use_count();
00196 #endif
00197         }
00198         if (imgIt->second->imageFloat) {
00199             imgMem += imgIt->second->imageFloat->width() * imgIt->second->imageFloat->height() * 3 * 4;
00200 #ifdef DEBUG
00201             cout << " float: " << imgIt->second->imageFloat.use_count() ;
00202 #endif
00203         }
00204         if (imgIt->second->mask) {
00205             imgMem += imgIt->second->mask->width() * imgIt->second->mask->height();
00206 #ifdef DEBUG
00207             cout << " mask: " << imgIt->second->mask.use_count() << std:: endl;
00208 #endif
00209         }
00210     }
00211 
00212     long pyrMem = 0;
00213     std::map<std::string, vigra::BImage*>::iterator pyrIt;
00214     for(pyrIt=pyrImages.begin(); pyrIt != pyrImages.end(); pyrIt++) {
00215         pyrMem += pyrIt->second->width() * pyrIt->second->height();
00216     }
00217 
00218     long usedMem = imgMem + pyrMem;
00219 
00220     DEBUG_DEBUG("total: " << (usedMem>>20) << " MB upper bound: " << (purgeToSize>>20) << " MB");
00221     if (usedMem > upperBound) 
00222     {
00223         // we need to remove images.
00224         long purgeAmount = usedMem - purgeToSize;
00225         long purgedMem = 0;
00226         // remove images from cache, first the grey level image,
00227         // then the full size images
00228 
00229         // use least recently uses strategy.
00230         // sort images by their access time
00231         std::map<int,std::string> accessMap;
00232         for (map<string, EntryPtr>::iterator it = images.begin();
00233              it != images.end();
00234              ++it)
00235         {
00236             if (it->first.substr(it->first.size()-6) != ":small") {
00237                 // only consider full images that are not used elsewhere
00238                 if (it->second.unique()) {
00239                     DEBUG_DEBUG("Considering " << it->first << " for deletion");
00240                     accessMap.insert(make_pair(it->second->lastAccess, it->first));
00241                 } else {
00242                     DEBUG_DEBUG(it->first << ", usecount: " << it->second.use_count());
00243                 }
00244             }
00245         }
00246         while (purgeAmount > purgedMem) {
00247             bool deleted = false;
00248             if (pyrImages.size() > 0) {
00249                 vigra::BImage * imgPtr = (*(pyrImages.begin())).second;
00250                 purgedMem += imgPtr->width() * imgPtr->height();
00251                 delete imgPtr;
00252                 pyrImages.erase(pyrImages.begin());
00253                 deleted = true;
00254             } else if (accessMap.size() > 0) {
00255                 std::map<int,std::string>::iterator accIt = accessMap.begin();
00256                 map<string, EntryPtr>::iterator it = images.find(accIt->second);
00257                 // check for uniqueness.
00258                 if (it != images.end()) {
00259                     DEBUG_DEBUG("soft flush: removing image: " << it->first);
00260                     if (it->second->image8) {
00261                         purgedMem += it->second->image8->width() * it->second->image8->height() * 3;
00262                     }
00263                     if (it->second->image16) {
00264                         purgedMem += it->second->image16->width() * it->second->image16->height() * 3 * 2;
00265                     }
00266                     if (it->second->imageFloat) {
00267                         purgedMem += it->second->imageFloat->width() * it->second->imageFloat->height()*3*4;
00268                     }
00269                     if (it->second->mask) {
00270                         purgedMem += it->second->mask->width() * it->second->mask->height();
00271                     }
00272                     images.erase(it);
00273                     accessMap.erase(accIt);
00274                     deleted = true;
00275                 } else {
00276                     DEBUG_ASSERT("internal error while purging cache");
00277                 }
00278             }
00279             if (!deleted) {
00280                 break;
00281             }
00282         }
00283 //        m_progress->progressMessage(
00284 //            wxString::Format(_("Purged %d MB from image cache. Current cache usage: %d MB"),
00285 //                             purgedMem>>20, (usedMem - purgedMem)>>20
00286 //                ).c_str(),0);
00287         DEBUG_DEBUG("purged: " << (purgedMem>>20) << " MB, memory used for images: " << ((usedMem - purgedMem)>>20) << " MB");
00288 
00289     }
00290 }
00291 
00292 
00293 
00294 ImageCache& ImageCache::getInstance()
00295 {
00296     if (instance == NULL)
00297     {
00298         instance = new ImageCache();
00299     }
00300     return *instance;
00301 }
00302 
00303 
00304 
00305 /*
00306 struct ApplyGammaFunctor
00307 {
00308     float minv;
00309     float maxv;
00310     float gamma;
00311     float scale;
00312 
00313     ApplyGammaFunctor(float min_, float max_, float gamma_)
00314     {
00315         minv = min_;
00316         maxv = max_;
00317         gamma = gamma_;
00318         scale = maxv - minv;
00319     }
00320 
00321     template <class T>
00322     unsigned char operator()(T v) const
00323     {
00324         typedef vigra::NumericTraits<vigra::UInt8>  DestTraits;
00325         return DestTraits::fromRealPromote(pow((float(v)-minv)/scale, gamma)*255);
00326     }
00327 
00328     template <class T, unsigned int R, unsigned int G, unsigned int B>
00329     RGBValue<vigra::UInt8,0,1,2> operator()(const RGBValue<T,R,G,B> & v) const
00330     {
00331         typedef vigra::NumericTraits< RGBValue<vigra::UInt8,0,1,2> >  DestTraits;
00332         typedef vigra::NumericTraits< RGBValue<T,R,G,B> >  SrcTraits;
00333         return DestTraits::fromRealPromote(pow((SrcTraits::toRealPromote(v)+(-minv))/scale, gamma)*255);
00334 //        return DestTraits::fromRealPromote((log10(SrcTraits::toRealPromote(v)) + (-minv))/scale);
00335     }
00336 };
00337 */
00338 
00339 
00340 //#if 0
00342 //template <class V1, class V2>
00343 //inline
00344 //vigra::RGBValue<V1>
00345 //operator+(const vigra::RGBValue<V1> l, V2 const & r)
00346 //{
00347 //    return vigra::RGBValue<V1>(l.red() + r, l.green() + r, l.blue() + r);
00348 //}
00349 //
00351 //template <class V1, class V2>
00352 //inline
00353 //vigra::RGBValue<V1>
00354 //operator-(const vigra::RGBValue<V1> l, V2 const & r)
00355 //{
00356 //    return vigra::RGBValue<V1>(l.red() - r, l.green() - r, l.blue() - r);
00357 //}
00358 //#endif
00359 
00360 
00361 
00362 
00363 //struct MyMultFunc
00364 //{
00365 //    MyMultFunc(double f)
00366 //    {
00367 //        m = f;
00368 //    }
00369 //    double m;
00370 //
00371 //    template<class T>
00372 //    T
00373 //    operator()(T v) const
00374 //    {
00375 //        return vigra::NumericTraits<T>::fromRealPromote(v*m);
00376 //    }
00377 //};
00378 
00379 template <class SrcPixelType,
00380           class DestIterator, class DestAccessor>
00381 void ImageCache::importAndConvertImage(const vigra::ImageImportInfo & info,
00382                                        vigra::pair<DestIterator, DestAccessor> dest,
00383                                        const std::string & type)
00384 {
00385     typedef typename DestAccessor::value_type DestPixelType;
00386 
00387     if (type == "FLOAT" || type == "DOUBLE" ) {
00388         // import image as it is
00389         vigra::importImage(info, dest);
00390     } else {
00391         vigra::importImage(info, dest);
00392         // integer image.. scale to 0 .. 1
00393         double scale = 1.0/vigra_ext::LUTTraits<SrcPixelType>::max();
00394 //        DestPixelType factor(scale);
00395         vigra_ext::Multiply<double> f(scale);
00396         transformImage(dest.first, dest.first+ vigra::Diff2D(info.width(), info.height()), dest.second,
00397                        dest.first, dest.second,
00398                        Arg1()*Param(scale));
00399     }
00400 }
00401 
00402 #if 0
00403 
00404 template <class SrcPixelType,
00405           class DestIterator, class DestAccessor>
00406 void ImageCache::importAndConvertGrayImage(const ImageImportInfo & info,
00407                                            vigra::pair<DestIterator, DestAccessor> dest,
00408                                            wxString type)
00409 
00410 {
00411     typedef typename DestAccessor::value_type DestPixelType;
00412     typedef typename DestPixelType::value_type DestComponentType;
00413     typedef SrcPixelType SrcComponentType;
00414 
00415     // load image into temporary buffer.
00416     BasicImage<SrcPixelType> tmp(info.width(), info.height());
00417     vigra::importImage(info, destImage(tmp));
00418 
00419     SrcComponentType min,max;
00420 
00421     int range = wxConfigBase::Get()->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE);
00422     if (range == 0) {
00423         double t;
00424         wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/min"), &t, double(GetRange<SrcComponentType>::min()));
00425         min = SrcComponentType(t);
00426         wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/max"), &t, double(GetRange<SrcComponentType>::max()));
00427         max = SrcComponentType(t);
00428     } else if (range == 1) {
00429         vigra::FindMinMax<SrcComponentType> minmax;   // init functor
00430         vigra::inspectImage(srcImageRange(tmp),
00431                             minmax);
00432         min = minmax.min;
00433         max = minmax.max;
00434     } else{
00435         vigra_fail("Unknown image import range mode");
00436     }
00437 
00438     int mapping = wxConfigBase::Get()->Read(wxT("/ImageCache/Mapping"), HUGIN_IMGCACHE_MAPPING);
00439     applyMapping(srcImageRange(tmp), dest, min, max, mapping);
00440 }
00441 
00442 template <class SrcPixelType,
00443           class DestIterator, class DestAccessor>
00444 void ImageCache::importAndConvertGrayAlphaImage(const ImageImportInfo & info,
00445                                                 vigra::pair<DestIterator, DestAccessor> dest,
00446                                                 wxString type)
00447 {
00448     typedef typename DestAccessor::value_type DestPixelType;
00449     typedef typename DestPixelType::value_type DestComponentType;
00450     typedef SrcPixelType SrcComponentType;
00451 
00452     // load image into temporary buffer.
00453     BasicImage<SrcPixelType> tmp(info.width(), info.height());
00454     BImage mask(info.width(), info.height());
00455     vigra::importImageAlpha(info, destImage(tmp), destImage(mask));
00456 
00457     SrcComponentType min,max;
00458 
00459     int range = wxConfigBase::Get()->Read(wxT("/ImageCache/Range"), HUGIN_IMGCACHE_RANGE);
00460     if (range == 0) {
00461         double t;
00462         wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/min"), &t, double(GetRange<SrcComponentType>::min()));
00463         min = SrcComponentType(t);
00464         wxConfigBase::Get()->Read(wxT("/ImageCache/") + type + wxT("/max"), &t, double(GetRange<SrcComponentType>::max()));
00465         max = SrcComponentType(t);
00466     } else if (range == 1) {
00467         vigra::FindMinMax<SrcComponentType> minmax;   // init functor
00468         vigra::inspectImage(srcImageRange(tmp),
00469                             minmax);
00470         min = minmax.min;
00471         max = minmax.max;
00472     } else{
00473         vigra_fail("Unknown image import range mode");
00474     }
00475 
00476     int mapping = wxConfigBase::Get()->Read(wxT("/ImageCache/Mapping"), HUGIN_IMGCACHE_MAPPING);
00477     applyMapping(srcImageRange(tmp), dest, min, max, mapping);
00478 }
00479 
00480 #endif
00481 
00482 template <class SrcPixelType,
00483           class DestIterator, class DestAccessor,
00484           class MaskIterator, class MaskAccessor>
00485 void ImageCache::importAndConvertAlphaImage(const vigra::ImageImportInfo & info,
00486                                             vigra::pair<DestIterator, DestAccessor> dest,
00487                                             vigra::pair<MaskIterator, MaskAccessor> mask,
00488                                             const std::string & type)
00489 {
00490     if (type == "FLOAT" || type == "DOUBLE" ) {
00491         // import image as it is
00492         vigra::importImageAlpha(info, dest, mask);
00493     } else {
00494         // integer image.. scale to 0 .. 1
00495         vigra::importImageAlpha(info, dest, mask);
00496         double scale = 1.0/vigra_ext::LUTTraits<SrcPixelType>::max();
00497         transformImage(dest.first, dest.first+ vigra::Diff2D(info.width(), info.height()),  dest.second, 
00498                        dest.first, dest.second,
00499                        Arg1()*Param(scale));
00500     }
00501 }
00502 
00503 ImageCache::EntryPtr ImageCache::getImage(const std::string & filename)
00504 {
00505 //    softFlush();
00506     m_accessCounter++;
00507     std::map<std::string, EntryPtr>::iterator it;
00508     it = images.find(filename);
00509     if (it != images.end()) {
00510         it->second->lastAccess = m_accessCounter;
00511         return it->second;
00512     } else {
00513         if (m_progress) {
00514             m_progress->pushTask(AppBase::ProgressTask("Loading image: "+hugin_utils::stripPath(filename), "", 0));
00515         }
00516         
00517         EntryPtr e = loadImageSafely(filename);
00518         
00519         if (m_progress) {
00520             m_progress->popTask();
00521         }
00522         
00523         if (!e.get())
00524         {
00525             // could not access image.
00526             throw std::exception();
00527         }
00528         
00529         images[filename] = e;
00530         e->lastAccess = m_accessCounter;
00531         return e;
00532     }
00533 }
00534 
00535 ImageCache::EntryPtr ImageCache::loadImageSafely(const std::string & filename)
00536 {
00537     // load images with VIGRA impex, and store either 8 bit or float images
00538     std::string pixelTypeStr;
00539     ImageCacheRGB8Ptr img8(new vigra::BRGBImage);
00540     ImageCacheRGB16Ptr img16(new vigra::UInt16RGBImage);
00541     ImageCacheRGBFloatPtr imgFloat(new vigra::FRGBImage);
00542     ImageCache8Ptr mask(new vigra::BImage);
00543 
00544     try {
00545         vigra::ImageImportInfo info(filename.c_str());
00546 
00547         int bands = info.numBands();
00548         int extraBands = info.numExtraBands();
00549         const char * pixelType = info.getPixelType();
00550         pixelTypeStr = pixelType;
00551 
00552         DEBUG_DEBUG(filename << ": bands: " << bands << "  extra bands: " << extraBands << "  type: " << pixelType);
00553 
00554         if (pixelTypeStr == "UINT8") {
00555             img8->resize(info.size());
00556         } else if (pixelTypeStr == "UINT16" ) {
00557             img16->resize(info.size());
00558         } else {
00559             imgFloat->resize(info.size());
00560         }
00561 
00562         if ( bands == 1) {
00563             // load and convert image to 8 bit, if needed
00564             if (strcmp(pixelType, "UINT8") == 0 ) {
00565                 vigra::importImage(info, destImage(*img8,
00566                                                    vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)));
00567                 copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
00568                           destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(1)));
00569                 copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
00570                           destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(2)));
00571             } else if (strcmp(pixelType, "UINT16") == 0 ) {
00572                 vigra::importImage(info, destImage(*img16,
00573                                                    vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)));
00574                 copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
00575                           destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(1)));
00576                 copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
00577                           destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(2)));
00578             } else {
00579                 if (strcmp(pixelType, "INT16") == 0 ) {
00580                     importAndConvertImage<vigra::Int16> (info, destImage(*imgFloat,
00581                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
00582                 } else if (strcmp(pixelType, "UINT32") == 0 ) {
00583                     importAndConvertImage<vigra::UInt32>(info, destImage(*imgFloat,
00584                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
00585                 } else if (strcmp(pixelType, "INT32") == 0 ) {
00586                     importAndConvertImage<vigra::Int32>(info, destImage(*imgFloat,
00587                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
00588                 } else if (strcmp(pixelType, "FLOAT") == 0 ) {
00589                     importAndConvertImage<float>(info, destImage(*imgFloat,
00590                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
00591                 } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
00592                     importAndConvertImage<double>(info, destImage(*imgFloat,
00593                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
00594                 } else {
00595                     DEBUG_ERROR("Unsupported pixel type: " << pixelType);
00596                     return EntryPtr();
00597                 }
00598                 copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
00599                           destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(1)));
00600                 copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
00601                           destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(2)));
00602             }
00603         } else if ( bands == 2 && extraBands==1) {
00604             mask->resize(info.size());
00605             // load and convert image to 8 bit, if needed
00606             if (strcmp(pixelType, "UINT8") == 0 ) {
00607                 vigra::importImageAlpha(info, destImage(*img8,
00608                                                    vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
00609                                                    destImage(*mask));
00610                 copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
00611                           destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(1)));
00612                 copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
00613                           destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(2)));
00614             } else if (strcmp(pixelType, "UINT16") == 0 ) {
00615                 vigra::importImageAlpha(info, destImage(*img16,
00616                                                    vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
00617                                                    destImage(*mask));
00618                 copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
00619                           destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(1)));
00620                 copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
00621                           destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(2)));
00622             } else {
00623                 if (strcmp(pixelType, "INT16") == 0 ) {
00624                     importAndConvertAlphaImage<vigra::Int16> (info, destImage(*imgFloat,
00625                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
00626                 } else if (strcmp(pixelType, "UINT32") == 0 ) {
00627                     importAndConvertAlphaImage<vigra::UInt32>(info, destImage(*imgFloat,
00628                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
00629                 } else if (strcmp(pixelType, "INT32") == 0 ) {
00630                     importAndConvertAlphaImage<vigra::Int32>(info, destImage(*imgFloat,
00631                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
00632                 } else if (strcmp(pixelType, "FLOAT") == 0 ) {
00633                     importAndConvertAlphaImage<float>(info, destImage(*imgFloat,
00634                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
00635                 } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
00636                     importAndConvertAlphaImage<double>(info, destImage(*imgFloat,
00637                             vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
00638                 } else {
00639                     DEBUG_ERROR("Unsupported pixel type: " << pixelType);
00640                     return EntryPtr();
00641                 }
00642                 copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
00643                           destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(1)));
00644                 copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
00645                           destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(2)));
00646             }
00647         } else if (bands == 3 && extraBands == 0) {
00648             DEBUG_DEBUG( pixelType);
00649             // load and convert image to 8 bit, if needed
00650             if (strcmp(pixelType, "UINT8") == 0 ) {
00651                 vigra::importImage(info, destImage(*img8));
00652             } else if (strcmp(pixelType, "UINT16") == 0 ) {
00653                 vigra::importImage(info, destImage(*img16));
00654             } else if (strcmp(pixelType, "INT16") == 0 ) {
00655                 importAndConvertImage<vigra::RGBValue<vigra::Int16> > (info, destImage(*imgFloat), pixelType);
00656             } else if (strcmp(pixelType, "UINT32") == 0 ) {
00657                 importAndConvertImage<vigra::RGBValue<vigra::UInt32> >(info, destImage(*imgFloat), pixelType);
00658             } else if (strcmp(pixelType, "INT32") == 0 ) {
00659                 importAndConvertImage<vigra::RGBValue<vigra::Int32> >(info, destImage(*imgFloat), pixelType);
00660             } else if (strcmp(pixelType, "FLOAT") == 0 ) {
00661                 vigra::importImage(info, destImage(*imgFloat));
00662 //                    importAndConvertImage<vigra::RGBValue<float> >(info, destImage(*imgFloat), pixelType);
00663             } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
00664                 vigra::importImage(info, destImage(*imgFloat));
00665 //                    importAndConvertImage<vigra::RGBValue<double> >(info, destImage(*imgFloat), pixelType);
00666             } else {
00667                 DEBUG_ERROR("Unsupported pixel type: " << pixelType);
00668                 return EntryPtr();
00669             }
00670         } else if ( bands == 4 && extraBands == 1) {
00671             mask->resize(info.size());
00672             // load and convert image to 8 bit, if needed
00673             if (strcmp(pixelType, "UINT8") == 0 ) {
00674                 vigra::importImageAlpha(info, destImage(*img8), destImage(*mask));
00675             } else if (strcmp(pixelType, "UINT16") == 0 ) {
00676                 vigra::importImageAlpha(info, destImage(*img16), destImage(*mask));
00677             } else if (strcmp(pixelType, "INT16") == 0 ) {
00678                 importAndConvertAlphaImage<short> (info, destImage(*imgFloat), destImage(*mask), pixelType);
00679             } else if (strcmp(pixelType, "UINT32") == 0 ) {
00680                 importAndConvertAlphaImage<unsigned int>(info, destImage(*imgFloat), destImage(*mask), pixelType);
00681             } else if (strcmp(pixelType, "INT32") == 0 ) {
00682                 importAndConvertAlphaImage<int>(info, destImage(*imgFloat), destImage(*mask), pixelType);
00683             } else if (strcmp(pixelType, "FLOAT") == 0 ) {
00684                 importAndConvertAlphaImage<float>(info, destImage(*imgFloat), destImage(*mask), pixelType);
00685             } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
00686                 importAndConvertAlphaImage<double>(info, destImage(*imgFloat), destImage(*mask), pixelType);
00687             } else {
00688                 DEBUG_FATAL("Unsupported pixel type: " << pixelType);
00689                 return EntryPtr();
00690             }
00691         } else {
00692             DEBUG_ERROR("unsupported depth, only images with 1 or 3 channel images are supported.");
00693             return EntryPtr();
00694         }
00695     } catch (std::exception & e) {
00696         // could not load image..
00697        DEBUG_ERROR("Error during image reading: " << e.what());
00698        return EntryPtr();
00699     }
00700 
00701     return EntryPtr(new Entry(img8, img16, imgFloat, mask, pixelTypeStr));
00702 }
00703 
00704 ImageCache::EntryPtr ImageCache::getImageIfAvailable(const std::string & filename)
00705 {
00706     std::map<std::string, EntryPtr>::iterator it;
00707     it = images.find(filename);
00708     if (it != images.end()) {
00709         m_accessCounter++;
00710         it->second->lastAccess = m_accessCounter;
00711         return it->second;
00712     } else {
00713         // not found, return 0 pointer.
00714         return EntryPtr();
00715     }
00716 }
00717 
00718 ImageCache::EntryPtr ImageCache::getSmallImage(const std::string & filename)
00719 {
00720     m_accessCounter++;
00721     softFlush();
00722     std::map<std::string, EntryPtr>::iterator it;
00723     // "_small" is only used internally
00724     string name = filename + string(":small");
00725     it = images.find(name);
00726     if (it != images.end()) {
00727         return it->second;
00728     } else {
00729         if (m_progress)
00730         {
00731             m_progress->pushTask(AppBase::ProgressTask("Scaling image: "+hugin_utils::stripPath(filename), "", 0));
00732         }
00733         DEBUG_DEBUG("creating small image " << name );
00734         EntryPtr entry = getImage(filename);
00735         
00736         EntryPtr small_entry = loadSmallImageSafely(entry);
00737         small_entry->lastAccess = m_accessCounter;
00738         images[name] = small_entry;
00739         DEBUG_INFO ( "created small image: " << name);
00740         if (m_progress) {
00741             m_progress->popTask();
00742         }
00743         return small_entry;
00744     }
00745 }
00746 
00747 ImageCache::EntryPtr ImageCache::loadSmallImageSafely(EntryPtr entry)
00748 {
00749     // && entry->image8
00750     size_t w=0;
00751     size_t h=0;
00752     if (entry->image8->width() > 0) {
00753         w = entry->image8->width();
00754         h = entry->image8->height();
00755     } else if (entry->image16->width() > 0) {
00756         w = entry->image16->width();
00757         h = entry->image16->height();
00758     } else if (entry->imageFloat->width() > 0) {
00759         w = entry->imageFloat->width();
00760         h = entry->imageFloat->height();
00761     } else {
00762         vigra_fail("Could not load image");
00763     }
00764 
00765     size_t sz = w*h;
00766     size_t smallImageSize = 800 * 800l;
00767 
00768     int nLevel=0;
00769     while(sz > smallImageSize) {
00770         sz /=4;
00771         nLevel++;
00772     }
00773     EntryPtr e(new Entry);
00774     e->origType = entry->origType;
00775     // TODO: fix bug with mask reduction
00776     vigra::BImage fullsizeMask = *(entry->mask);
00777     if (entry->imageFloat->width() != 0 ) {
00778         e->imageFloat = ImageCacheRGBFloatPtr(new vigra::FRGBImage);
00779         if (entry->mask->width() != 0) {
00780             vigra_ext::reduceNTimes(*(entry->imageFloat), fullsizeMask, *(e->imageFloat), *(e->mask), nLevel);
00781         } else {
00782             vigra_ext::reduceNTimes(*(entry->imageFloat), *(e->imageFloat), nLevel);
00783         }
00784     }
00785     if (entry->image16->width() != 0 ) {
00786         e->image16 = ImageCacheRGB16Ptr(new vigra::UInt16RGBImage);
00787         if (entry->mask->width() != 0) {
00788             vigra_ext::reduceNTimes(*(entry->image16), fullsizeMask, *(e->image16), *(e->mask), nLevel);
00789         } else {
00790             vigra_ext::reduceNTimes(*(entry->image16), *(e->image16), nLevel);
00791         }
00792     }
00793     if (entry->image8->width() != 0) {
00794         e->image8 = ImageCacheRGB8Ptr(new vigra::BRGBImage);
00795         if (entry->mask->width() != 0) {
00796             vigra_ext::reduceNTimes(*(entry->image8), fullsizeMask, *(e->image8), *(e->mask), nLevel);
00797         } else {
00798             vigra_ext::reduceNTimes(*(entry->image8), *(e->image8), nLevel);
00799         }
00800     }
00801     return e;
00802 }
00803 
00804 ImageCache::EntryPtr ImageCache::getSmallImageIfAvailable(const std::string & filename)
00805 {
00806     m_accessCounter++;
00807     softFlush();
00808     std::map<std::string, EntryPtr>::iterator it;
00809     // "_small" is only used internally
00810     string name = filename + string(":small");
00811     it = images.find(name);
00812     if (it != images.end()) {
00813         return it->second;
00814     } else {
00815         // not found, return 0 pointer.
00816         return EntryPtr();
00817     }
00818 }
00819 
00820 ImageCache::RequestPtr ImageCache::requestAsyncImage(const std::string & filename)
00821 {
00822     // see if we have a request already
00823     std::map<std::string, RequestPtr>::iterator it = m_requests.find(filename);
00824     if (it != m_requests.end()) {
00825         // return a copy of the existing request.
00826         return it->second;
00827     } else {
00828         bool need_thread = m_requests.empty() && m_smallRequests.empty();
00829         // Make a new request.
00830         RequestPtr request = RequestPtr(new Request(filename, false));
00831         m_requests[filename] = request;
00832         if (need_thread) {
00833             spawnAsyncThread();
00834         }
00835         return request;
00836     }
00837 }
00838 
00839 ImageCache::RequestPtr ImageCache::requestAsyncSmallImage(const std::string & filename)
00840 {
00841     // see if we have a request already
00842     std::map<std::string, RequestPtr>::iterator it = m_smallRequests.find(filename);
00843     if (it != m_smallRequests.end()) {
00844         // return a copy of the existing request.
00845         return it->second;
00846     } else {
00847         // Make a new request.
00848         bool need_thread = m_requests.empty() && m_smallRequests.empty();
00849         RequestPtr request = RequestPtr(new Request(filename, true));
00850         m_smallRequests[filename] = request;
00851         if (need_thread) {
00852             spawnAsyncThread();
00853         }
00854         return request;
00855     }
00856 }
00857 
00858 void ImageCache::postEvent(RequestPtr request, EntryPtr entry)
00859 {
00860     // This is called in the main thread, but the request and entry came from
00861     // the background loading thread, which will close itself now.
00862     bool is_small_request = request->getIsSmall();
00863     const std::string & filename = request->getFilename();
00864     // Put the loaded image in the cache.
00865     if (is_small_request) {
00866         std::string name = filename+std::string(":small");
00867         images[name] = entry;
00868     } else {
00869         images[filename] = entry;
00870     }
00871     entry->lastAccess = m_accessCounter;
00872     // Remove all the completed and no longer wanted requests from the queues.
00873     // We need to check everything, as images can be loaded synchronously after
00874     // an asynchronous request for it was made, and also something could have
00875     // given up waiting (e.g. when the user switches images faster than they
00876     // load).
00877     // Take this opportunity to give out the signals, for the image just loaded
00878     // and anything else we spot.
00879     for (std::map<std::string, RequestPtr>::iterator it = m_smallRequests.begin();
00880          it != m_smallRequests.end();)
00881     {
00882         std::map<std::string, RequestPtr>::iterator next_it = it;
00883         next_it++;
00884         if (it->second.unique()) {
00885             // Last copy of the request is in the list.
00886             // Anything requesting it must have given up waiting.
00887             m_smallRequests.erase(it);
00888             
00889         } else if (getSmallImageIfAvailable(it->first).get()) {
00890             // already loaded.
00891             // signal to anything waiting and remove from the list.
00892             it->second->ready(getSmallImage(it->first), it->first, true);
00893             m_smallRequests.erase(it);
00894         }
00895         it = next_it;
00896     }
00897     for (std::map<std::string, RequestPtr>::iterator it = m_requests.begin();
00898          it != m_requests.end();)
00899     {
00900         std::map<std::string, RequestPtr>::iterator next_it = it;
00901         next_it++;
00902         if (it->second.unique()) {
00903             // The last copy of the request is in the list of requests.
00904             // Anything that requested it must have given up waiting.
00905             // Forget about it without loading.
00906             m_requests.erase(it);
00907         } else if (getImageIfAvailable(it->first).get()) {
00908             // already loaded.
00909             // Signal to anything waiting.
00910             it->second->ready(getImage(it->first), it->first, false);
00911             m_requests.erase(it);
00912         }
00913         it = next_it;
00914     }
00915     // If there are more images to load, start the thread again.
00916     if (!(m_requests.empty() && m_smallRequests.empty())) {
00917         // Start a background thread to load another image.
00918         spawnAsyncThread();
00919     }
00920 }
00921 
00922 void ImageCache::spawnAsyncThread()
00923 {
00924     // Pick an image to load.
00925     // Try the small images first.
00926     std::map<std::string, RequestPtr>::iterator it = m_smallRequests.begin();
00927     if (it == m_smallRequests.end()) {
00928         it = m_requests.begin();
00929         if (it == m_requests.end())
00930         {
00931             DEBUG_DEBUG("Not staring a thread to load an image, since no images are wanted.");
00932         } else {
00933             boost::thread(loadSafely, it->second, EntryPtr());
00934         }
00935     } else {
00936         // got a small image request, check if its larger version has loaded.
00937         const std::string & filename = it->second->getFilename();
00938         EntryPtr large = getImageIfAvailable(filename);
00939         if (large.get() == 0)
00940         {
00941             // the larger one is needed to generate it.
00942             RequestPtr request(new Request(filename, false));
00943             boost::thread(loadSafely, request, EntryPtr());
00944         } else {
00945             // we have the large image.
00946             boost::thread(loadSafely, it->second, large);
00947         }
00948     }
00949     // thread should be processing the image asynchronously now.
00950     // Only one image is done at a time, so very little can go wrong between the
00951     // threads. The loading thread does not need to alter the ImageCache, and
00952     // there is a time when we can safely pick Requests which haven't started
00953     // loading, without giving images and lists mutexes.
00954 }
00955 
00956 void ImageCache::loadSafely(ImageCache::RequestPtr request, EntryPtr large)
00957 {
00958     // load the image
00959     EntryPtr new_entry;
00960     if (large.get())
00961     {
00962         new_entry = loadSmallImageSafely(large);
00963     } else {
00964         new_entry = loadImageSafely(request->getFilename());
00965     }
00966     // pass an event with the load image and request, which can get picked up by
00967     // the main thread later. This could be a wxEvent for example.
00968     // Check if it exists, to avoid crashing in odd cases.
00969     if (getInstance().asyncLoadCompleteSignal)
00970     {
00971         (*getInstance().asyncLoadCompleteSignal)(request, new_entry);
00972     } else {
00973         DEBUG_ERROR("Please set HuginBase::ImageCache::getInstance().asyncLoadCompleteSignal to handle asynchronous image loads.");
00974     }
00975 }
00976 
00977 } //namespace

Generated on Wed Jul 16 01:25:38 2014 for Hugintrunk by  doxygen 1.3.9.1