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