Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
67 changes: 49 additions & 18 deletions include/dmxdenoiser/Filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,73 @@ namespace dmxdenoiser

/*Abstract Base*/
struct Filter
{
{
// Default parameters
float m_strength = 1.0f; // Mixing factor with the original, range [0.0, 1.0]
bool m_filterAlpha = false;
std::vector<int> m_frames;
std::vector<std::string> m_layers;
Backend m_backend = Backend::CPU;
BackendResource m_backendResource;
std::string m_filterInfo{};

virtual const char* Name() const = 0;

virtual void setParams(const ParamDictionary& params) = 0;
void apply(const DMXImage& in, DMXImage& out) const { applyFilter(in, out); }
void apply(DMXImage& inOut) const {
DMXImage tmp{ inOut };
applyFilter(inOut, tmp);
inOut = std::move(tmp);
}

void apply(
const DMXImage& in,
DMXImage& out,
const std::vector<std::string>& layers = {},
const std::vector<int>& frames = {}
) const;
void apply(
DMXImage& inOut,
const std::vector<std::string>& layers = {},
const std::vector<int>& frames = {}
) const;

// String representation of the filter name and parameters for logging.
virtual std::string ToString() const = 0;
std::string ToString() const;

virtual ~Filter() = default;

protected:
virtual void resetParams() = 0;

// Apply implementations of the filter. NVI pattern
virtual void runFilterCPU(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const = 0;

virtual void runFilterGPU(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const;

virtual void runFilterMETAL(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const;

private:
// Apply implementation of the filter
virtual void applyFilter(const DMXImage& in, DMXImage& out) const = 0;
};
std::vector<int> resolveFrameIndices(
const DMXImage& input,
const std::vector<int>& frames
) const;
std::vector<int> resolveLayerIndices(
const DMXImage& input,
const std::vector<std::string>& layers
) const;

inline void Filter::resetParams() {
m_strength = 1.0f; m_filterAlpha = false;
m_frames.clear(); m_layers.clear();
m_backend = Backend::CPU; m_backendResource = {};
};



/**
* @brief Maps filter names to their parameter dictionaries.
*
Expand Down
10 changes: 5 additions & 5 deletions include/dmxdenoiser/ImageSequence.hpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

/*
void readSequence(
const DenoiserParams& denoiserParams,
int frame,
DMXImage& img);
/*
void readSequence(
const DenoiserParams& denoiserParams,
int frame,
DMXImage& img);
*/
26 changes: 17 additions & 9 deletions include/dmxdenoiser/filters/ConvolutionFilter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ namespace dmxdenoiser
/// - "filterAlpha": bool (whether to filter alpha channel, optional) default: false
struct ConvolutionFilter : public Filter
{
// Parameters
// Filter parameters
Kernel2D m_kernel;

// Required: unique filter name
Expand All @@ -38,16 +38,24 @@ namespace dmxdenoiser

void setParams(const ParamDictionary& params) override;

std::string ToString() const override;

protected:
void resetParams() override { Filter::resetParams(); m_kernel.clear(); };
void resetParams() override;

// Apply implementations of the filter. NVI pattern
void runFilterCPU(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const override;

void runFilterGPU(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const override;

private:
void applyFilter(const DMXImage& in, DMXImage& out) const override;
void convolveCPU(const DMXImage& input, DMXImage& output) const;
void convolveGPU(const DMXImage& input, DMXImage& output) const;
void convolveMETAL(const DMXImage& input, DMXImage& output) const;
};

} // namespace dmxdenoiser
27 changes: 10 additions & 17 deletions include/dmxdenoiser/filters/NLMFilter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,17 @@ namespace dmxdenoiser

void setParams(const ParamDictionary& params) override;

std::string ToString() const override;

protected:
void resetParams() override {
Filter::resetParams();
m_radius = 4;
m_patchRadius = 3;
m_sigmaBeauty = 1.f;
m_sigmaAlbedo = 1.f;
m_sigmaNormal = 1.f;
m_sigmaDepth = 1.f;
};

private:
void applyFilter(const DMXImage& in, DMXImage& out) const override;
void runFilterCPU(const DMXImage& input, DMXImage& output) const;
void runFilterGPU(const DMXImage& input, DMXImage& output) const;
void runFilterMETAL(const DMXImage& input, DMXImage& output) const;
void resetParams() override;

// Apply implementations of the filter. NVI pattern
void runFilterCPU(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const override;

};

} // namespace dmxdenoiser
2 changes: 2 additions & 0 deletions include/dmxdenoiser/utils/NumericUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ namespace dmxdenoiser
return abs_c(a - b) < epsilon;
}

DMX_CPU_GPU inline float sqr(float a) { return a*a; }

DMX_CPU_GPU /*constexpr*/ inline int clampi(int x, int min, int max) {
return (x > max) ? max : ((x < min) ? min : x);
}
Expand Down
179 changes: 179 additions & 0 deletions src/Filter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
#include <dmxdenoiser/DMXImage.hpp>
#include <dmxdenoiser/Filter.hpp>

namespace dmxdenoiser
{

std::vector<int> Filter::resolveFrameIndices(
const DMXImage& input,
const std::vector<int>& frames
) const {
std::vector<int> frameIndices;
// If no specific frames were set, process all frames by default.
if (frames.empty())
{
for (int i = 0; i < input.numFrames(); ++i) // Add all frames
frameIndices.push_back(i);
} else {
for (int i = 0; i < frames.size(); ++i)
{
int requestedFrame = frames[i];
if(requestedFrame < input.numFrames())
frameIndices.push_back(requestedFrame);
else
DMX_LOG_WARNING(Name(),
"Filter::apply(): requested frame ", requestedFrame,
" out of range for input; skipping");
}
}
return frameIndices;
}

std::vector<int> Filter::resolveLayerIndices(
const DMXImage& input,
const std::vector<std::string>& layers
) const {
std::vector<int> layerIndices;
// If no specific layers were set, process by default.
if (layers.empty()) {
layerIndices = input.getFilteringLayersIndices();
} else {
for (const auto& layer : layers)
{
if (input.hasLayer(layer))
layerIndices.push_back(input.getLayerIndex(layer));
else
DMX_LOG_WARNING(Name(),
"Filter::apply(): requested layer '", layer, "' not found; skipping");
}
}
return layerIndices;
}

void Filter::apply(
const DMXImage& in,
DMXImage& out,
const std::vector<std::string>& layers,
const std::vector<int>& frames
) const
{
std::vector<int> frameIndices = resolveFrameIndices(in, frames);
std::vector<int> layerIndices = resolveLayerIndices(in, layers);

if (frameIndices.empty() || layerIndices.empty()) {
DMX_LOG_WARNING(Name(),
"Filter::apply(): no valid frames or layers to process; skipping");
return;
}

if (m_backend == Backend::CPU) {
this->runFilterCPU(in, out, layerIndices, frameIndices);
} else if (m_backend == Backend::GPU) {
this->runFilterGPU(in, out, layerIndices, frameIndices);
} else if (m_backend == Backend::METAL) {
this->runFilterMETAL(in, out, layerIndices, frameIndices);
} else {
DMX_LOG_ERROR(Name(),
"apply(): Unsupported backend: ", dmxdenoiser::ToString(m_backend));
throw std::runtime_error("Unsupported backend");
}
}

void Filter::apply(
DMXImage& inOut,
const std::vector<std::string>& layers,
const std::vector<int>& frames
) const
{
DMXImage tmp{ inOut };
apply(inOut, tmp, layers, frames);
inOut = std::move(tmp);
}

void Filter::resetParams() {
m_strength = 1.0f;
m_filterAlpha = false;
m_backend = Backend::CPU;
m_backendResource = {};
m_filterInfo.clear();
};

void Filter::setParams(const ParamDictionary& params) {
if (auto v = params.getSingleParam<float>("strength")) {
m_strength = *v;
m_filterInfo += "\tstrength (set) = " + std::to_string(m_strength) + "\n";
}
else
{
m_filterInfo += "\tstrength (default) = " + std::to_string(m_strength) + "\n";
DMX_LOG_TRACE(Name(), "setParams(): 'strength' parameter not set, using default: ", m_strength);
}

if (auto v = params.getSingleParam<bool>("filterAlpha"))
{
m_filterAlpha = *v;
m_filterInfo += "\tfilterAlpha (set) = " + std::string(m_filterAlpha ? "true" : "false") + "\n";
}
else
{
m_filterInfo += "\tfilterAlpha (default) = " + std::string(m_filterAlpha ? "true" : "false") + "\n";
DMX_LOG_TRACE(Name(),
"setParams(): 'filterAlpha' parameter not set, using default: ", m_filterAlpha);
}

if (auto v = params.getSingleParam<Backend>("backend"))
{
m_backend = *v;
m_filterInfo += "\tbackend (set) = " + dmxdenoiser::ToString(m_backend) + "\n";
}
else
{
m_filterInfo += "\tbackend (default) = " + dmxdenoiser::ToString(m_backend) + "\n";
DMX_LOG_TRACE(Name(),
"setParams(): 'backend' parameter not set, using default: ", dmxdenoiser::ToString(m_backend));
}

if (auto v = params.getSingleParam<BackendResource>("backendResource"))
{
m_backendResource = *v;
m_filterInfo += "\tbackendResource (set) = \n" + m_backendResource.ToString(10) + "\n";
}
else
{
m_filterInfo += "\tbackendResource (default) = \n" + m_backendResource.ToString(10) + "\n";
DMX_LOG_TRACE(Name(),
"setParams(): 'backendResource' parameter not set, using default: \n",
m_backendResource.ToString(10));
}
}

std::string Filter::ToString() const
{
return std::string(Name()) + ": \n" + m_filterInfo;
};

void Filter::runFilterGPU(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const {
// Default: fallback
DMX_LOG_WARNING(Name(),
"GPU backend not implemented; falling back to CPU implementation");
runFilterCPU(input, output, layers, frames);
}

void Filter::runFilterMETAL(
const DMXImage& input,
DMXImage& output,
const std::vector<int>& layers,
const std::vector<int>& frames
) const {
// Default: fallback
DMX_LOG_WARNING(Name(),
"GPU backend not implemented; falling back to CPU implementation");
runFilterCPU(input, output, layers, frames);
}

} // namespace dmxdenoiser
Loading