Czi rendering (#10)
* add dimension selector sliders to czi view * version doc & version bump * cosmetic * fix build? * multi-dimension selection enabled in czi view * remove the parameterized reset function of the renderer it is not and is conceptually questionable doc updates & some minor improvements auto-update CZI-view when sliders are moved set initial window size fix windows build and msvc build For reasons I don not comprehend, on Windows, we must include =opencv2/core/mat.hpp= as the very last header in the =CvMatRender.hpp=. If this is at any other position building on Windows, compilation will break w/ all kinds of cryptic errors regarding OpenCV. When building w/ msvc =__PRETTY_FUNCTION__= is not defined. =ILog.hpp= now defines it. This should be revised to only be set when the compiler is msvc. For the time being, it is considered sufficient though.
This commit is contained in:
committed by
Maximilian Kueffner
parent
e60203b57d
commit
1ea83d9d11
@@ -1,9 +1,7 @@
|
||||
#include "CvMatRender.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <opencv2/core/mat.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "imaging/IPixelariumImage.hpp"
|
||||
|
||||
@@ -11,7 +9,7 @@ using namespace pixelarium::imaging;
|
||||
|
||||
/// @brief Constructor for the CvMatRender class.
|
||||
/// @param img A shared pointer to the PixelariumImage to be rendered.
|
||||
pixelarium::render::CvMatRender::CvMatRender(std::shared_ptr<pixelarium::imaging::IPixelariumImage>& img) : base_(img), texture_(0)
|
||||
pixelarium::render::CvMatRender::CvMatRender(const cv::Mat& img) : base_(img), texture_(0)
|
||||
{
|
||||
// storing a copy of the to-be-rendered image
|
||||
// because it will be resized and filtered eventually which we absolutely
|
||||
@@ -31,15 +29,6 @@ pixelarium::render::CvMatRender::~CvMatRender()
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Resets the render image with a new PixelariumImage.
|
||||
/// @param img A shared pointer to the new PixelariumImage.
|
||||
void pixelarium::render::CvMatRender::ResetRenderImage(std::shared_ptr<pixelarium::imaging::IPixelariumImage>& img)
|
||||
{
|
||||
this->base_ = img;
|
||||
this->ResetRenderImage();
|
||||
cv::cvtColor(this->img_, this->img_, cv::COLOR_BGR2RGBA);
|
||||
}
|
||||
|
||||
/// @brief Uploads the current image data to an OpenGL texture.
|
||||
/// @return The ID of the uploaded OpenGL texture.
|
||||
/// @throws std::runtime_error if the image data is empty or if there is an OpenGL error.
|
||||
@@ -103,11 +92,7 @@ GLuint pixelarium::render::CvMatRender::Render() { return this->uploadTexture();
|
||||
/// @return The ID of the OpenGL texture.
|
||||
GLuint pixelarium::render::CvMatRender::Render(float factor)
|
||||
{
|
||||
auto res_val {this->base_->TryGetImage()};
|
||||
if (res_val)
|
||||
{
|
||||
cv::resize(*res_val, this->img_, cv::Size(0, 0), factor, factor, cv::INTER_LINEAR_EXACT);
|
||||
}
|
||||
cv::resize(this->base_, this->img_, cv::Size(0, 0), factor, factor, cv::INTER_LINEAR_EXACT);
|
||||
|
||||
return this->uploadTexture();
|
||||
}
|
||||
@@ -118,14 +103,7 @@ GLuint pixelarium::render::CvMatRender::Render(float factor)
|
||||
/// @return The ID of the OpenGL texture.
|
||||
GLuint pixelarium::render::CvMatRender::Render(size_t width, size_t height)
|
||||
{
|
||||
auto res_val {this->base_->TryGetImage()};
|
||||
|
||||
if (!res_val)
|
||||
{
|
||||
return this->Render(1.0f);
|
||||
}
|
||||
|
||||
const auto sz{res_val->size()};
|
||||
const auto sz{this->base_.size()};
|
||||
|
||||
const auto get_factor = [](auto opt1, auto opt2) -> float { return opt1 < opt2 ? opt1 : opt2; };
|
||||
|
||||
@@ -136,18 +114,6 @@ GLuint pixelarium::render::CvMatRender::Render(size_t width, size_t height)
|
||||
|
||||
void pixelarium::render::CvMatRender::ResetRenderImage()
|
||||
{
|
||||
if (this->base_ == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto root_res = this->base_->TryGetImage();
|
||||
|
||||
if (!root_res)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// we copy here
|
||||
this->img_ = root_res->clone();
|
||||
this->img_ = this->base_.clone();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// windows.h must come before GL/GL.h here.
|
||||
// clang format would change this, effectively rendering the build broken.
|
||||
// clang-format off
|
||||
#include <memory>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <GL/GL.h>
|
||||
@@ -13,11 +12,12 @@
|
||||
#endif
|
||||
#include <GLFW/glfw3.h> // Will drag system OpenGL headers
|
||||
#endif
|
||||
#include "imaging/IPixelariumImage.hpp"
|
||||
#include <opencv2/core/mat.hpp>
|
||||
// clang-format on
|
||||
|
||||
namespace pixelarium::render
|
||||
{
|
||||
/// @brief Renders cv::Mat bitmaps as OpenGL textures.
|
||||
class CvMatRender
|
||||
{
|
||||
public:
|
||||
@@ -26,25 +26,24 @@ class CvMatRender
|
||||
// get removed in the future)
|
||||
// as the using AppGLFW constructs it empty as a member
|
||||
// when coming to life.
|
||||
CvMatRender() = default;
|
||||
// CvMatRender() = default;
|
||||
CvMatRender(CvMatRender&) = delete;
|
||||
CvMatRender(const CvMatRender&) = delete;
|
||||
CvMatRender(CvMatRender&&) = delete;
|
||||
CvMatRender& operator=(CvMatRender&) = default;
|
||||
CvMatRender& operator=(CvMatRender&& other) = default;
|
||||
CvMatRender& operator=(CvMatRender&) = delete;
|
||||
CvMatRender& operator=(CvMatRender&& other) = delete;
|
||||
~CvMatRender();
|
||||
explicit CvMatRender(std::shared_ptr<pixelarium::imaging::IPixelariumImage>& img);
|
||||
explicit CvMatRender(const cv::Mat& img);
|
||||
|
||||
public:
|
||||
GLuint Render();
|
||||
GLuint Render(float factor);
|
||||
GLuint Render(size_t width, size_t height);
|
||||
void ResetRenderImage();
|
||||
void ResetRenderImage(std::shared_ptr<pixelarium::imaging::IPixelariumImage>& img);
|
||||
|
||||
private:
|
||||
cv::Mat img_;
|
||||
std::shared_ptr<pixelarium::imaging::IPixelariumImage> base_;
|
||||
const cv::Mat& base_;
|
||||
GLuint texture_;
|
||||
|
||||
GLuint uploadTexture();
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
#include <memory>
|
||||
|
||||
#include "imaging/IPixelariumImage.hpp"
|
||||
#include "rendering/CvMatRender.hpp"
|
||||
#include "imgui.h"
|
||||
|
||||
namespace pixelarium::render
|
||||
{
|
||||
/// @brief An interface defining the contract on views to dedicated implementations of IPixelariumImage
|
||||
class IPixelariumImageView
|
||||
{
|
||||
public:
|
||||
@@ -17,11 +18,14 @@ class IPixelariumImageView
|
||||
public:
|
||||
virtual const bool* GetStatus() const noexcept { return &this->open_p; }
|
||||
virtual void ForceUpdate() noexcept { this->is_dirty_ = true; }
|
||||
virtual void SetInitialSize(float width = 700.0f, float height = 700.0f)
|
||||
{
|
||||
ImGui::SetNextWindowSize({width, height});
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<imaging::IPixelariumImage> img_{};
|
||||
std::unique_ptr<cv::Mat> cached_image_{};
|
||||
render::CvMatRender render_;
|
||||
bool open_p{true};
|
||||
bool is_dirty_{true};
|
||||
};
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include "ImageViewFactory.hpp"
|
||||
|
||||
#include <format>
|
||||
#include <memory>
|
||||
|
||||
#include "imaging/PixelariumImageFactory.hpp"
|
||||
#include "rendering/IPixelariumImageView.hpp"
|
||||
#include "rendering/PixelariumImageViewCzi.hpp"
|
||||
@@ -8,9 +10,10 @@
|
||||
|
||||
/// @brief Creates a PixelariumImageView from a resource image.
|
||||
/// @param image_id The ID of the image resource to render.
|
||||
/// @return A unique pointer to the PixelariumImageView, or nullptr if the image resource is not found or is empty. The image data is copied.
|
||||
/// @return A unique pointer to the PixelariumImageView, or nullptr if the image resource is not found or is empty. The
|
||||
/// image data is copied.
|
||||
std::unique_ptr<pixelarium::render::IPixelariumImageView> pixelarium::render::ImageViewFactory::RenderImage(
|
||||
size_t image_id)
|
||||
resources::ResourceKey image_id)
|
||||
{
|
||||
auto res{this->image_pool_.GetResource(image_id)};
|
||||
|
||||
@@ -35,15 +38,14 @@ std::unique_ptr<pixelarium::render::IPixelariumImageView> pixelarium::render::Im
|
||||
return {};
|
||||
case imaging::ImageFileType::PNG:
|
||||
case imaging::ImageFileType::JPG:
|
||||
log_.Info("Creating a Default View");
|
||||
log_.Info(std::format("{}: Creating a Default View", __PRETTY_FUNCTION__));
|
||||
// beware: here we copy the actual image resource over to the new image
|
||||
return std::make_unique<PixelariumImageViewDefault>(img);
|
||||
case imaging::ImageFileType::CZI:
|
||||
log_.Info("{}: Creating a CZI View");
|
||||
log_.Info(std::format("{}: Creating a CZI View", __PRETTY_FUNCTION__));
|
||||
// beware: here we copy the actual image resource over to the new image
|
||||
return std::make_unique<PixelariumImageViewCzi>(img);
|
||||
return std::make_unique<PixelariumImageViewCzi>(img, log_);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "utilities/ILog.hpp"
|
||||
namespace pixelarium::render
|
||||
{
|
||||
/// @brief Factory for instantiating matching views to different implementations of IPixelariumImage.
|
||||
class ImageViewFactory
|
||||
{
|
||||
using Image = imaging::IPixelariumImage;
|
||||
@@ -15,7 +16,7 @@ class ImageViewFactory
|
||||
public:
|
||||
explicit ImageViewFactory(Pool& pool, const Log& log) : image_pool_(pool), log_(log) {}
|
||||
|
||||
std::unique_ptr<IPixelariumImageView> RenderImage(size_t id);
|
||||
std::unique_ptr<IPixelariumImageView> RenderImage(resources::ResourceKey id);
|
||||
|
||||
private:
|
||||
Pool& image_pool_;
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
#include "PixelariumImageViewCzi.hpp"
|
||||
|
||||
#include <format>
|
||||
#include <memory>
|
||||
|
||||
#include "RenderHelpers.hpp"
|
||||
#include "imaging/IPixelariumImage.hpp"
|
||||
#include "imaging/impl/PixelariumCzi.hpp"
|
||||
#include "imgui.h"
|
||||
#include "rendering/CvMatRender.hpp"
|
||||
|
||||
pixelarium::render::PixelariumImageViewCzi::PixelariumImageViewCzi(std::shared_ptr<Image> img, const Log& log)
|
||||
: log_(log), render_(std::make_unique<CvMatRender>(*img->TryGetImage()))
|
||||
{
|
||||
img_ = img;
|
||||
auto czi_img = std::static_pointer_cast<imaging::PixelariumCzi>(this->img_);
|
||||
|
||||
auto stats = czi_img->GetStatistics();
|
||||
stats.dimBounds.EnumValidDimensions(
|
||||
[&](libCZI::DimensionIndex dim, int start, int) -> bool
|
||||
{
|
||||
this->dimension_map_[dim] = start;
|
||||
return true;
|
||||
});
|
||||
|
||||
this->SetInitialSize();
|
||||
log_.Info(std::format("{}: dimension map size: {}", __PRETTY_FUNCTION__, dimension_map_.size()));
|
||||
}
|
||||
|
||||
/// @brief Displays the image in an ImGui window.
|
||||
///
|
||||
@@ -20,7 +40,14 @@ void pixelarium::render::PixelariumImageViewCzi::ShowImage()
|
||||
|
||||
if (!this->cached_image_ || this->is_dirty_)
|
||||
{
|
||||
this->cached_image_ = this->img_->TryGetImage();
|
||||
log_.Info(std::format("{}: refreshing image.", __PRETTY_FUNCTION__));
|
||||
imaging::CziParams params;
|
||||
params.dimension_map = this->dimension_map_;
|
||||
this->cached_image_ = this->img_->TryGetImage(params);
|
||||
// Resetting the image while the renderer is possibly accessing the
|
||||
// image at the same time is not a good idea. Therefore, we simply create
|
||||
// a new renderer here.
|
||||
this->render_ = std::make_unique<CvMatRender>(*this->cached_image_);
|
||||
this->is_dirty_ = false;
|
||||
}
|
||||
|
||||
@@ -38,8 +65,8 @@ void pixelarium::render::PixelariumImageViewCzi::ShowImage()
|
||||
auto new_dim = ImGui::GetContentRegionAvail();
|
||||
auto texture =
|
||||
dim_changed_p(this->curr_dim_, new_dim)
|
||||
? this->render_.Render(static_cast<size_t>(this->curr_dim_.x), static_cast<size_t>(this->curr_dim_.y))
|
||||
: this->render_.Render();
|
||||
? this->render_->Render(static_cast<size_t>(this->curr_dim_.x), static_cast<size_t>(this->curr_dim_.y))
|
||||
: this->render_->Render();
|
||||
|
||||
this->curr_dim_ = new_dim;
|
||||
|
||||
@@ -53,12 +80,22 @@ void pixelarium::render::PixelariumImageViewCzi::ShowImage()
|
||||
ImGui::Text("%s", std::format("Render Dimensions W : {}, H: {}", curr_dim_.x, curr_dim_.y).c_str());
|
||||
ImGui::Text("Dimensions");
|
||||
ImGui::Separator();
|
||||
if (ImGui::Button("Update"))
|
||||
{
|
||||
this->ForceUpdate();
|
||||
}
|
||||
|
||||
auto stats = czi_img->GetStatistics();
|
||||
stats.dimBounds.EnumValidDimensions(
|
||||
[&](libCZI::DimensionIndex dim, int start, int size) -> bool
|
||||
{
|
||||
ImGui::Text("%c\t Start: %d\t End: %d", libCZI::Utils::DimensionToChar(dim), start, size);
|
||||
auto dim_char = libCZI::Utils::DimensionToChar(dim);
|
||||
if (ImGui::SliderInt(std::format("{}({}-{})", dim_char, start, size).c_str(), &dimension_map_[dim], start,
|
||||
size - 1))
|
||||
{
|
||||
this->ForceUpdate();
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "libCZI_DimCoordinate.h"
|
||||
#include "rendering/CvMatRender.hpp"
|
||||
#include "rendering/IPixelariumImageView.hpp"
|
||||
#include "utilities/ILog.hpp"
|
||||
|
||||
namespace pixelarium::render
|
||||
{
|
||||
/// @brief A CZI-specific implementation of IPixelariumImageView.
|
||||
class PixelariumImageViewCzi : public IPixelariumImageView
|
||||
{
|
||||
using Image = imaging::IPixelariumImage;
|
||||
using Render = render::CvMatRender;
|
||||
using Log = utils::log::ILog;
|
||||
|
||||
public:
|
||||
explicit PixelariumImageViewCzi(std::shared_ptr<Image> img)
|
||||
{
|
||||
img_ = img;
|
||||
render_ = Render(img_);
|
||||
}
|
||||
explicit PixelariumImageViewCzi(std::shared_ptr<Image> img, const Log& log);
|
||||
PixelariumImageViewCzi() = delete;
|
||||
PixelariumImageViewCzi(PixelariumImageViewCzi&) = delete;
|
||||
PixelariumImageViewCzi(const PixelariumImageViewCzi&) = delete;
|
||||
@@ -27,5 +31,8 @@ class PixelariumImageViewCzi : public IPixelariumImageView
|
||||
|
||||
private:
|
||||
ImVec2 curr_dim_{};
|
||||
const Log& log_;
|
||||
std::unordered_map<libCZI::DimensionIndex, int> dimension_map_;
|
||||
std::unique_ptr<CvMatRender> render_;
|
||||
};
|
||||
} // namespace pixelarium::render
|
||||
|
||||
@@ -9,16 +9,18 @@
|
||||
|
||||
namespace pixelarium::render
|
||||
{
|
||||
/// @brief A default implementation of IPixelariumImageView.
|
||||
/// This is sufficient for single dimension images like png or jpg.
|
||||
class PixelariumImageViewDefault : public IPixelariumImageView
|
||||
{
|
||||
using Image = imaging::IPixelariumImage;
|
||||
using Render = render::CvMatRender;
|
||||
|
||||
public:
|
||||
explicit PixelariumImageViewDefault(std::shared_ptr<Image> img)
|
||||
explicit PixelariumImageViewDefault(std::shared_ptr<Image> img) : render_(*img->TryGetImage())
|
||||
{
|
||||
img_ = img;
|
||||
render_ = Render(img_);
|
||||
this->SetInitialSize();
|
||||
}
|
||||
PixelariumImageViewDefault() = delete;
|
||||
PixelariumImageViewDefault(PixelariumImageViewDefault&) = delete;
|
||||
@@ -31,5 +33,6 @@ class PixelariumImageViewDefault : public IPixelariumImageView
|
||||
|
||||
private:
|
||||
ImVec2 curr_dim_{};
|
||||
CvMatRender render_;
|
||||
};
|
||||
} // namespace pixelarium::render
|
||||
|
||||
@@ -48,8 +48,13 @@ void pixelarium::render::RenderImageManager::Add(resources::ResourceKey key) noe
|
||||
{
|
||||
// we don't want to add what's already there
|
||||
// or empty render images
|
||||
if (this->render_image_map_.contains(key))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto current_view = this->view_factory_->RenderImage(key);
|
||||
if (this->render_image_map_.contains(key) || current_view == nullptr)
|
||||
if (current_view == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,14 @@ struct RenderImageStateWrapper
|
||||
const bool* show_state;
|
||||
};
|
||||
|
||||
|
||||
/// @brief Manage instances of IPixelariumImageView.
|
||||
///
|
||||
/// This class is used to keep track of what must be rendered.
|
||||
/// It manages a set of IPixelariumImageView instances that can be traversed
|
||||
/// via its Enumerate() function.
|
||||
/// Views that shall not be rendered anymore should be marked for deletion
|
||||
/// with MarkForDeletion()
|
||||
class RenderImageManager
|
||||
{
|
||||
using Pool = resources::ImageResourcePool;
|
||||
|
||||
Reference in New Issue
Block a user