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

Generated on 27 May 2016 for Hugintrunk by  doxygen 1.4.7