Hugin trunk 0.1
Loading...
Searching...
No Matches
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>
38#include <vigra_ext/Pyramid.h>
40
41namespace HuginBase {
42
43template <class SrcIMG>
44void 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
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());
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) {
78 *image8);
79 } else if (imageFloat->width() > 0) {
82 *image8,
84 }
85 return image8;
86}
87
89
90
91void 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
267template <class SrcPixelType,
268 class DestIterator, class DestAccessor>
269void 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
286template <class SrcPixelType,
287 class DestIterator, class DestAccessor,
288 class MaskIterator, class MaskAccessor>
289void 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
307ImageCache::EntryPtr ImageCache::getImage(const std::string & filename)
308{
309// softFlush();
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();
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 ) {
390 vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
391 } else if (strcmp(pixelType, "UINT32") == 0 ) {
393 vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), pixelType);
394 } else if (strcmp(pixelType, "INT32") == 0 ) {
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 ) {
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 ) {
416 vigra::importImageAlpha(info, destImage(*img8,
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 ) {
424 vigra::importImageAlpha(info, destImage(*img16,
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 ) {
434 vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
435 } else if (strcmp(pixelType, "UINT32") == 0 ) {
437 vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
438 } else if (strcmp(pixelType, "INT32") == 0 ) {
440 vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
441 } else if (strcmp(pixelType, "FLOAT") == 0 ) {
443 vigra::VectorComponentAccessor<vigra::RGBValue<float> >(0)), destImage(*mask), pixelType);
444 } else if (strcmp(pixelType, "DOUBLE") == 0 ) {
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) {
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 ) {
465 } else if (strcmp(pixelType, "UINT32") == 0 ) {
467 } else if (strcmp(pixelType, "INT32") == 0 ) {
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 ) {
483 vigra::importImageAlpha(info, destImage(*img8), destImage(*mask));
484 } else if (strcmp(pixelType, "UINT16") == 0 ) {
485 vigra::importImageAlpha(info, destImage(*img16), destImage(*mask));
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()) {
519 it->second->lastAccess = m_accessCounter;
520 return it->second;
521 } else {
522 // not found, return 0 pointer.
523 return EntryPtr();
524 }
525}
526
528{
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
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{
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();
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
847 if (large.get())
848 {
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 {
859 } else {
860 DEBUG_ERROR("Please set HuginBase::ImageCache::getInstance().asyncLoadCompleteSignal to handle asynchronous image loads.");
861 }
862}
863
864} //namespace
void setMessage(const std::string &message, const std::string &filename="")
sets the message to given string
void taskFinished()
call when a task has finished and the status message should be cleared
Request for an image to load Connect to the ready signal so when the image loads you can respond.
Definition ImageCache.h:118
This is a cache for all the images we use.
Definition ImageCache.h:53
std::shared_ptr< vigra::FRGBImage > ImageCacheRGBFloatPtr
Definition ImageCache.h:59
AppBase::ProgressDisplay * m_progress
Definition ImageCache.h:317
static void importAndConvertImage(const vigra::ImageImportInfo &info, vigra::pair< DestIterator, DestAccessor > dest, const std::string &type)
std::shared_ptr< vigra::BRGBImage > ImageCacheRGB8Ptr
use reference counted pointers
Definition ImageCache.h:57
EntryPtr getSmallImage(const std::string &filename)
get an small image.
std::shared_ptr< vigra::BImage > ImageCache8Ptr
Definition ImageCache.h:60
std::map< std::string, RequestPtr > m_smallRequests
Definition ImageCache.h:325
void spawnAsyncThread()
Start a background thread to load an image.
static void loadSafely(RequestPtr request, EntryPtr large=EntryPtr())
Load a requested image in a way that will work in parallel.
void removeRequest(RequestPtr request)
Removes the given RequestPtr from queue, Call from main GUI thread when an image could not loaded.
EntryPtr getSmallImageIfAvailable(const std::string &filename)
Get a small image if already loaded.
static ImageCache & getInstance()
get the global ImageCache object
EntryPtr getImageIfAvailable(const std::string &filename)
Get an image if already loaded.
EntryPtr getImage(const std::string &filename)
get a image.
static ImageCache * instance
Definition ImageCache.h:173
RequestPtr requestAsyncSmallImage(const std::string &filename)
Request a small image be loaded.
RequestPtr requestAsyncImage(const std::string &filename)
Request an image be loaded.
std::map< std::string, RequestPtr > m_requests
Definition ImageCache.h:322
unsigned long long upperBound
Definition ImageCache.h:287
void removeImage(const std::string &filename)
remove a specific image (and dependant images) from the cache
std::shared_ptr< vigra::UInt16RGBImage > ImageCacheRGB16Ptr
Definition ImageCache.h:58
static EntryPtr loadSmallImageSafely(EntryPtr entry)
Load a small image, in a way that will work in parallel.
void postEvent(RequestPtr request, EntryPtr entry)
Pass on a loaded event for any images loaded asynchronously.
std::map< std::string, vigra::BImage * > pyrImages
Definition ImageCache.h:383
void(* asyncLoadCompleteSignal)(RequestPtr, EntryPtr)
Signal for when a asynchronous load completes.
Definition ImageCache.h:268
std::shared_ptr< Request > RequestPtr
Reference counted request for an image to load.
Definition ImageCache.h:151
std::shared_ptr< vigra::ImageImportInfo::ICCProfile > ImageCacheICCProfile
Definition ImageCache.h:61
std::shared_ptr< Entry > EntryPtr
a shared pointer to the entry
Definition ImageCache.h:112
static EntryPtr loadImageSafely(const std::string &filename)
Load a full size image, in a way that will work in parallel.
void flush()
release all images in the cache.
static void importAndConvertAlphaImage(const vigra::ImageImportInfo &info, vigra::pair< DestIterator, DestAccessor > dest, vigra::pair< MaskIterator, MaskAccessor > mask, const std::string &type)
std::map< std::string, EntryPtr > images
Definition ImageCache.h:314
void softFlush()
a soft version of flush.
#define HUGIN_IMGCACHE_MAPPING_INTEGER
#define HUGIN_IMGCACHE_MAPPING_FLOAT
class AlphaIterator class AlphaAccessor inline void importImageAlpha(const ImageImportInfo &import_info, ImageIterator image_iterator, ImageAccessor image_accessor, AlphaIterator alpha_iterator, AlphaAccessor alpha_accessor)
#define DEBUG_ERROR(msg)
Definition utils.h:76
#define DEBUG_ASSERT(cond)
Definition utils.h:80
#define DEBUG_DEBUG(msg)
Definition utils.h:68
#define DEBUG_FATAL(msg)
Definition utils.h:78
#define DEBUG_INFO(msg)
Definition utils.h:69
mainly consists of wrapper around the pano tools library, to assist in ressource management and to pr...
Definition wxcms.cpp:39
void convertTo8Bit(SrcIMG &src, const std::string &origType, vigra::BRGBImage &dest, int desiredMapping=-1)
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 reduceNTimes(ImageIn &in, Image &out, int n)
Definition Pyramid.h:39
double getMaxValForPixelType(const std::string &v)
Definition utils.h:89
void applyMapping(vigra::triple< SrcIterator, SrcIterator, SrcAccessor > img, vigra::pair< DestIterator, DestAccessor > dest, T min, T max, int mapping)
Definition utils.h:685
information about an image inside the cache
Definition ImageCache.h:65
ImageCacheRGB16Ptr image16
Definition ImageCache.h:67
ImageCacheRGB8Ptr image8
Definition ImageCache.h:66
ImageCacheRGBFloatPtr imageFloat
Definition ImageCache.h:68
ImageCacheRGB8Ptr get8BitImage(int desiredMapping=-1)
std::vector< deghosting::BImagePtr > threshold(const std::vector< deghosting::FImagePtr > &inputImages, const double threshold, const uint16_t flags)
Threshold function used for creating alpha masks for images.
Definition threshold.h:41
functions to manage ROI's