Skip to content

Commit

Permalink
[imageCaching] support all downscale levels instead of just powers of…
Browse files Browse the repository at this point in the history
… two
  • Loading branch information
mugulmd committed Mar 2, 2023
1 parent ed8cdae commit 5acb92a
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 71 deletions.
3 changes: 1 addition & 2 deletions src/aliceVision/image/imageCaching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ std::string ImageCache::toString() const
std::string keyDesc = key.filename +
", nbChannels: " + std::to_string(key.nbChannels) +
", typeDesc: " + std::to_string(key.typeDesc) +
", halfSampleLevel: " + std::to_string(key.halfSampleLevel) +
", downscaleLevel: " + std::to_string(key.downscaleLevel) +
", usages: " + std::to_string(_imagePtrs.at(key).useCount()) +
", size: " + std::to_string(_imagePtrs.at(key).memorySize());
description += "\n * " + keyDesc;
Expand All @@ -151,7 +151,6 @@ std::string ImageCache::toString() const
std::string statsDesc = "\nUsage statistics: "
"\n * nb load from disk: " + std::to_string(_info.nbLoadFromDisk) +
"\n * nb load from cache: " + std::to_string(_info.nbLoadFromCache) +
"\n * nb load from higher scale: " + std::to_string(_info.nbLoadFromHigherScale) +
"\n * nb remove unused: " + std::to_string(_info.nbRemoveUnused);
description += statsDesc;

Expand Down
66 changes: 17 additions & 49 deletions src/aliceVision/image/imageCaching.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,21 @@ namespace aliceVision {
namespace image {

/**
* @brief A struct used to identify a cached image using its file description, color type info and half-sampling level.
* @brief A struct used to identify a cached image using its file description, color type info and downscale level.
*/
struct CacheKey
{
std::string filename;
int nbChannels;
oiio::TypeDesc::BASETYPE typeDesc;
int halfSampleLevel;
int downscaleLevel;
std::time_t lastWriteTime;

CacheKey(const std::string& path, int nchannels, oiio::TypeDesc::BASETYPE baseType, int level, std::time_t time) :
filename(path),
nbChannels(nchannels),
typeDesc(baseType),
halfSampleLevel(level),
downscaleLevel(level),
lastWriteTime(time)
{
}
Expand All @@ -52,7 +52,7 @@ struct CacheKey
return (filename == other.filename &&
nbChannels == other.nbChannels &&
typeDesc == other.typeDesc &&
halfSampleLevel == other.halfSampleLevel &&
downscaleLevel == other.downscaleLevel &&
lastWriteTime == other.lastWriteTime);
}
};
Expand All @@ -65,7 +65,7 @@ struct CacheKeyHasher
boost::hash_combine(seed, key.filename);
boost::hash_combine(seed, key.nbChannels);
boost::hash_combine(seed, key.typeDesc);
boost::hash_combine(seed, key.halfSampleLevel);
boost::hash_combine(seed, key.downscaleLevel);
boost::hash_combine(seed, key.lastWriteTime);
return seed;
}
Expand All @@ -88,7 +88,6 @@ struct CacheInfo
/// usage statistics
int nbLoadFromDisk = 0;
int nbLoadFromCache = 0;
int nbLoadFromHigherScale = 0;
int nbRemoveUnused = 0;

CacheInfo(float capacity_MiB, float maxSize_MiB) :
Expand Down Expand Up @@ -193,11 +192,6 @@ inline std::shared_ptr<Image<RGBAfColor>> CacheValue::get<RGBAfColor>() { return
* or until there is nothing to remove
* 5. if the image fits in the maximal size, load it, store it and return it
* 6. the image is too big for the cache, throw an error.
*
* In the process described above, we also take advantage of the cache content when loading an image:
* if the same image with a lower half-sampling level (i.e. higher resolution) exists in the cache,
* we take the high-resolution version of the image from the cache and create and new downscaled version of it
* instead of loading the image from disk.
*/
class ImageCache
{
Expand All @@ -220,15 +214,15 @@ class ImageCache
ImageCache& operator=(const ImageCache&) = delete;

/**
* @brief Retrieve a cached image at a given half-sampling level.
* @brief Retrieve a cached image at a given downscale level.
* @note This method is thread-safe.
* @param[in] filename the image's filename on disk
* @param[in] halfSampleLevel the half-sampling level
* @param[in] downscaleLevel the downscale level
* @return a shared pointer to the cached image
* @throws std::runtime_error if the image does not fit in the maximal size of the cache
*/
template<typename TPix>
std::shared_ptr<Image<TPix>> get(const std::string& filename, int halfSampleLevel);
std::shared_ptr<Image<TPix>> get(const std::string& filename, int downscaleLevel);

/**
* @return information on the current cache state and usage
Expand Down Expand Up @@ -274,18 +268,18 @@ class ImageCache
// their definition must be given in this header file

template<typename TPix>
std::shared_ptr<Image<TPix>> ImageCache::get(const std::string& filename, int halfSampleLevel)
std::shared_ptr<Image<TPix>> ImageCache::get(const std::string& filename, int downscaleLevel)
{
const std::lock_guard<std::mutex> lock(_mutex);

ALICEVISION_LOG_TRACE("[image] ImageCache: reading " << filename
<< " with half-sampling level " << halfSampleLevel
<< " with downscale level " << downscaleLevel
<< " from thread " << std::this_thread::get_id());

using TInfo = ColorTypeInfo<TPix>;

auto lastWriteTime = boost::filesystem::last_write_time(filename);
CacheKey keyReq(filename, TInfo::size, TInfo::typeDesc, halfSampleLevel, lastWriteTime);
CacheKey keyReq(filename, TInfo::size, TInfo::typeDesc, downscaleLevel, lastWriteTime);

// find the requested image in the cached images
{
Expand All @@ -306,8 +300,7 @@ std::shared_ptr<Image<TPix>> ImageCache::get(const std::string& filename, int ha
// retrieve image size
int width, height;
readImageSize(filename, width, height);
int downscale = 1 << halfSampleLevel;
unsigned long long int memSize = (width / downscale) * (height / downscale) * sizeof(TPix);
unsigned long long int memSize = (width / downscaleLevel) * (height / downscaleLevel) * sizeof(TPix);

// add image to cache if it fits in capacity
if (memSize + _info.contentSize <= _info.capacity)
Expand Down Expand Up @@ -391,38 +384,13 @@ void ImageCache::load(const CacheKey& key)
{
auto img = std::make_shared<Image<TPix>>();

// find the same image with a higher scale
auto it = std::find_if(_keys.begin(), _keys.end(), [&key](const CacheKey& k){
return k.filename == key.filename &&
k.nbChannels == key.nbChannels &&
k.typeDesc == key.typeDesc &&
k.halfSampleLevel < key.halfSampleLevel &&
k.lastWriteTime == key.lastWriteTime;
});

if (it != _keys.end())
{
// retrieve high-scale image from cache
const CacheKey& keyHighScale = *it;
CacheValue& valueHighScale = _imagePtrs.at(keyHighScale);

// apply downscale
int downscale = 1 << (key.halfSampleLevel - keyHighScale.halfSampleLevel);
imageAlgo::resizeImage(downscale, *(valueHighScale.get<TPix>()), *img);
// load image from disk
readImage(key.filename, *img, _options);

_info.nbLoadFromHigherScale++;
}
else
{
// load image from disk
readImage(key.filename, *img, _options);
// apply downscale
imageAlgo::resizeImage(key.downscaleLevel, *img);

// apply downscale
int downscale = 1 << key.halfSampleLevel;
imageAlgo::resizeImage(downscale, *img);

_info.nbLoadFromDisk++;
}
_info.nbLoadFromDisk++;

// create wrapper around shared pointer
CacheValue value = CacheValue::wrap(img);
Expand Down
13 changes: 0 additions & 13 deletions src/aliceVision/image/imageCaching_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,6 @@ BOOST_AUTO_TEST_CASE(load_image_twice) {
BOOST_CHECK_EQUAL(cache.info().nbLoadFromCache, 1);
}

BOOST_AUTO_TEST_CASE(reload_lower_scale) {
ImageCache cache(256, 1024, EImageColorSpace::LINEAR);
const std::string filename = std::string(THIS_SOURCE_DIR) + "/image_test/lena.png";
auto img1 = cache.get<RGBAfColor>(filename, 0);
auto img2 = cache.get<RGBAfColor>(filename, 1);
BOOST_CHECK_EQUAL(img1->Width() / 2, img2->Width());
BOOST_CHECK_EQUAL(img1->Height() / 2, img2->Height());
BOOST_CHECK_EQUAL(cache.info().nbImages, 2);
BOOST_CHECK_EQUAL(cache.info().contentSize, img1->MemorySize() + img2->MemorySize());
BOOST_CHECK_EQUAL(cache.info().nbLoadFromDisk, 1);
BOOST_CHECK_EQUAL(cache.info().nbLoadFromHigherScale, 1);
}

BOOST_AUTO_TEST_CASE(load_all_pixel_types) {
ImageCache cache(256, 1024, EImageColorSpace::LINEAR);
const std::string filename = std::string(THIS_SOURCE_DIR) + "/image_test/lena.png";
Expand Down
14 changes: 7 additions & 7 deletions src/samples/imageCaching/main_imageCaching.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ int aliceVision_main(int argc, char **argv)
float capacity = 256.f;
float maxSize = 1024.f;
std::vector<std::string> filenames;
std::vector<int> halfSampleLevels;
int defaultHalfSampleLevel = 0;
std::vector<int> downscaleLevels;
int defaultDownscaleLevel = 1;
std::vector<int> pixelTypes;
int defaultPixelType = 5;
int nbThreads = 1;
Expand All @@ -50,10 +50,10 @@ int aliceVision_main(int argc, char **argv)
"Cache capacity")
("maxSize", po::value<float>(&maxSize)->default_value(maxSize),
"Cache max size")
("halfSampleLevels", po::value<std::vector<int>>(&halfSampleLevels)->multitoken()->default_value(halfSampleLevels),
"Half-sampling levels")
("defaultHalfSampleLevel", po::value<int>(&defaultHalfSampleLevel)->default_value(defaultHalfSampleLevel),
"Default half-sampling level")
("downscaleLevels", po::value<std::vector<int>>(&downscaleLevels)->multitoken()->default_value(downscaleLevels),
"Downscale levels")
("defaultDownscaleLevel", po::value<int>(&defaultDownscaleLevel)->default_value(defaultDownscaleLevel),
"Default downscale level")
("pixelTypes", po::value<std::vector<int>>(&pixelTypes)->multitoken()->default_value(pixelTypes),
"Pixel types:"
"\n * 0: unsigned char"
Expand Down Expand Up @@ -90,7 +90,7 @@ int aliceVision_main(int argc, char **argv)
for (int j = 0; j < filenames.size(); j++)
{
const std::string& filename = filenames[j];
const int level = (j < halfSampleLevels.size()) ? halfSampleLevels[j] : defaultHalfSampleLevel;
const int level = (j < downscaleLevels.size()) ? downscaleLevels[j] : defaultDownscaleLevel;
const int pixelType = (j < pixelTypes.size()) ? pixelTypes[j] : defaultPixelType;
switch (pixelType)
{
Expand Down

0 comments on commit 5acb92a

Please sign in to comment.