From 654721c27bc0b2ca5fe27a5932b42332c1674547 Mon Sep 17 00:00:00 2001 From: Niklas Haas Date: Wed, 26 Oct 2016 16:32:57 +0200 Subject: filter_kernels: add ability to taper kernels/windows This allows us to define the tukey window (and other tapered windows). Also add a missing option definition for `wblur` while we're at it, to make testing out window-related stuff easier. --- DOCS/man/options.rst | 19 ++++++++++++------ video/out/filter_kernels.c | 49 ++++++++++++++++++++++++++++------------------ video/out/filter_kernels.h | 1 + video/out/opengl/video.c | 11 ++++++++++- video/out/opengl/video.h | 1 + 5 files changed, 55 insertions(+), 26 deletions(-) diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 45c0bca2f1..3fd3b62d85 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -3874,12 +3874,19 @@ The following video options are currently all specific to ``--vo=opengl`` and never interpolate, thus behaving as if the regular nearest neighbour algorithm was used. Defaults to 0.0. -``--scale-blur=`` - Kernel scaling factor (also known as a blur factor). Decreasing this makes - the result sharper, increasing it makes it blurrier (default 0). If set to - 0, the kernel's preferred blur factor is used. Note that setting this too - low (eg. 0.5) leads to bad results. It's generally recommended to stick to - values between 0.8 and 1.2. +``--scale-blur=``, ``--scale-wblur=`` + Kernel/window scaling factor (also known as a blur factor). Decreasing this + makes the result sharper, increasing it makes it blurrier (default 0). If + set to 0, the kernel's preferred blur factor is used. Note that setting + this too low (eg. 0.5) leads to bad results. It's generally recommended to + stick to values between 0.8 and 1.2. + +``--scale-taper=``, ``--scale-wtaper=`` + Kernel/window taper factor. Increasing this flattens the filter function. + Value range is 0 to 1. A value of 0 (the default) means no flattening, a + value of 1 makes the filter completely flat (equivalent to a box function). + Values in between mean that some portion will be flat and the actual filter + function will be squeezed into the space in between. ``--scale-radius=`` Set radius for tunable filters, must be a float number between 0.5 and diff --git a/video/out/filter_kernels.c b/video/out/filter_kernels.c index fe5265c70c..c5a12295f7 100644 --- a/video/out/filter_kernels.c +++ b/video/out/filter_kernels.c @@ -92,34 +92,45 @@ bool mp_init_filter(struct filter_kernel *filter, const int *sizes, } } -// Sample from the blurred, windowed kernel. Note: The window is always -// stretched to the true radius, regardless of the filter blur/scale. -static double sample_filter(struct filter_kernel *filter, - struct filter_window *window, double x) +// Sample from a blurred and tapered window +static double sample_window(struct filter_window *kernel, double x) { - double bk = filter->f.blur > 0.0 ? filter->f.blur : 1.0; - double bw = window->blur > 0.0 ? window->blur : 1.0; - double c = fabs(x) / (filter->inv_scale * bk); - double w = window->weight ? window->weight(window, x/bw * window->radius - / filter->f.radius) - : 1.0; - double v = c < filter->f.radius ? w * filter->f.weight(&filter->f, c) : 0.0; - return filter->clamp ? fmax(0.0, fmin(1.0, v)) : v; + if (!kernel->weight) + return 1.0; + + // All windows are symmetric, this makes life easier + x = fabs(x); + if (x >= kernel->radius) + return 0.0; + + // Stretch and taper the window size as needed + x = kernel->blur > 0.0 ? x / kernel->blur : x; + x = x <= kernel->taper ? 0.0 : (x - kernel->taper) / (1 - kernel->taper); + + return kernel->weight(kernel, x); +} + +// Evaluate a filter's kernel and window at a given absolute position +static double sample_filter(struct filter_kernel *filter, double x) +{ + // The window is always stretched to the entire kernel + double w = sample_window(&filter->w, x / filter->f.radius * filter->w.radius); + double k = sample_window(&filter->f, x / filter->inv_scale); + return filter->clamp ? fmax(0.0, fmin(1.0, w * k)) : w * k; } // Calculate the 1D filtering kernel for N sample points. // N = number of samples, which is filter->size // The weights will be stored in out_w[0] to out_w[N - 1] // f = x0 - abs(x0), subpixel position in the range [0,1) or [0,1]. -static void mp_compute_weights(struct filter_kernel *filter, - struct filter_window *window, - double f, float *out_w) +static void mp_compute_weights(struct filter_kernel *filter, double f, + float *out_w) { assert(filter->size > 0); double sum = 0; for (int n = 0; n < filter->size; n++) { double x = f - (n - filter->size / 2 + 1); - double w = sample_filter(filter, window, x); + double w = sample_filter(filter, x); out_w[n] = w; sum += w; } @@ -138,17 +149,16 @@ static void mp_compute_weights(struct filter_kernel *filter, // [0.5 / count, 1.0 - 0.5 / count]. void mp_compute_lut(struct filter_kernel *filter, int count, float *out_array) { - struct filter_window *window = &filter->w; if (filter->polar) { // Compute a 1D array indexed by radius for (int x = 0; x < count; x++) { double r = x * filter->f.radius / (count - 1); - out_array[x] = sample_filter(filter, window, r); + out_array[x] = sample_filter(filter, r); } } else { // Compute a 2D array indexed by subpixel position for (int n = 0; n < count; n++) { - mp_compute_weights(filter, window, n / (double)(count - 1), + mp_compute_weights(filter, n / (double)(count - 1), out_array + filter->size * n); } } @@ -321,6 +331,7 @@ const struct filter_window mp_filter_windows[] = { {"triangle", 1, triangle}, {"bartlett", 1, triangle}, {"hanning", 1, hanning}, + {"tukey", 1, hanning, .taper = 0.5}, {"hamming", 1, hamming}, {"quadric", 1.5, quadric}, {"welch", 1, welch}, diff --git a/video/out/filter_kernels.h b/video/out/filter_kernels.h index 2354ef4d0c..fc90a1cdde 100644 --- a/video/out/filter_kernels.h +++ b/video/out/filter_kernels.h @@ -22,6 +22,7 @@ struct filter_window { double params[2]; // User-defined custom filter parameters. Not used by // all filters double blur; // Blur coefficient (sharpens or widens the filter) + double taper; // Taper coefficient (flattens the filter's center) }; struct filter_kernel { diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 17468ab9a5..8afae2f062 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -340,7 +340,10 @@ static int validate_window_opt(struct mp_log *log, const m_option_t *opt, OPT_FLOAT(n"-param1", scaler[i].kernel.params[0], 0), \ OPT_FLOAT(n"-param2", scaler[i].kernel.params[1], 0), \ OPT_FLOAT(n"-blur", scaler[i].kernel.blur, 0), \ + OPT_FLOATRANGE(n"-taper", scaler[i].kernel.taper, 0, 0.0, 1.0), \ OPT_FLOAT(n"-wparam", scaler[i].window.params[0], 0), \ + OPT_FLOAT(n"-wblur", scaler[i].window.blur, 0), \ + OPT_FLOATRANGE(n"-wtaper", scaler[i].window.taper, 0, 0.0, 1.0), \ OPT_FLAG(n"-clamp", scaler[i].clamp, 0), \ OPT_FLOATRANGE(n"-radius", scaler[i].radius, 0, 0.5, 16.0), \ OPT_FLOATRANGE(n"-antiring", scaler[i].antiring, 0, 0.0, 1.0), \ @@ -1357,7 +1360,8 @@ static bool scaler_fun_eq(struct scaler_fun a, struct scaler_fun b) return ((!a.name && !b.name) || strcmp(a.name, b.name) == 0) && double_seq(a.params[0], b.params[0]) && double_seq(a.params[1], b.params[1]) && - a.blur == b.blur; + a.blur == b.blur && + a.taper == b.taper; } static bool scaler_conf_eq(struct scaler_config a, struct scaler_config b) @@ -1418,6 +1422,11 @@ static void reinit_scaler(struct gl_video *p, struct scaler *scaler, if (conf->window.blur > 0.0) scaler->kernel->w.blur = conf->window.blur; + if (conf->kernel.taper > 0.0) + scaler->kernel->f.taper = conf->kernel.taper; + if (conf->window.taper > 0.0) + scaler->kernel->w.taper = conf->window.taper; + if (scaler->kernel->f.resizable && conf->radius > 0.0) scaler->kernel->f.radius = conf->radius; diff --git a/video/out/opengl/video.h b/video/out/opengl/video.h index 5011af83d1..54b7022f27 100644 --- a/video/out/opengl/video.h +++ b/video/out/opengl/video.h @@ -35,6 +35,7 @@ struct scaler_fun { char *name; float params[2]; float blur; + float taper; }; struct scaler_config { -- cgit v1.2.3