Original implementation of Sharpen Image filters

This commit is contained in:
Bruno Herbelin
2022-05-31 22:53:28 +02:00
parent e3bb95b3dd
commit 7c850b0405
12 changed files with 153 additions and 57 deletions

View File

@@ -150,6 +150,9 @@ void CloneSource::setFilter(FrameBufferFilter::Type T)
case FrameBufferFilter::FILTER_BLUR: case FrameBufferFilter::FILTER_BLUR:
filter_ = new BlurFilter; filter_ = new BlurFilter;
break; break;
case FrameBufferFilter::FILTER_SHARPEN:
filter_ = new SharpenFilter;
break;
case FrameBufferFilter::FILTER_IMAGE: case FrameBufferFilter::FILTER_IMAGE:
filter_ = new ImageFilter; filter_ = new ImageFilter;
break; break;

View File

@@ -6,7 +6,7 @@
#include "FrameBufferFilter.h" #include "FrameBufferFilter.h"
const char* FrameBufferFilter::type_label[FrameBufferFilter::FILTER_INVALID] = { const char* FrameBufferFilter::type_label[FrameBufferFilter::FILTER_INVALID] = {
"None", "Delay", "Blur", "Shader code" "None", "Delay", "Blur", "Sharpen", "Shader code"
}; };
FrameBufferFilter::FrameBufferFilter() : enabled_(true), input_(nullptr) FrameBufferFilter::FrameBufferFilter() : enabled_(true), input_(nullptr)

View File

@@ -21,6 +21,7 @@ public:
FILTER_PASSTHROUGH = 0, FILTER_PASSTHROUGH = 0,
FILTER_DELAY, FILTER_DELAY,
FILTER_BLUR, FILTER_BLUR,
FILTER_SHARPEN,
FILTER_IMAGE, FILTER_IMAGE,
FILTER_INVALID FILTER_INVALID
} Type; } Type;

View File

@@ -818,6 +818,51 @@ void ImGuiVisitor::visit (BlurFilter& f)
} }
void ImGuiVisitor::visit (SharpenFilter& f)
{
std::ostringstream oss;
oss << "Blur ";
// Method selection
if (ImGuiToolkit::IconButton(7, 16)) {
f.setMethod( 0 );
oss << SharpenFilter::method_label[0];
Action::manager().store(oss.str());
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
int m = (int) f.method();
if (ImGui::Combo("Method", &m, SharpenFilter::method_label, IM_ARRAYSIZE(SharpenFilter::method_label) )) {
f.setMethod( m );
oss << SharpenFilter::method_label[m];
Action::manager().store(oss.str());
}
// List of parameters
std::map<std::string, float> filter_parameters = f.program().parameters();
for (auto param = filter_parameters.begin(); param != filter_parameters.end(); ++param)
{
ImGui::PushID( param->first.c_str() );
float v = param->second;
if (ImGuiToolkit::IconButton(13, 14)) {
v = 0.f;
f.setProgramParameter(param->first, v);
}
ImGui::SameLine(0, IMGUI_SAME_LINE);
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
if (ImGui::SliderFloat( param->first.c_str(), &v, 0.f, 1.f, "%.2f")) {
f.setProgramParameter(param->first, v);
}
if (ImGui::IsItemDeactivatedAfterEdit()) {
oss << SharpenFilter::method_label[ f.method() ];
oss << " : " << param->first << " " << std::setprecision(3) <<param->second;
Action::manager().store(oss.str());
}
ImGui::PopID();
}
}
void ImGuiVisitor::visit (ImageFilter& f) void ImGuiVisitor::visit (ImageFilter& f)
{ {
// Selection of Algorithm // Selection of Algorithm

View File

@@ -40,6 +40,7 @@ public:
void visit (PassthroughFilter&) override; void visit (PassthroughFilter&) override;
void visit (DelayFilter&) override; void visit (DelayFilter&) override;
void visit (BlurFilter&) override; void visit (BlurFilter&) override;
void visit (SharpenFilter&) override;
void visit (ImageFilter&) override; void visit (ImageFilter&) override;
}; };

View File

@@ -485,15 +485,15 @@ void ImageFilter::setProgramParameter(const std::string &p, float value)
//////////////////////////////////////// ////////////////////////////////////////
const char* BlurFilter::method_label[BlurFilter::BLUR_INVALID] = { const char* BlurFilter::method_label[BlurFilter::BLUR_INVALID] = {
"Gaussian", "Hash", "Openning", "Closing", "Fast 2x2" "Gaussian", "Scattered", "Opening", "Closing", "Fast"
}; };
std::vector< FilteringProgram > BlurFilter::programs_ = { std::vector< FilteringProgram > BlurFilter::programs_ = {
FilteringProgram("Gaussian", "shaders/filters/blur_1.glsl", "shaders/filters/blur_2.glsl", { { "Radius", 0.5} }), FilteringProgram("Gaussian", "shaders/filters/blur_1.glsl", "shaders/filters/blur_2.glsl", { { "Radius", 0.5} }),
FilteringProgram("Hashed", "shaders/filters/hashedblur.glsl", "", { { "Iterations", 0.5 }, { "Radius", 0.5} }), FilteringProgram("Scattered","shaders/filters/hashedblur.glsl", "", { { "Radius", 0.5}, { "Iterations", 0.25 } }),
FilteringProgram("Openning", "shaders/filters/hashederosion.glsl", "shaders/filters/hasheddilation.glsl", { { "Radius", 0.5} }), FilteringProgram("Opening", "shaders/filters/hashederosion.glsl", "shaders/filters/hasheddilation.glsl", { { "Radius", 0.5} }),
FilteringProgram("Closing", "shaders/filters/hasheddilation.glsl", "shaders/filters/hashederosion.glsl", { { "Radius", 0.5} }), FilteringProgram("Closing", "shaders/filters/hasheddilation.glsl","shaders/filters/hashederosion.glsl", { { "Radius", 0.5} }),
FilteringProgram("Fast 2x2", "shaders/filters/blur.glsl", "", { }) FilteringProgram("Fast", "shaders/filters/blur.glsl", "", { })
}; };
BlurFilter::BlurFilter (): ImageFilter(), method_(BLUR_INVALID), mipmap_buffer_(nullptr) BlurFilter::BlurFilter (): ImageFilter(), method_(BLUR_INVALID), mipmap_buffer_(nullptr)
@@ -588,4 +588,37 @@ void BlurFilter::accept (Visitor& v)
} }
////////////////////////////////////////
///// //
//// SHARPENING FILTERS ///
/// ////
////////////////////////////////////////
const char* SharpenFilter::method_label[SharpenFilter::SHARPEN_INVALID] = {
"Unsharp mask", "Convolution", "Edge"
};
std::vector< FilteringProgram > SharpenFilter::programs_ = {
FilteringProgram("Unsharp Mask", "shaders/filters/sharpen_1.glsl", "shaders/filters/sharpen_2.glsl", { { "Amount", 0.5} }),
FilteringProgram("Sharpen", "shaders/filters/sharp.glsl", "", { { "Amount", 0.5} }),
FilteringProgram("Sharp Edge", "shaders/filters/bilinear.glsl", "shaders/filters/sharpenedge.glsl", { { "Strength", 0.5} }),
};
SharpenFilter::SharpenFilter (): ImageFilter(), method_(SHARPEN_INVALID)
{
}
void SharpenFilter::setMethod(int method)
{
method_ = (SharpenMethod) CLAMP(method, SHARPEN_MASK, SHARPEN_INVALID-1);
setProgram( programs_[ (int) method_] );
}
void SharpenFilter::accept (Visitor& v)
{
FrameBufferFilter::accept(v);
v.visit(*this);
}

View File

@@ -137,6 +137,33 @@ private:
}; };
class SharpenFilter : public ImageFilter
{
public:
SharpenFilter();
// Algorithms used for sharpen
typedef enum {
SHARPEN_MASK = 0,
SHARPEN_CONVOLUTION,
SHARPEN_EDGE,
SHARPEN_INVALID
} SharpenMethod;
static const char* method_label[SHARPEN_INVALID];
SharpenMethod method () const { return method_; }
void setMethod(int method);
// implementation of FrameBufferFilter
Type type() const override { return FrameBufferFilter::FILTER_SHARPEN; }
void accept (Visitor& v) override;
private:
SharpenMethod method_;
static std::vector< FilteringProgram > programs_;
};
#endif // IMAGEFILTER_H #endif // IMAGEFILTER_H

View File

@@ -45,6 +45,7 @@ class FrameBufferFilter;
class PassthroughFilter; class PassthroughFilter;
class DelayFilter; class DelayFilter;
class BlurFilter; class BlurFilter;
class SharpenFilter;
class ImageFilter; class ImageFilter;
class SourceCallback; class SourceCallback;
@@ -106,6 +107,7 @@ public:
virtual void visit (PassthroughFilter&) {} virtual void visit (PassthroughFilter&) {}
virtual void visit (DelayFilter&) {} virtual void visit (DelayFilter&) {}
virtual void visit (BlurFilter&) {} virtual void visit (BlurFilter&) {}
virtual void visit (SharpenFilter&) {}
virtual void visit (ImageFilter&) {} virtual void visit (ImageFilter&) {}
virtual void visit (SourceCallback&) {} virtual void visit (SourceCallback&) {}

View File

@@ -37,10 +37,10 @@ void mainImage( out vec4 fragColor, in vec2 fragCoord )
float ar = iResolution.y / iResolution.x ; float ar = iResolution.y / iResolution.x ;
float R = 0.25 * Radius ; float R = 0.25 * Radius ;
vec4 O = vec4(1.); vec4 O = vec4(1.);
float N = 17; float N = 17;
for (float i = 0.; i < N; i++) { for (float i = 0.; i < N; i++) {
vec2 q = vec2(cos(TWOPI*((i+.5)/N)) * ar, sin(TWOPI*(i+.5)/N)) * Hash01(uv, floatBitsToUint(i)); vec2 q = vec2(cos(TWOPI*((i+.5)/N)) * ar, sin(TWOPI*(i+.5)/N)) * (0.1 + 0.9 * Hash01(uv, floatBitsToUint(i)));
// vec2 q = vec2(cos(TWOPI*((i+.5)/N)) * ar, sin(TWOPI*(i+.5)/N)) * hash(vec2(i + 12., uv.x * uv.y + 24.)); // vec2 q = vec2(cos(TWOPI*((i+.5)/N)) * ar, sin(TWOPI*(i+.5)/N)) * hash(vec2(i + 12., uv.x * uv.y + 24.));
O = min( texture(iChannel0, uv + q*R), O ); O = min( texture(iChannel0, uv + q*R), O );
} }

View File

@@ -38,10 +38,10 @@ void mainImage( out vec4 fragColor, in vec2 fragCoord )
float ar = iResolution.y / iResolution.x ; float ar = iResolution.y / iResolution.x ;
float R = 0.25 * Radius ; float R = 0.25 * Radius ;
vec4 O = vec4(0.); vec4 O = vec4(0.);
float N = 17; float N = 17;
for (float i = 0.; i < N; i++) { for (float i = 0.; i < N; i++) {
vec2 q = vec2(cos(TWOPI*i/N) * ar, sin(TWOPI*i/N)) * Hash01(uv, floatBitsToUint(i) ); vec2 q = vec2(cos(TWOPI*i/N) * ar, sin(TWOPI*i/N)) * (0.1 + 0.9 * Hash01(uv, floatBitsToUint(i)));
// vec2 q = vec2(cos(TWOPI*i/N) * ar, sin(TWOPI*i/N)) * hash(vec2(i, uv.x + uv.y)); // vec2 q = vec2(cos(TWOPI*i/N) * ar, sin(TWOPI*i/N)) * hash(vec2(i, uv.x + uv.y));
O = max( texture(iChannel0, uv + q*R), O ); O = max( texture(iChannel0, uv + q*R), O );
} }

View File

@@ -1,30 +1,22 @@
uniform float Amount; uniform float Amount;
float SCurve (float x) { #define N 7
x = x * 2.0 - 1.0; vec4 blur1D(vec2 U, vec2 D, float rad)
return -x * abs(x) * 0.5 + x + 0.5; {
} float w = rad * iResolution.y;
float z = ceil(max(0.,log2(w/float(N)))); // LOD N/w = res/2^z
vec3 BlurV (sampler2D source, vec2 uv, float step, float radius) { vec4 O = vec4(0);
vec3 C = vec3(0.0); float r = float(N-1)/2., g, t=0., x;
float divisor = 0.0; for( int k=0; k<N; k++ ) {
float weight = 0.0; x = float(k)/r -1.;
float radiusMultiplier = 1.0 / max(1.0, radius); t += g = exp(-2.*x*x );
O += g * textureLod(iChannel0, (U + w*x*D) / iResolution.xy, z );
// loop on pixels in Y to apply vertical blur
for (float y = -radius; y <= radius; y++) {
weight = SCurve(1.0 - (abs(y) * radiusMultiplier));
C += texture(source, uv + vec2(0.0, y * step)).rgb * weight;
divisor += weight;
} }
return O/t;
return C / divisor;
} }
void mainImage( out vec4 fragColor, in vec2 fragCoord ) void mainImage( out vec4 fragColor, in vec2 fragCoord )
{ {
vec2 uv = fragCoord.xy / iResolution.xy;
// Apply vertical blur // Apply vertical blur
fragColor = vec4( BlurV(iChannel0, uv, 1.0 / iResolution.y, mix(1.0, 0.1*iResolution.y, Amount)), 1.0); fragColor = blur1D(fragCoord, vec2(1,0), 0.1 * Amount);
} }

View File

@@ -1,37 +1,29 @@
uniform float Amount; uniform float Amount;
float SCurve (float x) { #define N 7
x = x * 2.0 - 1.0; vec4 blur1D(vec2 U, vec2 D, float rad)
return -x * abs(x) * 0.5 + x + 0.5; {
} float w = rad * iResolution.y;
float z = ceil(max(0.,log2(w/float(N)))); // LOD N/w = res/2^z
vec3 BlurH (sampler2D source, vec2 uv, float step, float radius) { vec4 O = vec4(0);
vec3 C = vec3(0.0); float r = float(N-1)/2., g, t=0., x;
float divisor = 0.0; for( int k=0; k<N; k++ ) {
float weight = 0.0; x = float(k)/r -1.;
float radiusMultiplier = 1.0 / max(1.0, radius); t += g = exp(-2.*x*x );
O += g * textureLod(iChannel0, (U + w*x*D) / iResolution.xy, z );
// loop on pixels in X to apply horizontal blur
for (float x = -radius; x <= radius; x++) {
weight = SCurve(1.0 - (abs(x) * radiusMultiplier));
C += texture(source, uv + vec2(x * step, 0.0)).rgb * weight;
divisor += weight;
} }
return O/t;
return C / divisor;
} }
void mainImage( out vec4 fragColor, in vec2 fragCoord ) void mainImage( out vec4 fragColor, in vec2 fragCoord )
{ {
vec2 uv = fragCoord.xy / iResolution.xy;
// get original image // get original image
vec3 c = texture(iChannel1, uv).rgb; vec4 c = texture(iChannel1, fragCoord.xy / iResolution.xy);
// Remove blurred image to original image // Remove blurred image to original image
vec3 lumcoeff = vec3(0.299,0.587,0.114); vec4 lumcoeff = vec4(0.299, 0.587, 0.114, 1.);
float luminance = dot( c - BlurH(iChannel0, uv, 1.0 / iResolution.x, mix(1.0, 0.1*iResolution.y, Amount) ), lumcoeff); float luminance = dot( c - blur1D(fragCoord, vec2(0,1), 0.1 * Amount ), lumcoeff);
// composition // composition
fragColor = vec4( c + vec3(luminance), 1.0); fragColor = c + vec4(luminance);
} }