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

Generated on 2 Dec 2016 for Hugintrunk by  doxygen 1.4.7