Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
253 changes: 253 additions & 0 deletions src/qtAliceVision/AsyncFetcher.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
#include "AsyncFetcher.hpp"

#include <QMutexLocker>
#include <QPoint>

#include <thread>
#include <chrono>

using namespace aliceVision;

namespace qtAliceVision {
namespace imgserve {

AsyncFetcher::AsyncFetcher()
{
_resizeRatio = 0.001;
_isAsynchroneous = false;
_requestSynchroneous = false;
}

AsyncFetcher::~AsyncFetcher()
{

}

void AsyncFetcher::setSequence(const std::vector<std::string> & paths)
{
//Sequence can't be changed while thread is running
if (_isAsynchroneous)
{
return;
}

_sequence = paths;
_currentIndex = 0;

for (unsigned idx = 0; idx < _sequence.size(); idx++)
{
_pathToSeqId[_sequence[idx]] = idx;
}
}

void AsyncFetcher::setResizeRatio(double ratio)
{
QMutexLocker locker(&_mutexResizeRatio);
_resizeRatio = ratio;
}

void AsyncFetcher::setCache(ImageCache::uptr && cache)
{
//Cache can't be changed while thread is running
if (_isAsynchroneous)
{
return;
}
_cache = std::move(cache);
}


void AsyncFetcher::run()
{
using namespace std::chrono_literals;

_isAsynchroneous = true;
_requestSynchroneous = false;

int previousCacheSize = getDiskLoads();

while (1)
{
if (_requestSynchroneous)
{
_requestSynchroneous = false;
break;
}

if (_sequence.size() == 0)
{
std::this_thread::sleep_for(100ms);
}
else
{
const std::string & lpath = _sequence[_currentIndex];

//Load in cache
if (_cache)
{
double ratio;
{
QMutexLocker locker(&_mutexResizeRatio);
ratio = _resizeRatio;
}

_cache->get<image::RGBAfColor>(lpath, _currentIndex, ratio, false);
}

_currentIndex++;

int size = _sequence.size();
if (_currentIndex >= size)
{
_currentIndex = 0;
}

std::this_thread::sleep_for(1ms);
}

int cacheSize = getDiskLoads();
if (cacheSize != previousCacheSize)
{
previousCacheSize = cacheSize;
Q_EMIT onAsyncFetchProgressed();
}
}

_requestSynchroneous = false;
_isAsynchroneous = false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_requestSynchroneous = false;
_isAsynchroneous = false;
_requestSynchronous = false;
_isAsynchronous = false;

}

void AsyncFetcher::stopAsync()
{
_requestSynchroneous = true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_requestSynchroneous = true;
_requestSynchronous = true;

}

void AsyncFetcher::updateCacheMemory(size_t maxMemory)
{
if (_cache)
{
_cache->updateMaxMemory(maxMemory);
}
}

size_t AsyncFetcher::getCacheSize() const
{
return (_cache)?_cache->info().getContentSize():0.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (_cache)?_cache->info().getContentSize():0.0f;
return (_cache) ? _cache->info().getContentSize() : 0.0f;

}

size_t AsyncFetcher::getDiskLoads() const
{
return (_cache)?_cache->info().getLoadFromDisk():0.0f;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return (_cache)?_cache->info().getLoadFromDisk():0.0f;
return (_cache) ? _cache->info().getLoadFromDisk() : 0.0f;

}


QVariantList AsyncFetcher::getCachedFrames() const
{
QVariantList intervals;

if (!_cache)
{
return intervals;
}

// Accumulator variables
auto region = std::make_pair(-1, -1);
bool regionOpen = false;

size_t size = _sequence.size();

{
// Build cached frames intervals
for (std::size_t i = 0; i < size; ++i)
{
const int frame = static_cast<int>(i);

// Check if current frame is in cache
if (_cache->contains<aliceVision::image::RGBAfColor>(_sequence[i], _resizeRatio))
{
// Either grow currently open region or create a new region
if (regionOpen)
{
region.second = frame;
}
else
{
region.first = frame;
region.second = frame;
regionOpen = true;
}
}
else
{
// Close currently open region
if (regionOpen)
{
intervals.append(QPoint(region.first, region.second));
regionOpen = false;
}
}
}
}

// Last region may still be open
if (regionOpen)
{
intervals.append(QPoint(region.first, region.second));
}

return intervals;
}

bool AsyncFetcher::getFrame(const std::string & path,
std::shared_ptr<image::Image<image::RGBAfColor>> & image,
oiio::ParamValueList & metadatas,
size_t & originalWidth,
size_t & originalHeight)
{
//Need a cache
if (!_cache)
{
return false;
}

// First try getting the image
bool onlyCache = _isAsynchroneous;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bool onlyCache = _isAsynchroneous;
bool onlyCache = _isAsynchronous;


//Upgrade the thread with the current Index
for (int idx = 0; idx < _sequence.size(); ++idx)
{
if (_sequence[idx] == path)
{
_currentIndex = idx;
break;
}
}

std::optional<CacheValue> ovalue = _cache->get<aliceVision::image::RGBAfColor>(path, _currentIndex, _resizeRatio, onlyCache);


if (ovalue.has_value())
{
auto & value = ovalue.value();
image = value.get<aliceVision::image::RGBAfColor>();

oiio::ParamValueList copy_metadatas = value.getMetadatas();
metadatas = copy_metadatas;
originalWidth = value.getOriginalWidth();
originalHeight = value.getOriginalHeight();

if (image)
{
_cache->setReferenceFrameId(_currentIndex);
}

return true;
}

return false;
}

}
}

#include "AsyncFetcher.moc"
117 changes: 117 additions & 0 deletions src/qtAliceVision/AsyncFetcher.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

#include <QObject>
#include <QRunnable>
#include <QMutex>
#include <QVariantList>


#include "ImageCache.hpp"

namespace qtAliceVision {
namespace imgserve {

class AsyncFetcher : public QObject, public QRunnable
{
Q_OBJECT

public:
AsyncFetcher();
~AsyncFetcher();

/**
* @brief Cache object is created externally.
* Pass it to the Fetcher for use (Fetcher get ownership)
* @param cache the cache object to store
*/
void setCache(ImageCache::uptr && cache);

/**
* @brief set the image sequence
* The image sequence is a list of image paths which is ordered
* The Fetcher must not be in asynchroneous mode for this function to work
* As such, the _sequence object is only used in read mode during async mode.
*/
void setSequence(const std::vector<std::string> & paths);

/**
* @brief update the resize ratio of the image
* @param ratio the coefficient of resize of the loaded images
*/
void setResizeRatio(double ratio);

/**
* @brief retrieve a frame from the cache in both sync and async mode
* @param path the image path which should be contained in _sequence.
* @param image the result image pointer
* @param metadatas the image metadatas found in the file
* @param originalWidth the image width before the resize
* @param originalHeight the image height before the resize
* @return true if the image was succesfully found in the cache
*/
bool getFrame(const std::string & path,
std::shared_ptr<aliceVision::image::Image<aliceVision::image::RGBAfColor>> & image,
oiio::ParamValueList & metadatas,
size_t & originalWidth,
size_t & originalHeight);


/**
* @brief Internal function for QT to start the asynchroneous mode
*/
Q_SLOT void run() override;

/**
* @brief stop asynchroneous mode
* The caller have to wait on the thread pool to guarantee the effective end
*/
void stopAsync();

bool isAsync() const
{
return _isAsynchroneous;
}

/**
* @brief get the cache content size in bytes
* @return the cache content size in bytes
*/
size_t getCacheSize() const;

/**
* @brief get the number of images loaded
* @return the count of images loaded since the creation of the cache object
*/
size_t getDiskLoads() const;

/**
* @brief update maxMemory for the cache
* @param maxMemory the number of bytes allowed in the cache
*/
void updateCacheMemory(size_t maxMemory);

/**
* @brief get a list of regions containing the image frames
* @return a list of two values (begin, end)
*/
QVariantList getCachedFrames() const;

public:
Q_SIGNAL void onAsyncFetchProgressed();

private:
ImageCache::uptr _cache;

std::vector<std::string> _sequence;
std::unordered_map<std::string, unsigned> _pathToSeqId;

QAtomicInt _currentIndex;
QAtomicInt _isAsynchroneous;
QAtomicInt _requestSynchroneous;

double _resizeRatio;
QMutex _mutexResizeRatio;
};

} // namespace imgserve
} // namespace qtAliceVision
6 changes: 6 additions & 0 deletions src/qtAliceVision/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ set(PLUGIN_SOURCES
Painter.cpp
SequenceCache.cpp
SingleImageLoader.cpp
ImageCache.cpp
AsyncFetcher.cpp
)

set(PLUGIN_HEADERS
Expand All @@ -32,6 +34,9 @@ set(PLUGIN_HEADERS
ImageServer.hpp
SequenceCache.hpp
SingleImageLoader.hpp
ImageCache.hpp
AsyncFetcher.hpp

)

set(PLUGIN_MOCS
Expand All @@ -43,6 +48,7 @@ set(PLUGIN_MOCS
MSfMDataStats.hpp
SequenceCache.hpp
SingleImageLoader.hpp
AsyncFetcher.hpp
)


Expand Down
Loading