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

Generated on 30 Aug 2015 for Hugintrunk by  doxygen 1.4.7