Skip to content

Commit 6dbc0da

Browse files
authored
Merge pull request #76 from alicevision/dev/sequencePlayer
[qtAliceVision] Improve the Sequence Player
2 parents 870058f + 02206d4 commit 6dbc0da

File tree

10 files changed

+999
-614
lines changed

10 files changed

+999
-614
lines changed

.git-blame-ignore-revs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# [qtAliceVision] Reformat all sequence cache-related files
2+
d304d8655de26ed5702966b4d855b031f85268f1
13
# [qtAliceVision] Apply clang-format on modified files
24
d0fd33d78af7d73f4c1c5ba0de7a1d49d263f5d0
35
# Reformat all plugins with latest clang-format rules

src/qtAliceVision/AsyncFetcher.cpp

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
#include "AsyncFetcher.hpp"
2+
3+
#include <QMutexLocker>
4+
#include <QPoint>
5+
6+
#include <thread>
7+
#include <chrono>
8+
9+
using namespace aliceVision;
10+
11+
namespace qtAliceVision {
12+
namespace imgserve {
13+
14+
AsyncFetcher::AsyncFetcher()
15+
{
16+
_resizeRatio = 0.001;
17+
_isAsynchronous = false;
18+
_requestSynchronous = false;
19+
}
20+
21+
AsyncFetcher::~AsyncFetcher() {}
22+
23+
void AsyncFetcher::setSequence(const std::vector<std::string>& paths)
24+
{
25+
// Sequence can't be changed while thread is running
26+
if (_isAsynchronous)
27+
{
28+
return;
29+
}
30+
31+
_sequence = paths;
32+
_currentIndex = 0;
33+
34+
for (unsigned idx = 0; idx < _sequence.size(); idx++)
35+
{
36+
_pathToSeqId[_sequence[idx]] = idx;
37+
}
38+
}
39+
40+
void AsyncFetcher::setResizeRatio(double ratio)
41+
{
42+
QMutexLocker locker(&_mutexResizeRatio);
43+
_resizeRatio = ratio;
44+
}
45+
46+
void AsyncFetcher::setCache(ImageCache::uptr&& cache)
47+
{
48+
// Cache can't be changed while thread is running
49+
if (_isAsynchronous)
50+
{
51+
return;
52+
}
53+
_cache = std::move(cache);
54+
}
55+
56+
void AsyncFetcher::run()
57+
{
58+
using namespace std::chrono_literals;
59+
60+
_isAsynchronous = true;
61+
_requestSynchronous = false;
62+
63+
std::size_t previousCacheSize = getDiskLoads();
64+
65+
while (1)
66+
{
67+
if (_requestSynchronous)
68+
{
69+
_requestSynchronous = false;
70+
break;
71+
}
72+
73+
if (_sequence.size() == 0)
74+
{
75+
std::this_thread::sleep_for(100ms);
76+
}
77+
else
78+
{
79+
const std::string& lpath = _sequence[static_cast<std::size_t>(_currentIndex)];
80+
81+
// Load in cache
82+
if (_cache)
83+
{
84+
double ratio;
85+
{
86+
QMutexLocker locker(&_mutexResizeRatio);
87+
ratio = _resizeRatio;
88+
}
89+
90+
_cache->get<image::RGBAfColor>(lpath, static_cast<unsigned int>(_currentIndex), ratio, false);
91+
}
92+
93+
_currentIndex++;
94+
95+
int size = static_cast<int>(_sequence.size());
96+
if (_currentIndex >= size)
97+
{
98+
_currentIndex = 0;
99+
}
100+
101+
std::this_thread::sleep_for(1ms);
102+
}
103+
104+
std::size_t cacheSize = getDiskLoads();
105+
if (cacheSize != previousCacheSize)
106+
{
107+
previousCacheSize = cacheSize;
108+
Q_EMIT onAsyncFetchProgressed();
109+
}
110+
}
111+
112+
_requestSynchronous = false;
113+
_isAsynchronous = false;
114+
}
115+
116+
void AsyncFetcher::stopAsync()
117+
{
118+
_requestSynchronous = true;
119+
}
120+
121+
void AsyncFetcher::updateCacheMemory(std::size_t maxMemory)
122+
{
123+
if (_cache)
124+
{
125+
_cache->updateMaxMemory(maxMemory);
126+
}
127+
}
128+
129+
std::size_t AsyncFetcher::getCacheSize() const
130+
{
131+
return (_cache) ? static_cast<std::size_t>(_cache->info().getContentSize()) : 0;
132+
}
133+
134+
std::size_t AsyncFetcher::getDiskLoads() const
135+
{
136+
return (_cache) ? static_cast<std::size_t>(_cache->info().getLoadFromDisk()) : 0;
137+
}
138+
139+
QVariantList AsyncFetcher::getCachedFrames() const
140+
{
141+
QVariantList intervals;
142+
143+
if (!_cache)
144+
{
145+
return intervals;
146+
}
147+
148+
// Accumulator variables
149+
auto region = std::make_pair(-1, -1);
150+
bool regionOpen = false;
151+
152+
size_t size = _sequence.size();
153+
154+
{
155+
// Build cached frames intervals
156+
for (std::size_t i = 0; i < size; ++i)
157+
{
158+
const int frame = static_cast<int>(i);
159+
160+
// Check if current frame is in cache
161+
if (_cache->contains<aliceVision::image::RGBAfColor>(_sequence[i], _resizeRatio))
162+
{
163+
// Either grow currently open region or create a new region
164+
if (regionOpen)
165+
{
166+
region.second = frame;
167+
}
168+
else
169+
{
170+
region.first = frame;
171+
region.second = frame;
172+
regionOpen = true;
173+
}
174+
}
175+
else
176+
{
177+
// Close currently open region
178+
if (regionOpen)
179+
{
180+
intervals.append(QPoint(region.first, region.second));
181+
regionOpen = false;
182+
}
183+
}
184+
}
185+
}
186+
187+
// Last region may still be open
188+
if (regionOpen)
189+
{
190+
intervals.append(QPoint(region.first, region.second));
191+
}
192+
193+
return intervals;
194+
}
195+
196+
bool AsyncFetcher::getFrame(const std::string& path,
197+
std::shared_ptr<image::Image<image::RGBAfColor>>& image,
198+
oiio::ParamValueList& metadatas,
199+
size_t& originalWidth,
200+
size_t& originalHeight)
201+
{
202+
// Need a cache
203+
if (!_cache)
204+
{
205+
return false;
206+
}
207+
208+
// First try getting the image
209+
bool onlyCache = _isAsynchronous;
210+
211+
// Upgrade the thread with the current Index
212+
for (std::size_t idx = 0; idx < _sequence.size(); ++idx)
213+
{
214+
if (_sequence[idx] == path)
215+
{
216+
_currentIndex = static_cast<int>(idx);
217+
break;
218+
}
219+
}
220+
221+
std::optional<CacheValue> ovalue = _cache->get<aliceVision::image::RGBAfColor>(path, _currentIndex, _resizeRatio, onlyCache);
222+
223+
if (ovalue.has_value())
224+
{
225+
auto& value = ovalue.value();
226+
image = value.get<aliceVision::image::RGBAfColor>();
227+
228+
oiio::ParamValueList copy_metadatas = value.getMetadatas();
229+
metadatas = copy_metadatas;
230+
originalWidth = value.getOriginalWidth();
231+
originalHeight = value.getOriginalHeight();
232+
233+
if (image)
234+
{
235+
_cache->setReferenceFrameId(_currentIndex);
236+
}
237+
238+
return true;
239+
}
240+
241+
return false;
242+
}
243+
244+
} // namespace imgserve
245+
} // namespace qtAliceVision
246+
247+
#include "AsyncFetcher.moc"

src/qtAliceVision/AsyncFetcher.hpp

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#pragma once
2+
3+
#include <QObject>
4+
#include <QRunnable>
5+
#include <QMutex>
6+
#include <QVariantList>
7+
8+
#include "ImageCache.hpp"
9+
10+
namespace qtAliceVision {
11+
namespace imgserve {
12+
13+
class AsyncFetcher : public QObject, public QRunnable
14+
{
15+
Q_OBJECT
16+
17+
public:
18+
AsyncFetcher();
19+
~AsyncFetcher();
20+
21+
/**
22+
* @brief Cache object is created externally.
23+
* Pass it to the Fetcher for use (Fetcher get ownership)
24+
* @param cache the cache object to store
25+
*/
26+
void setCache(ImageCache::uptr&& cache);
27+
28+
/**
29+
* @brief set the image sequence
30+
* The image sequence is a list of image paths which is ordered
31+
* The Fetcher must not be in asynchronous mode for this function to work
32+
* As such, the _sequence object is only used in read mode during async mode.
33+
*/
34+
void setSequence(const std::vector<std::string>& paths);
35+
36+
/**
37+
* @brief update the resize ratio of the image
38+
* @param ratio the coefficient of resize of the loaded images
39+
*/
40+
void setResizeRatio(double ratio);
41+
42+
/**
43+
* @brief retrieve a frame from the cache in both sync and async mode
44+
* @param path the image path which should be contained in _sequence.
45+
* @param image the result image pointer
46+
* @param metadatas the image metadatas found in the file
47+
* @param originalWidth the image width before the resize
48+
* @param originalHeight the image height before the resize
49+
* @return true if the image was succesfully found in the cache
50+
*/
51+
bool getFrame(const std::string& path,
52+
std::shared_ptr<aliceVision::image::Image<aliceVision::image::RGBAfColor>>& image,
53+
oiio::ParamValueList& metadatas,
54+
std::size_t& originalWidth,
55+
std::size_t& originalHeight);
56+
57+
/**
58+
* @brief Internal function for QT to start the asynchronous mode
59+
*/
60+
Q_SLOT void run() override;
61+
62+
/**
63+
* @brief stop asynchronous mode
64+
* The caller have to wait on the thread pool to guarantee the effective end
65+
*/
66+
void stopAsync();
67+
68+
bool isAsync() const { return _isAsynchronous; }
69+
70+
/**
71+
* @brief get the cache content size in bytes
72+
* @return the cache content size in bytes
73+
*/
74+
std::size_t getCacheSize() const;
75+
76+
/**
77+
* @brief get the number of images loaded
78+
* @return the count of images loaded since the creation of the cache object
79+
*/
80+
std::size_t getDiskLoads() const;
81+
82+
/**
83+
* @brief update maxMemory for the cache
84+
* @param maxMemory the number of bytes allowed in the cache
85+
*/
86+
void updateCacheMemory(std::size_t maxMemory);
87+
88+
/**
89+
* @brief get a list of regions containing the image frames
90+
* @return a list of two values (begin, end)
91+
*/
92+
QVariantList getCachedFrames() const;
93+
94+
public:
95+
Q_SIGNAL void onAsyncFetchProgressed();
96+
97+
private:
98+
ImageCache::uptr _cache;
99+
100+
std::vector<std::string> _sequence;
101+
std::unordered_map<std::string, unsigned> _pathToSeqId;
102+
103+
QAtomicInt _currentIndex;
104+
QAtomicInt _isAsynchronous;
105+
QAtomicInt _requestSynchronous;
106+
107+
double _resizeRatio;
108+
QMutex _mutexResizeRatio;
109+
};
110+
111+
} // namespace imgserve
112+
} // namespace qtAliceVision

src/qtAliceVision/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ set(PLUGIN_SOURCES
1313
Painter.cpp
1414
SequenceCache.cpp
1515
SingleImageLoader.cpp
16+
ImageCache.cpp
17+
AsyncFetcher.cpp
1618
)
1719

1820
set(PLUGIN_HEADERS
@@ -32,6 +34,9 @@ set(PLUGIN_HEADERS
3234
ImageServer.hpp
3335
SequenceCache.hpp
3436
SingleImageLoader.hpp
37+
ImageCache.hpp
38+
AsyncFetcher.hpp
39+
3540
)
3641

3742
set(PLUGIN_MOCS
@@ -43,6 +48,7 @@ set(PLUGIN_MOCS
4348
MSfMDataStats.hpp
4449
SequenceCache.hpp
4550
SingleImageLoader.hpp
51+
AsyncFetcher.hpp
4652
)
4753

4854

0 commit comments

Comments
 (0)