Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[qtAV] Image sequence caching #36

Merged
merged 26 commits into from
Jul 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
8174cbc
[qtAV] FloatImageViewer: use AV image cache when loading images
mugulmd Mar 28, 2023
e2242f5
[qtAV] SequenceCache: new caching system for image sequences
mugulmd Apr 14, 2023
82b49f9
[qtAV] FloatImageViewer: use SequenceCache to load images
mugulmd Apr 14, 2023
71e9591
[qtAV] FloatImageViewer: expose cached frames
mugulmd Apr 17, 2023
e870972
[qtAV] SequenceCache: anticipate prefetching with safe region
mugulmd Apr 24, 2023
93509c6
[qtAV] SequenceCache: store image size and metadata
mugulmd Apr 24, 2023
c436769
[qtAV] SequenceCache: prevent cache monopolization by prefetching thread
mugulmd Apr 24, 2023
bb7cef0
[qtAV] SequenceCache: adapt cache capacity and usage to available RAM
mugulmd Apr 24, 2023
4404d12
[qtAV] SequenceCache: merge cached frame regions for display
mugulmd Apr 24, 2023
cb439c1
[qtAV] SequenceCache: send progress signals from prefetch thread
mugulmd Apr 19, 2023
67dc400
[qtAV] SequenceCache: rely directly on cache content when answering r…
mugulmd Apr 24, 2023
a3a439d
boost flag for windows build
mugulmd Apr 24, 2023
eef0562
[qtAV] SequenceCache: disable cache lazy cleaning to only use LRU
mugulmd Apr 24, 2023
b47464e
[qtAV] SequenceCache: use cache capacity to determine how many images…
mugulmd Apr 24, 2023
3c0d935
[qtAV] abstraction of image server system with ImageServer interface
mugulmd Apr 24, 2023
9a1cf47
[qtAV] new SingleImageLoader class
mugulmd Apr 24, 2023
4a1a158
[qtAV] register imageio::Response as QMetaType
mugulmd Apr 24, 2023
e7e9021
[qtAV] FloatImageViewer: single image loader fallback
mugulmd Apr 24, 2023
4c245bc
[qtAV] imageio: documentation, code comments and minor bug fixes
mugulmd Apr 25, 2023
b4b7242
[qtAV] rename imageio to imgserve to avoid confusions
mugulmd Apr 25, 2023
f29a2d6
[qtAV] SequenceCache: fix build warnings
mugulmd Apr 25, 2023
3971e56
corrections after rebase on refacto
mugulmd May 30, 2023
68e9807
[qtAV] imgserve: catch runtime_error when image cannot be read
mugulmd Jun 20, 2023
86ac404
[qtAV] SequenceCache: do not reorder sequence
mugulmd Jul 3, 2023
1dc75f3
[qtAV] imgserve: encapsulate request data in struct
mugulmd Jul 12, 2023
1b6c19a
[qtAV] SingleImageLoader: apply downscale
mugulmd Jul 12, 2023
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ add_definitions(-DQT_ALICEVISIONIMAGEIO_USE_FORMATS_BLACKLIST)


# AliceVision dependency
add_definitions(-DBOOST_USE_WINAPI_VERSION=BOOST_WINAPI_VERSION_WIN7)
find_package(AliceVision REQUIRED)


Expand Down
7 changes: 7 additions & 0 deletions src/qtAliceVision/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ set(PLUGIN_SOURCES
MSfMDataStats.cpp
PanoramaViewer.cpp
Painter.cpp
SequenceCache.cpp
SingleImageLoader.cpp
)

set(PLUGIN_HEADERS
Expand All @@ -27,6 +29,9 @@ set(PLUGIN_HEADERS
Surface.hpp
ShaderImageViewer.hpp
Painter.hpp
ImageServer.hpp
SequenceCache.hpp
SingleImageLoader.hpp
)

set(PLUGIN_MOCS
Expand All @@ -36,6 +41,8 @@ set(PLUGIN_MOCS
MViewStats.hpp
MTracks.hpp
MSfMDataStats.hpp
SequenceCache.hpp
SingleImageLoader.hpp
)
Comment on lines 41 to 46
Copy link
Contributor

Choose a reason for hiding this comment

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

Not related to this PR, but there are several files in here that trigger AutoMoc warnings during the build, and we might want to look into that at some point (doesn't seem to have any functional impact, though).



Expand Down
147 changes: 48 additions & 99 deletions src/qtAliceVision/FloatImageViewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,74 +7,14 @@
#include <QSGTexture>
#include <QThreadPool>

#include <aliceVision/image/Image.hpp>
#include <aliceVision/image/convertion.hpp>
#include <aliceVision/image/io.hpp>
#include <aliceVision/image/resampling.hpp>
#include <cmath>
#include <algorithm>
#include <vector>
#include <utility>

namespace qtAliceVision
{

FloatImageIORunnable::FloatImageIORunnable(const QUrl& path, int downscaleLevel, QObject* parent)
: QObject(parent)
, _path(path)
, _downscaleLevel(downscaleLevel)
{
}

void FloatImageIORunnable::run()
{
using namespace aliceVision;
QSharedPointer<FloatImage> result;
QSize sourceSize(0, 0);
QVariantMap qmetadata;

try
{
const auto path = _path.toLocalFile().toUtf8().toStdString();
FloatImage image;
image::readImage(path, image,
image::EImageColorSpace::LINEAR); // linear: sRGB conversion is done in display shader

sourceSize = QSize(image.Width(), image.Height());

// ensure it fits in GPU memory
if (FloatTexture::maxTextureSize() != -1)
{
const auto maxTextureSize = FloatTexture::maxTextureSize();
while (maxTextureSize != -1 && (image.Width() > maxTextureSize || image.Height() > maxTextureSize))
{
FloatImage tmp;
aliceVision::image::ImageHalfSample(image, tmp);
image = std::move(tmp);
}
}

// ensure it fits in RAM memory
for (int i = 0; i < _downscaleLevel; i++)
{
FloatImage tmp;
aliceVision::image::ImageHalfSample(image, tmp);
image = std::move(tmp);
}

// load metadata as well
const auto metadata = image::readImageMetadata(path);
for (const auto& item : metadata)
{
qmetadata[QString::fromStdString(item.name().string())] = QString::fromStdString(item.get_string());
}

result = QSharedPointer<FloatImage>(new FloatImage(std::move(image)));
}
catch (std::exception& e)
{
qInfo() << "[QtAliceVision] Failed to load image: " << _path << "\n" << e.what();
}

Q_EMIT resultReady(result, sourceSize, qmetadata);
}

FloatImageViewer::FloatImageViewer(QQuickItem* parent)
: QQuickItem(parent)
{
Expand Down Expand Up @@ -102,9 +42,15 @@ FloatImageViewer::FloatImageViewer(QQuickItem* parent)

connect(&_surface, &Surface::subdivisionsChanged, this, &FloatImageViewer::update);
connect(&_surface, &Surface::verticesChanged, this, &FloatImageViewer::update);

connect(&_sequenceCache, &imgserve::SequenceCache::requestHandled, this, &FloatImageViewer::reload);
connect(&_singleImageLoader, &imgserve::SingleImageLoader::requestHandled, this, &FloatImageViewer::reload);
connect(this, &FloatImageViewer::sequenceChanged, this, &FloatImageViewer::reload);
connect(this, &FloatImageViewer::useSequenceChanged, this, &FloatImageViewer::reload);
}

FloatImageViewer::~FloatImageViewer() {}
FloatImageViewer::~FloatImageViewer()
{}

// LOADING FUNCTIONS
void FloatImageViewer::setLoading(bool loading)
Expand All @@ -117,6 +63,18 @@ void FloatImageViewer::setLoading(bool loading)
Q_EMIT loadingChanged();
}

void FloatImageViewer::setSequence(const QVariantList& paths)
{
_sequenceCache.setSequence(paths);

Q_EMIT sequenceChanged();
}

QVariantList FloatImageViewer::getCachedFrames() const
{
return _sequenceCache.getCachedFrames();
}

void FloatImageViewer::reload()
{
if (_clearBeforeLoad)
Expand All @@ -125,12 +83,12 @@ void FloatImageViewer::reload()
_imageChanged = true;
Q_EMIT imageChanged();
}

_outdated = false;
if (_loading) _outdated = true;

if (!_source.isValid())
{
if (_loading)
_outdated = true;
_image.reset();
_imageChanged = true;
_surface.clearVertices();
Expand All @@ -139,44 +97,35 @@ void FloatImageViewer::reload()
return;
}

if (!_loading)
{
setLoading(true);
// Send request
imgserve::RequestData reqData;
reqData.path = _source.toLocalFile().toUtf8().toStdString();
reqData.downscale = 1 << _downscaleLevel;

// async load from file
auto ioRunnable = new FloatImageIORunnable(_source, _downscaleLevel);
connect(ioRunnable, &FloatImageIORunnable::resultReady, this, &FloatImageViewer::onResultReady);
QThreadPool::globalInstance()->start(ioRunnable);
}
else
imgserve::ResponseData response = _useSequence ? _sequenceCache.request(reqData) : _singleImageLoader.request(reqData);

if (response.img)
{
_outdated = true;
}
}
setLoading(false);

void FloatImageViewer::onResultReady(QSharedPointer<FloatImage> image, QSize sourceSize, const QVariantMap& metadata)
{
setLoading(false);
_surface.setVerticesChanged(true);
_surface.setNeedToUseIntrinsic(true);
_image = response.img;
_imageChanged = true;
Q_EMIT imageChanged();

_sourceSize = response.dim;
Q_EMIT sourceSizeChanged();

if (_outdated)
_metadata = response.metadata;
Q_EMIT metadataChanged();
}
else
{
// another request has been made while io thread was working
_image.reset();
reload();
return;
setLoading(true);
}

_surface.setVerticesChanged(true);
_surface.setNeedToUseIntrinsic(true);
_image = image;
_imageChanged = true;
Q_EMIT imageChanged();

_sourceSize = sourceSize;
Q_EMIT sourceSizeChanged();

_metadata = metadata;
Q_EMIT metadataChanged();
Q_EMIT cachedFramesChanged();
}

QVector4D FloatImageViewer::pixelValueAt(int x, int y)
Expand Down Expand Up @@ -421,4 +370,4 @@ void FloatImageViewer::updatePaintSurface(QSGGeometryNode* root, QSGSimpleMateri
root->childAtIndex(0)->markDirty(QSGNode::DirtyGeometry | QSGNode::DirtyMaterial);
}

} // namespace qtAliceVision
} // namespace qtAliceVision
57 changes: 26 additions & 31 deletions src/qtAliceVision/FloatImageViewer.hpp
Original file line number Diff line number Diff line change
@@ -1,47 +1,27 @@
#pragma once

#include "FloatTexture.hpp"
#include "ShaderImageViewer.hpp"
#include "Surface.hpp"
#include "ShaderImageViewer.hpp"
#include "SequenceCache.hpp"
#include "SingleImageLoader.hpp"

#include <aliceVision/image/all.hpp>

#include <QQuickItem>
#include <QRunnable>
#include <QSGGeometryNode>
#include <QSGSimpleMaterial>
#include <QUrl>

#include <QSharedPointer>
#include <QVariant>
#include <QVector4D>
#include <QList>

#include <memory>
#include <string>

namespace qtAliceVision
{

/**
* @brief QRunnable object dedicated to load image using AliceVision.
*/
class FloatImageIORunnable : public QObject, public QRunnable
{
Q_OBJECT

public:
explicit FloatImageIORunnable(const QUrl& path, int downscaleLevel = 0, QObject* parent = nullptr);

/// Load image at path
Q_SLOT void run() override;

/// Emitted when the image is loaded
Q_SIGNAL void resultReady(QSharedPointer<qtAliceVision::FloatImage> image, QSize sourceSize,
const QVariantMap& metadata);

private:
QUrl _path;
int _downscaleLevel;
};

/**
* @brief Load and display image.
*/
Expand Down Expand Up @@ -76,6 +56,12 @@ class FloatImageViewer : public QQuickItem

Q_PROPERTY(bool cropFisheye READ getCropFisheye WRITE setCropFisheye NOTIFY isCropFisheyeChanged)

Q_PROPERTY(QVariantList sequence WRITE setSequence NOTIFY sequenceChanged)

Q_PROPERTY(QVariantList cachedFrames READ getCachedFrames NOTIFY cachedFramesChanged)

Q_PROPERTY(bool useSequence MEMBER _useSequence NOTIFY useSequenceChanged)

public:
explicit FloatImageViewer(QQuickItem* parent = nullptr);
~FloatImageViewer() override;
Expand Down Expand Up @@ -134,18 +120,23 @@ class FloatImageViewer : public QQuickItem
Q_SIGNAL void canBeHoveredChanged();
Q_SIGNAL void sfmRequiredChanged();
Q_SIGNAL void fisheyeCircleParametersChanged();
Q_SIGNAL void sequenceChanged();
Q_SIGNAL void cachedFramesChanged();
Q_SIGNAL void useSequenceChanged();

// Q_INVOKABLE
Q_INVOKABLE QVector4D pixelValueAt(int x, int y);

Surface* getSurfacePtr() { return &_surface; }

void setSequence(const QVariantList& paths);

QVariantList getCachedFrames() const;

private:
/// Reload image from source
void reload();
/// Handle result from asynchronous file loading
Q_SLOT void onResultReady(QSharedPointer<qtAliceVision::FloatImage> image, QSize sourceSize,
const QVariantMap& metadata);

/// Custom QSGNode update
QSGNode* updatePaintNode(QSGNode* oldNode, QQuickItem::UpdatePaintNodeData* data) override;

Expand All @@ -161,7 +152,7 @@ class FloatImageViewer : public QQuickItem

bool _imageChanged = false;
EChannelMode _channelMode;
QSharedPointer<FloatImage> _image;
std::shared_ptr<FloatImage> _image;
QRectF _boundingRect;
QSize _textureSize;
QSize _sourceSize = QSize(0, 0);
Expand All @@ -177,9 +168,13 @@ class FloatImageViewer : public QQuickItem
bool _canBeHovered = false;

bool _cropFisheye = false;

imgserve::SequenceCache _sequenceCache;
imgserve::SingleImageLoader _singleImageLoader;
bool _useSequence = true;
};

} // namespace qtAliceVision

Q_DECLARE_METATYPE(qtAliceVision::FloatImage)
Q_DECLARE_METATYPE(QSharedPointer<qtAliceVision::FloatImage>)
Q_DECLARE_METATYPE(std::shared_ptr<qtAliceVision::FloatImage>)
2 changes: 1 addition & 1 deletion src/qtAliceVision/FloatTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ FloatTexture::~FloatTexture()
}
}

void FloatTexture::setImage(QSharedPointer<FloatImage>& image)
void FloatTexture::setImage(std::shared_ptr<FloatImage>& image)
{
_srcImage = image;
_textureSize = {_srcImage->Width(), _srcImage->Height()};
Expand Down
8 changes: 4 additions & 4 deletions src/qtAliceVision/FloatTexture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

#include <QSGTexture>

#include <QSharedPointer>
#include <memory>

namespace qtAliceVision
{
Expand All @@ -30,8 +30,8 @@ class FloatTexture : public QSGTexture

bool hasMipmaps() const override { return mipmapFiltering() != QSGTexture::None; }

void setImage(QSharedPointer<FloatImage>& image);
const FloatImage& image() { return *_srcImage; }
void setImage(std::shared_ptr<FloatImage>& image);
const FloatImage &image() { return *_srcImage; }

void bind() override;

Expand All @@ -48,7 +48,7 @@ class FloatTexture : public QSGTexture
bool isValid() const;

private:
QSharedPointer<FloatImage> _srcImage;
std::shared_ptr<FloatImage> _srcImage;

unsigned int _textureId = 0;
QSize _textureSize;
Expand Down
Loading