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

Generated on 30 Jul 2016 for Hugintrunk by  doxygen 1.4.7