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

Generated on 10 Feb 2016 for Hugintrunk by  doxygen 1.4.7