Hugintrunk  0.1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ImageCache.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 4 -*-
2 
27 #include "ImageCache.h"
28 
29 #include <iostream>
30 #include "hugin_config.h"
31 #include <thread>
32 #include <vigra/inspectimage.hxx>
33 #include <vigra/accessor.hxx>
34 #include <vigra/functorexpression.hxx>
35 #include <vigra/sized_int.hxx>
36 #include <vigra_ext/utils.h>
37 #include <vigra_ext/impexalpha.hxx>
38 #include <vigra_ext/Pyramid.h>
40 
41 
42 
43 namespace HuginBase {
44 
45 template <class T1>
46 class GetRange
47 {
48  public:
49  static T1 min();
50  static T1 max();
51 };
52 
53 // ImageCache::GetRange implementation
54 #define VIGRA_EXT_GETRANGE(T1, MI,MA) \
55 template<> \
56 T1 GetRange<T1>::min() \
57 { \
58  return MI; \
59 } \
60 template<> \
61 T1 GetRange<T1>::max() \
62 { \
63  return MA; \
64 } \
65 
66 VIGRA_EXT_GETRANGE(vigra::UInt8, 0, 255);
67 VIGRA_EXT_GETRANGE(vigra::Int16, 0, 32767);
68 VIGRA_EXT_GETRANGE(vigra::UInt16, 0, 65535);
69 VIGRA_EXT_GETRANGE(vigra::Int32, 0, 2147483647);
70 VIGRA_EXT_GETRANGE(vigra::UInt32, 0, 4294967295u);
71 VIGRA_EXT_GETRANGE(float, 0, 1.0f);
72 VIGRA_EXT_GETRANGE(double, 0, 1.0);
73 
74 #undef VIGRA_EXT_GETRANGE
75 
76 
77 template <class SrcIMG>
78 void convertTo8Bit(SrcIMG & src, const std::string & origType, vigra::BRGBImage & dest)
79 {
80  // code to apply the mapping to 8 bit
81  // always scaled from 0..1 for integer images.
82 
83  dest.resize(src.size());
84 
85  double min=0;
86  double max=vigra_ext::getMaxValForPixelType(origType);
87 
88  int mapping = HUGIN_IMGCACHE_MAPPING_INTEGER;
89 
90  // float needs to be from min ... max.
91  if (origType == "FLOAT" || origType == "DOUBLE")
92  {
93  vigra::RGBToGrayAccessor<vigra::RGBValue<float> > ga;
94  vigra::FindMinMax<float> minmax; // init functor
95  vigra::inspectImage(srcImageRange(src, ga),
96  minmax);
97  min = minmax.min;
98  max = minmax.max;
100  }
101  vigra_ext::applyMapping(srcImageRange(src), destImage(dest), min, max, mapping);
102 }
103 
104 
105 
107 {
108  if (image8->width() > 0) {
109  return image8;
110  } else if (image16->width() > 0) {
112  origType,
113  *image8);
114  } else if (imageFloat->width() > 0) {
116  origType,
117  *image8);
118  }
119  return image8;
120 }
121 
123 
124 
125 void ImageCache::removeImage(const std::string & filename)
126 {
127  std::map<std::string, EntryPtr>::iterator it = images.find(filename);
128  if (it != images.end()) {
129  images.erase(it);
130  }
131 
132  std::string sfilename = filename + std::string(":small");
133  it = images.find(sfilename);
134  if (it != images.end()) {
135  images.erase(it);
136  }
137 
138  int level = 0;
139  bool found = true;
140  do {
141  // found. xyz
142  PyramidKey key(filename,level);
143  std::map<std::string, vigra::BImage*>::iterator it = pyrImages.find(key.toString());
144  found = (it != pyrImages.end());
145  if (found) {
146  delete it->second;
147  pyrImages.erase(it);
148  }
149  level++;
150  } while (found);
151 }
152 
154 {
155  std::ostringstream s;
156  s << filename << level;
157  return s.str();
158 };
159 
161 {
162  images.clear();
163 
164  for (std::map<std::string, vigra::BImage*>::iterator it = pyrImages.begin();
165  it != pyrImages.end();
166  ++it)
167  {
168  delete it->second;
169  }
170  pyrImages.clear();
171 }
172 
174 {
175  if (upperBound == 0ull)
176  {
177  upperBound = 100 * 1024 * 1024ull;
178  };
179  const unsigned long long purgeToSize = static_cast<unsigned long long>(0.75 * upperBound);
180 
181  // calculate used memory
182  unsigned long long imgMem = 0;
183 
184  std::map<std::string, EntryPtr>::iterator imgIt;
185  for(imgIt=images.begin(); imgIt != images.end(); ++imgIt) {
186 #ifdef DEBUG
187  std::cout << "Image: " << imgIt->first << std::endl;
188  std::cout << "CacheEntry: " << imgIt->second.use_count() << "last access: " << imgIt->second->lastAccess;
189 #endif
190  if (imgIt->second->image8) {
191  imgMem += imgIt->second->image8->width() * imgIt->second->image8->height() * 3;
192 #ifdef DEBUG
193  std::cout << " 8bit: " << imgIt->second->image8.use_count();
194 #endif
195  }
196  if (imgIt->second->image16) {
197  imgMem += imgIt->second->image16->width() * imgIt->second->image16->height() * 3*2;
198 #ifdef DEBUG
199  std::cout << " 16bit: " << imgIt->second->image8.use_count();
200 #endif
201  }
202  if (imgIt->second->imageFloat) {
203  imgMem += imgIt->second->imageFloat->width() * imgIt->second->imageFloat->height() * 3 * 4;
204 #ifdef DEBUG
205  std::cout << " float: " << imgIt->second->imageFloat.use_count() ;
206 #endif
207  }
208  if (imgIt->second->mask) {
209  imgMem += imgIt->second->mask->width() * imgIt->second->mask->height();
210 #ifdef DEBUG
211  std::cout << " mask: " << imgIt->second->mask.use_count() << std::endl;
212 #endif
213  }
214  }
215 
216  unsigned long long pyrMem = 0;
217  std::map<std::string, vigra::BImage*>::iterator pyrIt;
218  for(pyrIt=pyrImages.begin(); pyrIt != pyrImages.end(); ++pyrIt) {
219  pyrMem += pyrIt->second->width() * pyrIt->second->height();
220  }
221 
222  const unsigned long long usedMem = imgMem + pyrMem;
223 
224  DEBUG_DEBUG("total: " << (usedMem>>20) << " MB upper bound: " << (purgeToSize>>20) << " MB");
225  if (usedMem > upperBound)
226  {
227  // we need to remove images.
228  const unsigned long long purgeAmount = usedMem - purgeToSize;
229  unsigned long long purgedMem = 0;
230  // remove images from cache, first the grey level image,
231  // then the full size images
232 
233  // use least recently uses strategy.
234  // sort images by their access time
235  std::map<int,std::string> accessMap;
236  for (std::map<std::string, EntryPtr>::iterator it = images.begin();
237  it != images.end();
238  ++it)
239  {
240  if (it->first.substr(it->first.size()-6) != ":small") {
241  // only consider full images that are not used elsewhere
242  if (it->second.unique()) {
243  DEBUG_DEBUG("Considering " << it->first << " for deletion");
244  accessMap.insert(make_pair(it->second->lastAccess, it->first));
245  } else {
246  DEBUG_DEBUG(it->first << ", usecount: " << it->second.use_count());
247  }
248  }
249  }
250  while (purgeAmount > purgedMem) {
251  bool deleted = false;
252  if (!pyrImages.empty()) {
253  vigra::BImage * imgPtr = (*(pyrImages.begin())).second;
254  purgedMem += imgPtr->width() * imgPtr->height();
255  delete imgPtr;
256  pyrImages.erase(pyrImages.begin());
257  deleted = true;
258  } else if (!accessMap.empty()) {
259  std::map<int,std::string>::iterator accIt = accessMap.begin();
260  std::map<std::string, EntryPtr>::iterator it = images.find(accIt->second);
261  // check for uniqueness.
262  if (it != images.end()) {
263  DEBUG_DEBUG("soft flush: removing image: " << it->first);
264  if (it->second->image8) {
265  purgedMem += it->second->image8->width() * it->second->image8->height() * 3;
266  }
267  if (it->second->image16) {
268  purgedMem += it->second->image16->width() * it->second->image16->height() * 3 * 2;
269  }
270  if (it->second->imageFloat) {
271  purgedMem += it->second->imageFloat->width() * it->second->imageFloat->height()*3*4;
272  }
273  if (it->second->mask) {
274  purgedMem += it->second->mask->width() * it->second->mask->height();
275  }
276  images.erase(it);
277  accessMap.erase(accIt);
278  deleted = true;
279  } else {
280  DEBUG_ASSERT("internal error while purging cache");
281  }
282  }
283  if (!deleted) {
284  break;
285  }
286  }
287  DEBUG_DEBUG("purged: " << (purgedMem>>20) << " MB, memory used for images: " << ((usedMem - purgedMem)>>20) << " MB");
288 
289  }
290 }
291 
292 
293 
295 {
296  if (instance == NULL)
297  {
298  instance = new ImageCache();
299  }
300  return *instance;
301 }
302 
303 
304 
305 /*
306 struct ApplyGammaFunctor
307 {
308  float minv;
309  float maxv;
310  float gamma;
311  float scale;
312 
313  ApplyGammaFunctor(float min_, float max_, float gamma_)
314  {
315  minv = min_;
316  maxv = max_;
317  gamma = gamma_;
318  scale = maxv - minv;
319  }
320 
321  template <class T>
322  unsigned char operator()(T v) const
323  {
324  typedef vigra::NumericTraits<vigra::UInt8> DestTraits;
325  return DestTraits::fromRealPromote(pow((float(v)-minv)/scale, gamma)*255);
326  }
327 
328  template <class T, unsigned int R, unsigned int G, unsigned int B>
329  RGBValue<vigra::UInt8,0,1,2> operator()(const RGBValue<T,R,G,B> & v) const
330  {
331  typedef vigra::NumericTraits< RGBValue<vigra::UInt8,0,1,2> > DestTraits;
332  typedef vigra::NumericTraits< RGBValue<T,R,G,B> > SrcTraits;
333  return DestTraits::fromRealPromote(pow((SrcTraits::toRealPromote(v)+(-minv))/scale, gamma)*255);
334 // return DestTraits::fromRealPromote((log10(SrcTraits::toRealPromote(v)) + (-minv))/scale);
335  }
336 };
337 */
338 
339 
340 //#if 0
342 //template <class V1, class V2>
343 //inline
344 //vigra::RGBValue<V1>
345 //operator+(const vigra::RGBValue<V1> l, V2 const & r)
346 //{
347 // return vigra::RGBValue<V1>(l.red() + r, l.green() + r, l.blue() + r);
348 //}
349 //
351 //template <class V1, class V2>
352 //inline
353 //vigra::RGBValue<V1>
354 //operator-(const vigra::RGBValue<V1> l, V2 const & r)
355 //{
356 // return vigra::RGBValue<V1>(l.red() - r, l.green() - r, l.blue() - r);
357 //}
358 //#endif
359 
360 
361 
362 
363 //struct MyMultFunc
364 //{
365 // MyMultFunc(double f)
366 // {
367 // m = f;
368 // }
369 // double m;
370 //
371 // template<class T>
372 // T
373 // operator()(T v) const
374 // {
375 // return vigra::NumericTraits<T>::fromRealPromote(v*m);
376 // }
377 //};
378 
379 template <class SrcPixelType,
380  class DestIterator, class DestAccessor>
381 void ImageCache::importAndConvertImage(const vigra::ImageImportInfo & info,
382  vigra::pair<DestIterator, DestAccessor> dest,
383  const std::string & type)
384 {
385  if (type == "FLOAT" || type == "DOUBLE" ) {
386  // import image as it is
387  vigra::importImage(info, dest);
388  } else {
389  vigra::importImage(info, dest);
390  // integer image.. scale to 0 .. 1
391  double scale = 1.0/vigra_ext::LUTTraits<SrcPixelType>::max();
392  vigra::transformImage(dest.first, dest.first + vigra::Diff2D(info.width(), info.height()), dest.second,
393  dest.first, dest.second,
394  vigra::functor::Arg1()*vigra::functor::Param(scale));
395  }
396 }
397 
398 template <class SrcPixelType,
399  class DestIterator, class DestAccessor,
400  class MaskIterator, class MaskAccessor>
401 void ImageCache::importAndConvertAlphaImage(const vigra::ImageImportInfo & info,
402  vigra::pair<DestIterator, DestAccessor> dest,
403  vigra::pair<MaskIterator, MaskAccessor> mask,
404  const std::string & type)
405 {
406  if (type == "FLOAT" || type == "DOUBLE" ) {
407  // import image as it is
408  vigra::importImageAlpha(info, dest, mask);
409  } else {
410  // integer image.. scale to 0 .. 1
411  vigra::importImageAlpha(info, dest, mask);
412  double scale = 1.0/vigra_ext::LUTTraits<SrcPixelType>::max();
413  vigra::transformImage(dest.first, dest.first + vigra::Diff2D(info.width(), info.height()), dest.second,
414  dest.first, dest.second,
415  vigra::functor::Arg1()*vigra::functor::Param(scale));
416  }
417 }
418 
419 ImageCache::EntryPtr ImageCache::getImage(const std::string & filename)
420 {
421 // softFlush();
422  m_accessCounter++;
423  std::map<std::string, EntryPtr>::iterator it;
424  it = images.find(filename);
425  if (it != images.end()) {
426  it->second->lastAccess = m_accessCounter;
427  return it->second;
428  } else {
429  if (m_progress) {
430  m_progress->setMessage("Loading image:", hugin_utils::stripPath(filename));
431  }
432 
433  EntryPtr e = loadImageSafely(filename);
434 
435  if (m_progress) {
437  }
438 
439  if (!e.get())
440  {
441  // could not access image.
442  throw std::exception();
443  }
444 
445  images[filename] = e;
446  e->lastAccess = m_accessCounter;
447  return e;
448  }
449 }
450 
452 {
453  // load images with VIGRA impex, and store either 8 bit or float images
454  std::string pixelTypeStr;
455  ImageCacheRGB8Ptr img8(new vigra::BRGBImage);
456  ImageCacheRGB16Ptr img16(new vigra::UInt16RGBImage);
457  ImageCacheRGBFloatPtr imgFloat(new vigra::FRGBImage);
458  ImageCache8Ptr mask(new vigra::BImage);
459  ImageCacheICCProfile iccProfile(new vigra::ImageImportInfo::ICCProfile);
460 
461  try {
462  vigra::ImageImportInfo info(filename.c_str());
463 
464  if (!info.getICCProfile().empty())
465  {
466  *iccProfile = info.getICCProfile();
467  };
468  int bands = info.numBands();
469  int extraBands = info.numExtraBands();
470  const char * pixelType = info.getPixelType();
471  pixelTypeStr = pixelType;
472 
473  DEBUG_DEBUG(filename << ": bands: " << bands << " extra bands: " << extraBands << " type: " << pixelType);
474 
475  if (pixelTypeStr == "UINT8") {
476  img8->resize(info.size());
477  } else if (pixelTypeStr == "UINT16" ) {
478  img16->resize(info.size());
479  } else {
480  imgFloat->resize(info.size());
481  }
482 
483  if ( bands == 1) {
484  // load and convert image to 8 bit, if needed
485  if (strcmp(pixelType, "UINT8") == 0 ) {
486  vigra::importImage(info, destImage(*img8,
487  vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)));
488  copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
489  destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(1)));
490  copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
491  destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(2)));
492  } else if (strcmp(pixelType, "UINT16") == 0 ) {
493  vigra::importImage(info, destImage(*img16,
494  vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)));
495  copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
496  destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(1)));
497  copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
498  destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(2)));
499  } else {
500  if (strcmp(pixelType, "INT16") == 0 ) {
501  importAndConvertImage<vigra::Int16> (info, destImage(*imgFloat,
502  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
503  } else if (strcmp(pixelType, "UINT32") == 0 ) {
504  importAndConvertImage<vigra::UInt32>(info, destImage(*imgFloat,
505  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
506  } else if (strcmp(pixelType, "INT32") == 0 ) {
507  importAndConvertImage<vigra::Int32>(info, destImage(*imgFloat,
508  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
509  } else if (strcmp(pixelType, "FLOAT") == 0 ) {
510  importAndConvertImage<float>(info, destImage(*imgFloat,
511  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
512  } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
513  importAndConvertImage<double>(info, destImage(*imgFloat,
514  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
515  } else {
516  DEBUG_ERROR("Unsupported pixel type: " << pixelType);
517  return EntryPtr();
518  }
519  copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
520  destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(1)));
521  copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
522  destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(2)));
523  }
524  } else if ( bands == 2 && extraBands==1) {
525  mask->resize(info.size());
526  // load and convert image to 8 bit, if needed
527  if (strcmp(pixelType, "UINT8") == 0 ) {
529  vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
530  destImage(*mask));
531  copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
532  destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(1)));
533  copyImage(srcImageRange(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(0)),
534  destImage(*img8, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt8> >(2)));
535  } else if (strcmp(pixelType, "UINT16") == 0 ) {
537  vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
538  destImage(*mask));
539  copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
540  destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(1)));
541  copyImage(srcImageRange(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(0)),
542  destImage(*img16, vigra::VectorComponentAccessor<vigra::RGBValue<vigra::UInt16> >(2)));
543  } else {
544  if (strcmp(pixelType, "INT16") == 0 ) {
545  importAndConvertAlphaImage<vigra::Int16> (info, destImage(*imgFloat,
546  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
547  } else if (strcmp(pixelType, "UINT32") == 0 ) {
548  importAndConvertAlphaImage<vigra::UInt32>(info, destImage(*imgFloat,
549  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
550  } else if (strcmp(pixelType, "INT32") == 0 ) {
551  importAndConvertAlphaImage<vigra::Int32>(info, destImage(*imgFloat,
552  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
553  } else if (strcmp(pixelType, "FLOAT") == 0 ) {
554  importAndConvertAlphaImage<float>(info, destImage(*imgFloat,
555  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
556  } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
557  importAndConvertAlphaImage<double>(info, destImage(*imgFloat,
558  vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
559  } else {
560  DEBUG_ERROR("Unsupported pixel type: " << pixelType);
561  return EntryPtr();
562  }
563  copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
564  destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(1)));
565  copyImage(srcImageRange(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)),
566  destImage(*imgFloat, vigra::VectorComponentAccessor<vigra::RGBValue<float> >(2)));
567  }
568  } else if (bands == 3 && extraBands == 0) {
569  DEBUG_DEBUG( pixelType);
570  // load and convert image to 8 bit, if needed
571  if (strcmp(pixelType, "UINT8") == 0 ) {
572  vigra::importImage(info, destImage(*img8));
573  } else if (strcmp(pixelType, "UINT16") == 0 ) {
574  vigra::importImage(info, destImage(*img16));
575  } else if (strcmp(pixelType, "INT16") == 0 ) {
576  importAndConvertImage<vigra::RGBValue<vigra::Int16> > (info, destImage(*imgFloat), pixelType);
577  } else if (strcmp(pixelType, "UINT32") == 0 ) {
578  importAndConvertImage<vigra::RGBValue<vigra::UInt32> >(info, destImage(*imgFloat), pixelType);
579  } else if (strcmp(pixelType, "INT32") == 0 ) {
580  importAndConvertImage<vigra::RGBValue<vigra::Int32> >(info, destImage(*imgFloat), pixelType);
581  } else if (strcmp(pixelType, "FLOAT") == 0 ) {
582  vigra::importImage(info, destImage(*imgFloat));
583 // importAndConvertImage<vigra::RGBValue<float> >(info, destImage(*imgFloat), pixelType);
584  } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
585  vigra::importImage(info, destImage(*imgFloat));
586 // importAndConvertImage<vigra::RGBValue<double> >(info, destImage(*imgFloat), pixelType);
587  } else {
588  DEBUG_ERROR("Unsupported pixel type: " << pixelType);
589  return EntryPtr();
590  }
591  } else if ( bands == 4 && extraBands == 1) {
592  mask->resize(info.size());
593  // load and convert image to 8 bit, if needed
594  if (strcmp(pixelType, "UINT8") == 0 ) {
596  } else if (strcmp(pixelType, "UINT16") == 0 ) {
598  } else if (strcmp(pixelType, "INT16") == 0 ) {
599  importAndConvertAlphaImage<short> (info, destImage(*imgFloat), destImage(*mask), pixelType);
600  } else if (strcmp(pixelType, "UINT32") == 0 ) {
601  importAndConvertAlphaImage<unsigned int>(info, destImage(*imgFloat), destImage(*mask), pixelType);
602  } else if (strcmp(pixelType, "INT32") == 0 ) {
603  importAndConvertAlphaImage<int>(info, destImage(*imgFloat), destImage(*mask), pixelType);
604  } else if (strcmp(pixelType, "FLOAT") == 0 ) {
605  importAndConvertAlphaImage<float>(info, destImage(*imgFloat), destImage(*mask), pixelType);
606  } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
607  importAndConvertAlphaImage<double>(info, destImage(*imgFloat), destImage(*mask), pixelType);
608  } else {
609  DEBUG_FATAL("Unsupported pixel type: " << pixelType);
610  return EntryPtr();
611  }
612  } else {
613  DEBUG_ERROR("unsupported depth, only images with 1 or 3 channel images are supported.");
614  return EntryPtr();
615  }
616  } catch (std::exception & e) {
617  // could not load image..
618  DEBUG_ERROR("Error during image reading: " << e.what());
619  return EntryPtr();
620  }
621 
622  return EntryPtr(new Entry(img8, img16, imgFloat, mask, iccProfile, pixelTypeStr));
623 }
624 
626 {
627  std::map<std::string, EntryPtr>::iterator it;
628  it = images.find(filename);
629  if (it != images.end()) {
630  m_accessCounter++;
631  it->second->lastAccess = m_accessCounter;
632  return it->second;
633  } else {
634  // not found, return 0 pointer.
635  return EntryPtr();
636  }
637 }
638 
639 ImageCache::EntryPtr ImageCache::getSmallImage(const std::string & filename)
640 {
641  m_accessCounter++;
642  softFlush();
643  std::map<std::string, EntryPtr>::iterator it;
644  // "_small" is only used internally
645  std::string name = filename + std::string(":small");
646  it = images.find(name);
647  if (it != images.end()) {
648  return it->second;
649  } else {
650  if (m_progress)
651  {
652  m_progress->setMessage("Scaling image:", hugin_utils::stripPath(filename));
653  }
654  DEBUG_DEBUG("creating small image " << name );
655  EntryPtr entry = getImage(filename);
656 
657  EntryPtr small_entry = loadSmallImageSafely(entry);
658  small_entry->lastAccess = m_accessCounter;
659  images[name] = small_entry;
660  DEBUG_INFO ( "created small image: " << name);
661  if (m_progress) {
663  }
664  return small_entry;
665  }
666 }
667 
669 {
670  // && entry->image8
671  size_t w=0;
672  size_t h=0;
673  if (entry->image8->width() > 0) {
674  w = entry->image8->width();
675  h = entry->image8->height();
676  } else if (entry->image16->width() > 0) {
677  w = entry->image16->width();
678  h = entry->image16->height();
679  } else if (entry->imageFloat->width() > 0) {
680  w = entry->imageFloat->width();
681  h = entry->imageFloat->height();
682  } else {
683  vigra_fail("Could not load image");
684  }
685 
686  size_t sz = w*h;
687  size_t smallImageSize = 800 * 800l;
688 
689  int nLevel=0;
690  while(sz > smallImageSize) {
691  sz /=4;
692  nLevel++;
693  }
694  EntryPtr e(new Entry);
695  e->origType = entry->origType;
696  // also copy icc profile
697  if (!entry->iccProfile->empty())
698  {
699  *(e->iccProfile) = *(entry->iccProfile);
700  };
701  // TODO: fix bug with mask reduction
702  vigra::BImage fullsizeMask = *(entry->mask);
703  if (entry->imageFloat->width() != 0 ) {
704  e->imageFloat = ImageCacheRGBFloatPtr(new vigra::FRGBImage);
705  if (entry->mask->width() != 0) {
706  vigra_ext::reduceNTimes(*(entry->imageFloat), fullsizeMask, *(e->imageFloat), *(e->mask), nLevel);
707  } else {
708  vigra_ext::reduceNTimes(*(entry->imageFloat), *(e->imageFloat), nLevel);
709  }
710  }
711  if (entry->image16->width() != 0 ) {
712  e->image16 = ImageCacheRGB16Ptr(new vigra::UInt16RGBImage);
713  if (entry->mask->width() != 0) {
714  vigra_ext::reduceNTimes(*(entry->image16), fullsizeMask, *(e->image16), *(e->mask), nLevel);
715  } else {
716  vigra_ext::reduceNTimes(*(entry->image16), *(e->image16), nLevel);
717  }
718  }
719  if (entry->image8->width() != 0) {
720  e->image8 = ImageCacheRGB8Ptr(new vigra::BRGBImage);
721  if (entry->mask->width() != 0) {
722  vigra_ext::reduceNTimes(*(entry->image8), fullsizeMask, *(e->image8), *(e->mask), nLevel);
723  } else {
724  vigra_ext::reduceNTimes(*(entry->image8), *(e->image8), nLevel);
725  }
726  }
727  return e;
728 }
729 
731 {
732  m_accessCounter++;
733  softFlush();
734  std::map<std::string, EntryPtr>::iterator it;
735  // "_small" is only used internally
736  std::string name = filename + std::string(":small");
737  it = images.find(name);
738  if (it != images.end()) {
739  return it->second;
740  } else {
741  // not found, return 0 pointer.
742  return EntryPtr();
743  }
744 }
745 
747 {
748  // see if we have a request already
749  std::map<std::string, RequestPtr>::iterator it = m_requests.find(filename);
750  if (it != m_requests.end()) {
751  // return a copy of the existing request.
752  return it->second;
753  } else {
754  bool need_thread = m_requests.empty() && m_smallRequests.empty();
755  // Make a new request.
756  RequestPtr request = RequestPtr(new Request(filename, false));
757  m_requests[filename] = request;
758  if (need_thread) {
760  }
761  return request;
762  }
763 }
764 
766 {
767  // see if we have a request already
768  std::map<std::string, RequestPtr>::iterator it = m_smallRequests.find(filename);
769  if (it != m_smallRequests.end()) {
770  // return a copy of the existing request.
771  return it->second;
772  } else {
773  // Make a new request.
774  bool need_thread = m_requests.empty() && m_smallRequests.empty();
775  RequestPtr request = RequestPtr(new Request(filename, true));
776  m_smallRequests[filename] = request;
777  if (need_thread) {
779  }
780  return request;
781  }
782 }
783 
785 {
786  // This is called in the main thread, but the request and entry came from
787  // the background loading thread, which will close itself now.
788  bool is_small_request = request->getIsSmall();
789  const std::string & filename = request->getFilename();
790  // Put the loaded image in the cache.
791  if (is_small_request) {
792  std::string name = filename+std::string(":small");
793  images[name] = entry;
794  } else {
795  images[filename] = entry;
796  }
797  entry->lastAccess = m_accessCounter;
798  // Remove all the completed and no longer wanted requests from the queues.
799  // We need to check everything, as images can be loaded synchronously after
800  // an asynchronous request for it was made, and also something could have
801  // given up waiting (e.g. when the user switches images faster than they
802  // load).
803  // Take this opportunity to give out the signals, for the image just loaded
804  // and anything else we spot.
805  for (std::map<std::string, RequestPtr>::iterator it = m_smallRequests.begin();
806  it != m_smallRequests.end();)
807  {
808  std::map<std::string, RequestPtr>::iterator next_it = it;
809  ++next_it;
810  if (it->second.unique()) {
811  // Last copy of the request is in the list.
812  // Anything requesting it must have given up waiting.
813  m_smallRequests.erase(it);
814 
815  } else if (getSmallImageIfAvailable(it->first).get()) {
816  // already loaded.
817  // signal to anything waiting and remove from the list.
818  while (!it->second->ready.empty())
819  {
820  it->second->ready.front()(getSmallImage(it->first), it->first, true);
821  it->second->ready.erase(it->second->ready.begin());
822  };
823  m_smallRequests.erase(it);
824  }
825  it = next_it;
826  }
827  for (std::map<std::string, RequestPtr>::iterator it = m_requests.begin();
828  it != m_requests.end();)
829  {
830  std::map<std::string, RequestPtr>::iterator next_it = it;
831  ++next_it;
832  if (it->second.unique()) {
833  // The last copy of the request is in the list of requests.
834  // Anything that requested it must have given up waiting.
835  // Forget about it without loading.
836  m_requests.erase(it);
837  } else if (getImageIfAvailable(it->first).get()) {
838  // already loaded.
839  // Signal to anything waiting.
840  while (!it->second->ready.empty())
841  {
842  it->second->ready.front()(getImage(it->first), it->first, false);
843  it->second->ready.erase(it->second->ready.begin());
844  };
845  m_requests.erase(it);
846  }
847  it = next_it;
848  }
849  // If there are more images to load, start the thread again.
850  if (!(m_requests.empty() && m_smallRequests.empty())) {
851  // Start a background thread to load another image.
853  }
854 }
855 
857 {
858  // Remove the no longer wanted requests from the queues.
859  // We need to check everything, as images can be loaded synchronously after
860  // an asynchronous request for it was made, and also something could have
861  // given up waiting (e.g. when the user switches images faster than they
862  // load).
863  // Take this opportunity to give out the signals, for the image just loaded
864  // and anything else we spot.
865  for (std::map<std::string, RequestPtr>::iterator it = m_smallRequests.begin();
866  it != m_smallRequests.end();)
867  {
868  std::map<std::string, RequestPtr>::iterator next_it = it;
869  ++next_it;
870  if (it->second.unique())
871  {
872  // Last copy of the request is in the list.
873  // Anything requesting it must have given up waiting.
874  m_smallRequests.erase(it);
875  }
876  else
877  {
878  // remove all copies from request list
879  while (!it->second->ready.empty())
880  {
881  it->second->ready.erase(it->second->ready.begin());
882  };
883  m_smallRequests.erase(it);
884  }
885  it = next_it;
886  }
887  for (std::map<std::string, RequestPtr>::iterator it = m_requests.begin();
888  it != m_requests.end();)
889  {
890  std::map<std::string, RequestPtr>::iterator next_it = it;
891  ++next_it;
892  if (it->second.unique())
893  {
894  // The last copy of the request is in the list of requests.
895  // Anything that requested it must have given up waiting.
896  // Forget about it without loading.
897  m_requests.erase(it);
898  }
899  else
900  {
901  // remove all parallel requests
902  while (!it->second->ready.empty())
903  {
904  it->second->ready.erase(it->second->ready.begin());
905  };
906  m_requests.erase(it);
907  }
908  it = next_it;
909  }
910  // If there are more images to load, start the thread again.
911  if (!(m_requests.empty() && m_smallRequests.empty()))
912  {
913  // Start a background thread to load another image.
915  }
916 }
917 
919 {
920  // Pick an image to load.
921  // Try the small images first.
922  std::map<std::string, RequestPtr>::iterator it = m_smallRequests.begin();
923  if (it == m_smallRequests.end()) {
924  it = m_requests.begin();
925  if (it == m_requests.end())
926  {
927  DEBUG_DEBUG("Not staring a thread to load an image, since no images are wanted.");
928  } else {
929  std::thread thread(loadSafely, it->second, EntryPtr());
930  thread.detach();
931  }
932  } else {
933  // got a small image request, check if its larger version has loaded.
934  const std::string & filename = it->second->getFilename();
935  EntryPtr large = getImageIfAvailable(filename);
936  if (large.get() == 0)
937  {
938  // the larger one is needed to generate it.
939  RequestPtr request(new Request(filename, false));
940  std::thread thread(loadSafely, request, EntryPtr());
941  thread.detach();
942  } else {
943  // we have the large image.
944  std::thread thread(loadSafely, it->second, large);
945  thread.detach();
946  }
947  }
948  // thread should be processing the image asynchronously now.
949  // Only one image is done at a time, so very little can go wrong between the
950  // threads. The loading thread does not need to alter the ImageCache, and
951  // there is a time when we can safely pick Requests which haven't started
952  // loading, without giving images and lists mutexes.
953 }
954 
956 {
957  // load the image
958  EntryPtr new_entry;
959  if (large.get())
960  {
961  new_entry = loadSmallImageSafely(large);
962  } else {
963  new_entry = loadImageSafely(request->getFilename());
964  }
965  // pass an event with the load image and request, which can get picked up by
966  // the main thread later. This could be a wxEvent for example.
967  // Check if it exists, to avoid crashing in odd cases.
969  {
970  (*getInstance().asyncLoadCompleteSignal)(request, new_entry);
971  } else {
972  DEBUG_ERROR("Please set HuginBase::ImageCache::getInstance().asyncLoadCompleteSignal to handle asynchronous image loads.");
973  }
974 }
975 
976 } //namespace
#define DEBUG_INFO(msg)
Definition: utils.h:69
RequestPtr requestAsyncImage(const std::string &filename)
Request an image be loaded.
Definition: ImageCache.cpp:746
void flush()
release all images in the cache.
Definition: ImageCache.cpp:160
double getMaxValForPixelType(const std::string &v)
Definition: utils.h:89
static EntryPtr loadSmallImageSafely(EntryPtr entry)
Load a small image, in a way that will work in parallel.
Definition: ImageCache.cpp:668
AppBase::ProgressDisplay * m_progress
Definition: ImageCache.h:330
void transformImage(vigra::triple< SrcImageIterator, SrcImageIterator, SrcAccessor > src, vigra::triple< DestImageIterator, DestImageIterator, DestAccessor > dest, std::pair< AlphaImageIterator, AlphaAccessor > alpha, vigra::Diff2D destUL, TRANSFORM &transform, PixelTransform &pixelTransform, bool warparound, Interpolator interpol, AppBase::ProgressDisplay *progress, bool singleThreaded=false)
Transform an image into the panorama.
Request for an image to load Connect to the ready signal so when the image loads you can respond...
Definition: ImageCache.h:117
std::shared_ptr< vigra::FRGBImage > ImageCacheRGBFloatPtr
Definition: ImageCache.h:59
void(* asyncLoadCompleteSignal)(RequestPtr, EntryPtr)
Signal for when a asynchronous load completes.
Definition: ImageCache.h:268
unsigned long long upperBound
Definition: ImageCache.h:287
std::shared_ptr< vigra::ImageImportInfo::ICCProfile > ImageCacheICCProfile
Definition: ImageCache.h:61
static void importAndConvertImage(const vigra::ImageImportInfo &info, vigra::pair< DestIterator, DestAccessor > dest, const std::string &type)
Definition: ImageCache.cpp:381
This is a cache for all the images we use.
Definition: ImageCache.h:52
#define DEBUG_ASSERT(cond)
Definition: utils.h:80
std::map< std::string, RequestPtr > m_smallRequests
Definition: ImageCache.h:338
ImageCacheRGB8Ptr image8
Definition: ImageCache.h:66
static ImageCache & getInstance()
get the global ImageCache object
Definition: ImageCache.cpp:294
void applyMapping(vigra::triple< SrcIterator, SrcIterator, SrcAccessor > img, vigra::pair< DestIterator, DestAccessor > dest, T min, T max, int mapping)
Definition: utils.h:685
EntryPtr getSmallImageIfAvailable(const std::string &filename)
Get a small image if already loaded.
Definition: ImageCache.cpp:730
functions to manage ROI&#39;s
VIGRA_EXT_GETRANGE(vigra::UInt8, 0, 255)
#define DEBUG_FATAL(msg)
Definition: utils.h:78
information about an image inside the cache
Definition: ImageCache.h:64
std::shared_ptr< vigra::BImage > ImageCache8Ptr
Definition: ImageCache.h:60
RequestPtr requestAsyncSmallImage(const std::string &filename)
Request a small image be loaded.
Definition: ImageCache.cpp:765
void taskFinished()
call when a task has finished and the status message should be cleared
static EntryPtr loadImageSafely(const std::string &filename)
Load a full size image, in a way that will work in parallel.
Definition: ImageCache.cpp:451
std::shared_ptr< Entry > EntryPtr
a shared pointer to the entry
Definition: ImageCache.h:112
void postEvent(RequestPtr request, EntryPtr entry)
Pass on a loaded event for any images loaded asynchronously.
Definition: ImageCache.cpp:784
std::shared_ptr< vigra::BRGBImage > ImageCacheRGB8Ptr
use reference counted pointers
Definition: ImageCache.h:57
ImageCacheRGB8Ptr get8BitImage()
Definition: ImageCache.cpp:106
get a pyramid image.
Definition: ImageCache.h:383
EntryPtr getImageIfAvailable(const std::string &filename)
Get an image if already loaded.
Definition: ImageCache.cpp:625
ImageCacheRGB16Ptr image16
Definition: ImageCache.h:67
std::map< std::string, RequestPtr > m_requests
Definition: ImageCache.h:335
std::shared_ptr< Request > RequestPtr
Reference counted request for an image to load.
Definition: ImageCache.h:151
#define HUGIN_IMGCACHE_MAPPING_INTEGER
void setMessage(const std::string &message, const std::string &filename="")
sets the message to given string
IMPEX double h[25][1024]
Definition: emor.cpp:169
EntryPtr getSmallImage(const std::string &filename)
get an small image.
Definition: ImageCache.cpp:639
vigra::pair< typename ROIImage< Image, Alpha >::image_traverser, typename ROIImage< Image, Alpha >::ImageAccessor > destImage(ROIImage< Image, Alpha > &img)
Definition: ROIImage.h:324
static ImageCache * instance
Definition: ImageCache.h:173
#define DEBUG_ERROR(msg)
Definition: utils.h:76
vigra::triple< typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::image_const_traverser, typename ROIImage< Image, Mask >::ImageConstAccessor > srcImageRange(const ROIImage< Image, Mask > &img)
helper function for ROIImages
Definition: ROIImage.h:287
ImageCacheRGBFloatPtr imageFloat
Definition: ImageCache.h:68
void convertTo8Bit(SrcIMG &src, const std::string &origType, vigra::BRGBImage &dest)
Definition: ImageCache.cpp:78
std::map< std::string, EntryPtr > images
Definition: ImageCache.h:327
void softFlush()
a soft version of flush.
Definition: ImageCache.cpp:173
void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
Read the image specified by the given vigra::ImageImportInfo object including its alpha channel...
Definition: impexalpha.hxx:479
EntryPtr getImage(const std::string &filename)
get a image.
Definition: ImageCache.cpp:419
static T max(T x, T y)
Definition: svm.cpp:65
void removeRequest(RequestPtr request)
Removes the given RequestPtr from queue, Call from main GUI thread when an image could not loaded...
Definition: ImageCache.cpp:856
#define DEBUG_DEBUG(msg)
Definition: utils.h:68
std::map< std::string, vigra::BImage * > pyrImages
Definition: ImageCache.h:396
void copyImage(SrcImageIterator src_upperleft, SrcImageIterator src_lowerright, SrcAccessor src_acc, DestImageIterator dest_upperleft, DestAccessor dest_acc)
Definition: openmp_vigra.h:305
static void info(const char *fmt,...)
Definition: svm.cpp:95
#define HUGIN_IMGCACHE_MAPPING_FLOAT
void removeImage(const std::string &filename)
remove a specific image (and dependant images) from the cache
Definition: ImageCache.cpp:125
void reduceNTimes(ImageIn &in, Image &out, int n)
Definition: Pyramid.h:39
std::shared_ptr< vigra::UInt16RGBImage > ImageCacheRGB16Ptr
Definition: ImageCache.h:58
static void importAndConvertAlphaImage(const vigra::ImageImportInfo &info, vigra::pair< DestIterator, DestAccessor > dest, vigra::pair< MaskIterator, MaskAccessor > mask, const std::string &type)
Definition: ImageCache.cpp:401
static T min(T x, T y)
Definition: svm.cpp:62
void spawnAsyncThread()
Start a background thread to load an image.
Definition: ImageCache.cpp:918
std::string stripPath(const std::string &filename)
remove the path of a filename (mainly useful for gui display of filenames)
Definition: utils.cpp:160
static void loadSafely(RequestPtr request, EntryPtr large=EntryPtr())
Load a requested image in a way that will work in parallel.
Definition: ImageCache.cpp:955