Image sequence tileprovider (#1798)

* Fix for rendering DoublePropertys in ImGui
* Adding an ImageSequenceTileProvider that loads a folder of images and makes them available through a user-selectable index
This commit is contained in:
Alexander Bock
2021-12-27 18:58:05 +01:00
committed by GitHub
parent 76b9c7587b
commit 7cf5c37474
8 changed files with 204 additions and 21 deletions
@@ -293,6 +293,11 @@ void GlobeBrowsingModule::internalInitialize(const ghoul::Dictionary& dict) {
layergroupid::TypeID::SingleImageTileLayer
)]
);
fTileProvider->registerClass<tileprovider::ImageSequenceTileProvider>(
layergroupid::LAYER_TYPE_NAMES[static_cast<int>(
layergroupid::TypeID::ImageSequenceTileLayer
)]
);
fTileProvider->registerClass<tileprovider::TemporalTileProvider>(
layergroupid::LAYER_TYPE_NAMES[static_cast<int>(
layergroupid::TypeID::TemporalTileLayer
@@ -149,18 +149,21 @@ vec4 getSample#{layerGroup}#{i}(vec2 uv, vec3 levelWeights,
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 1) // SingleImageTileLayer
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 2) // SizeReferenceTileLayer
#elif (#{#{layerGroup}#{i}LayerType} == 2) // ImageSequenceTileLayer
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 3) // TemporalTileLayer
#elif (#{#{layerGroup}#{i}LayerType} == 3) // SizeReferenceTileLayer
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 4) // TileIndexTileLayer
#elif (#{#{layerGroup}#{i}LayerType} == 4) // TemporalTileLayer
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 5) // ByIndexTileLayer
#elif (#{#{layerGroup}#{i}LayerType} == 5) // TileIndexTileLayer
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 6) // ByLevelTileLayer
#elif (#{#{layerGroup}#{i}LayerType} == 6) // ByIndexTileLayer
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 7) // SolidColor
#elif (#{#{layerGroup}#{i}LayerType} == 7) // ByLevelTileLayer
c = getTexVal(#{layerGroup}[#{i}].pile, levelWeights, uv, #{layerGroup}[#{i}].padding);
#elif (#{#{layerGroup}#{i}LayerType} == 8) // SolidColor
c.rgb = #{layerGroup}[#{i}].color;
#endif
return c;
+4 -2
View File
@@ -68,6 +68,7 @@ void GPULayerGroup::setValue(ghoul::opengl::ProgramObject& program,
// Intentional fall through. Same for all tile layers
case layergroupid::TypeID::DefaultTileLayer:
case layergroupid::TypeID::SingleImageTileLayer:
case layergroupid::TypeID::ImageSequenceTileLayer:
case layergroupid::TypeID::SizeReferenceTileLayer:
case layergroupid::TypeID::TemporalTileLayer:
case layergroupid::TypeID::TileIndexTileLayer:
@@ -103,7 +104,7 @@ void GPULayerGroup::setValue(ghoul::opengl::ProgramObject& program,
program.setUniform(galuc.color, al.solidColor());
break;
default:
break;
throw ghoul::MissingCaseException();
}
if (gal.isHeightLayer) {
@@ -148,6 +149,7 @@ void GPULayerGroup::bind(ghoul::opengl::ProgramObject& p,
// Intentional fall through. Same for all tile layers
case layergroupid::TypeID::DefaultTileLayer:
case layergroupid::TypeID::SingleImageTileLayer:
case layergroupid::TypeID::ImageSequenceTileLayer:
case layergroupid::TypeID::SizeReferenceTileLayer:
case layergroupid::TypeID::TemporalTileLayer:
case layergroupid::TypeID::TileIndexTileLayer:
@@ -177,7 +179,7 @@ void GPULayerGroup::bind(ghoul::opengl::ProgramObject& p,
galuc.color = p.uniformLocation(name + "color");
break;
default:
break;
throw ghoul::MissingCaseException();
}
if (gal.isHeightLayer) {
+8 -4
View File
@@ -113,8 +113,9 @@ namespace {
// Specifies the type of layer that is to be added. If this value is not
// specified, the layer is a DefaultTileLayer
std::optional<std::string> type [[codegen::inlist("DefaultTileLayer",
"SingleImageTileLayer", "SizeReferenceTileLayer", "TemporalTileLayer",
"TileIndexTileLayer", "ByIndexTileLayer", "ByLevelTileLayer", "SolidColor")]];
"SingleImageTileLayer", "ImageSequenceTileLayer", "SizeReferenceTileLayer",
"TemporalTileLayer", "TileIndexTileLayer", "ByIndexTileLayer",
"ByLevelTileLayer", "SolidColor")]];
// Determine whether the layer is enabled or not. If this value is not specified,
// the layer is disabled
@@ -301,6 +302,7 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict,
// Intentional fall through. Same for all tile layers
case layergroupid::TypeID::DefaultTileLayer:
case layergroupid::TypeID::SingleImageTileLayer:
case layergroupid::TypeID::ImageSequenceTileLayer:
case layergroupid::TypeID::SizeReferenceTileLayer:
case layergroupid::TypeID::TemporalTileLayer:
case layergroupid::TypeID::TileIndexTileLayer:
@@ -314,7 +316,7 @@ Layer::Layer(layergroupid::GroupID id, const ghoul::Dictionary& layerDict,
removeProperty(_solidColor);
break;
default:
break;
throw ghoul::MissingCaseException();
}
_type = static_cast<layergroupid::TypeID>(_typeOption.value());
@@ -471,6 +473,7 @@ void Layer::initializeBasedOnType(layergroupid::TypeID id, ghoul::Dictionary ini
// Intentional fall through. Same for all tile layers
case layergroupid::TypeID::DefaultTileLayer:
case layergroupid::TypeID::SingleImageTileLayer:
case layergroupid::TypeID::ImageSequenceTileLayer:
case layergroupid::TypeID::SizeReferenceTileLayer:
case layergroupid::TypeID::TemporalTileLayer:
case layergroupid::TypeID::TileIndexTileLayer:
@@ -502,6 +505,7 @@ void Layer::addVisibleProperties() {
// Intentional fall through. Same for all tile layers
case layergroupid::TypeID::DefaultTileLayer:
case layergroupid::TypeID::SingleImageTileLayer:
case layergroupid::TypeID::ImageSequenceTileLayer:
case layergroupid::TypeID::SizeReferenceTileLayer:
case layergroupid::TypeID::TemporalTileLayer:
case layergroupid::TypeID::TileIndexTileLayer:
@@ -517,7 +521,7 @@ void Layer::addVisibleProperties() {
break;
}
default:
break;
throw ghoul::MissingCaseException();
}
}
+11 -9
View File
@@ -53,19 +53,20 @@ enum GroupID {
Overlays,
NightLayers,
WaterMasks,
Unknown,
Unknown
};
static constexpr const int NUM_LAYER_TYPES = 8;
static constexpr const int NUM_LAYER_TYPES = 9;
static constexpr const char* LAYER_TYPE_NAMES[NUM_LAYER_TYPES] = {
"DefaultTileLayer",
"SingleImageTileLayer",
"ImageSequenceTileLayer",
"SizeReferenceTileLayer",
"TemporalTileLayer",
"TileIndexTileLayer",
"ByIndexTileLayer",
"ByLevelTileLayer",
"SolidColor",
"SolidColor"
};
/**
@@ -75,12 +76,13 @@ enum class TypeID {
Unknown = -1,
DefaultTileLayer = 0,
SingleImageTileLayer = 1,
SizeReferenceTileLayer = 2,
TemporalTileLayer = 3,
TileIndexTileLayer = 4,
ByIndexTileLayer = 5,
ByLevelTileLayer = 6,
SolidColor = 7,
ImageSequenceTileLayer = 2,
SizeReferenceTileLayer = 3,
TemporalTileLayer = 4,
TileIndexTileLayer = 5,
ByIndexTileLayer = 6,
ByLevelTileLayer = 7,
SolidColor = 8
};
static constexpr int NUM_ADJUSTMENT_TYPES = 3;
+146
View File
@@ -118,6 +118,28 @@ namespace singleimageprovider {
};
} // namespace singleimageprovider
namespace imagesequenceprovider {
constexpr openspace::properties::Property::PropertyInfo IndexInfo = {
"Index",
"Index",
"The index into the list of images that is used to pick the currently displayed "
"image"
};
constexpr openspace::properties::Property::PropertyInfo CurrentImageInfo = {
"CurrentImage",
"Current Image",
"The read-only value of the currently selected image"
};
constexpr openspace::properties::Property::PropertyInfo FolderPathInfo = {
"FolderPath",
"Folder Path",
"The path that is used to look for images for this image provider. The path must "
"point to an existing folder that contains images"
};
} // namepsace imagesequenceprovider
namespace sizereferenceprovider {
constexpr const char* KeyRadii = "Radii";
} // namespace sizereferenceprovider
@@ -779,6 +801,38 @@ SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary)
ImageSequenceTileProvider::ImageSequenceTileProvider(const ghoul::Dictionary& dictionary)
: index(imagesequenceprovider::IndexInfo, 0)
, currentImage(imagesequenceprovider::CurrentImageInfo)
, folderPath(imagesequenceprovider::FolderPathInfo)
, initDict(dictionary)
{
ZoneScoped
type = Type::ImageSequenceTileProvider;
if (dictionary.hasValue<int>(imagesequenceprovider::IndexInfo.identifier)) {
index = dictionary.value<int>(imagesequenceprovider::IndexInfo.identifier);
}
index.setMinValue(0);
index.onChange([this]() { isImageDirty = true; });
addProperty(index);
folderPath.setReadOnly(true);
addProperty(folderPath);
folderPath = dictionary.value<std::string>(
imagesequenceprovider::FolderPathInfo.identifier
);
addProperty(folderPath);
reset(*this);
}
TextTileProvider::TextTileProvider(TileTextureInitData initData_, size_t fontSize_)
: initData(std::move(initData_))
, fontSize(fontSize_)
@@ -1047,6 +1101,8 @@ bool initialize(TileProvider& tp) {
break;
case Type::SingleImageTileProvider:
break;
case Type::ImageSequenceTileProvider:
break;
case Type::SizeReferenceTileProvider: {
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
initialize(t);
@@ -1091,6 +1147,8 @@ bool deinitialize(TileProvider& tp) {
break;
case Type::SingleImageTileProvider:
break;
case Type::ImageSequenceTileProvider:
break;
case Type::SizeReferenceTileProvider: {
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
deinitialize(t);
@@ -1322,6 +1380,16 @@ Tile tile(TileProvider& tp, const TileIndex& tileIndex) {
SingleImageProvider& t = static_cast<SingleImageProvider&>(tp);
return t.tile;
}
case Type::ImageSequenceTileProvider: {
ZoneScopedN("Type::ImageSequenceTileProvider")
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
if (t.currentTileProvider) {
return tile(*t.currentTileProvider, tileIndex);
}
else {
return Tile();
}
}
case Type::SizeReferenceTileProvider: {
ZoneScopedN("Type::SizeReferenceTileProvider")
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
@@ -1447,6 +1515,15 @@ Tile::Status tileStatus(TileProvider& tp, const TileIndex& index) {
SingleImageProvider& t = static_cast<SingleImageProvider&>(tp);
return t.tile.status;
}
case Type::ImageSequenceTileProvider: {
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
if (t.currentTileProvider) {
return tileStatus(*t.currentTileProvider, index);
}
else {
return Tile::Status::Unavailable;
}
}
case Type::SizeReferenceTileProvider:
return Tile::Status::OK;
case Type::TileIndexTileProvider:
@@ -1510,6 +1587,15 @@ TileDepthTransform depthTransform(TileProvider& tp) {
}
case Type::SingleImageTileProvider:
return { 0.f, 1.f };
case Type::ImageSequenceTileProvider: {
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
if (t.currentTileProvider) {
return depthTransform(*t.currentTileProvider);
}
else {
return { 1.f, 0.f };
}
}
case Type::SizeReferenceTileProvider:
return { 0.f, 1.f };
case Type::TileIndexTileProvider:
@@ -1571,6 +1657,29 @@ int update(TileProvider& tp) {
}
case Type::SingleImageTileProvider:
break;
case Type::ImageSequenceTileProvider: {
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
if (t.isImageDirty && !t.imagePaths.empty() &&
t.index >= 0 && t.index < t.imagePaths.size())
{
if (t.currentTileProvider) {
deinitialize(*t.currentTileProvider);
}
std::string p = t.imagePaths[t.index].string();
t.currentImage = p;
t.initDict.setValue(KeyFilePath, p);
t.currentTileProvider = std::make_unique<DefaultTileProvider>(t.initDict);
initialize(*t.currentTileProvider);
t.isImageDirty = false;
}
if (t.currentTileProvider) {
update(*t.currentTileProvider);
}
break;
}
case Type::SizeReferenceTileProvider:
break;
case Type::TileIndexTileProvider:
@@ -1664,6 +1773,25 @@ void reset(TileProvider& tp) {
t.tile = Tile{ t.tileTexture.get(), std::nullopt, tileStatus };
break;
}
case Type::ImageSequenceTileProvider: {
namespace fs = std::filesystem;
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
std::string path = t.folderPath;
t.imagePaths.clear();
for (const fs::directory_entry& p : fs::directory_iterator(path)) {
if (p.is_regular_file()) {
t.imagePaths.push_back(p.path());
}
}
t.index = 0;
t.index.setMaxValue(static_cast<int>(t.imagePaths.size() - 1));
if (t.currentTileProvider) {
reset(*t.currentTileProvider);
}
break;
}
case Type::SizeReferenceTileProvider: {
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
reset(t);
@@ -1733,6 +1861,15 @@ int maxLevel(TileProvider& tp) {
}
case Type::SingleImageTileProvider:
return 1337; // unlimited
case Type::ImageSequenceTileProvider: {
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
if (t.currentTileProvider) {
return maxLevel(*t.currentTileProvider);
}
else {
return 0;
}
}
case Type::SizeReferenceTileProvider:
return 1337; // unlimited
case Type::TileIndexTileProvider:
@@ -1782,6 +1919,15 @@ float noDataValueAsFloat(TileProvider& tp) {
}
case Type::SingleImageTileProvider:
return std::numeric_limits<float>::min();
case Type::ImageSequenceTileProvider: {
ImageSequenceTileProvider& t = static_cast<ImageSequenceTileProvider&>(tp);
if (t.currentTileProvider) {
return noDataValueAsFloat(*t.currentTileProvider);
}
else {
return std::numeric_limits<float>::min();
}
}
case Type::SizeReferenceTileProvider:
return std::numeric_limits<float>::min();
case Type::TileIndexTileProvider:
+16
View File
@@ -38,6 +38,7 @@
#include <openspace/properties/scalar/intproperty.h>
#include <unordered_map>
#include <ghoul/opengl/programobject.h>
struct CPLXMLNode;
namespace ghoul::fontrendering {
@@ -59,6 +60,7 @@ namespace openspace::globebrowsing::tileprovider {
enum class Type {
DefaultTileProvider = 0,
SingleImageTileProvider,
ImageSequenceTileProvider,
SizeReferenceTileProvider,
TemporalTileProvider,
TileIndexTileProvider,
@@ -169,6 +171,20 @@ struct TileProviderByLevel : public TileProvider {
};
struct ImageSequenceTileProvider : public TileProvider {
ImageSequenceTileProvider(const ghoul::Dictionary& dictionary);
std::unique_ptr<DefaultTileProvider> currentTileProvider = nullptr;
properties::IntProperty index;
properties::StringProperty currentImage;
properties::StringProperty folderPath;
ghoul::Dictionary initDict;
bool isImageDirty = true;
std::vector<std::filesystem::path> imagePaths;
};
/**
* Provide <code>Tile</code>s from web map services that have temporal resolution.
*
+5
View File
@@ -386,6 +386,11 @@ void renderDoubleProperty(properties::Property* prop, const std::string& ownerNa
float value = static_cast<float>(*p);
float min = static_cast<float>(p->minValue());
float max = static_cast<float>(p->maxValue());
// Since we are doing a DoubleProperty, it would actually overflow here and produce
// -inf and inf as the min and max which confuses ImGui
min = std::max(min, std::numeric_limits<float>::min() / 2.f);
max = std::min(max, std::numeric_limits<float>::max() / 2.f);
bool changed = ImGui::SliderFloat(
name.c_str(),