mirror of
https://github.com/mpv-player/mpv
synced 2025-02-18 13:47:04 +00:00
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.
This commit is contained in:
parent
5838322975
commit
654721c27b
@ -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=<value>``
|
||||
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=<value>``, ``--scale-wblur=<value>``
|
||||
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=<value>``, ``--scale-wtaper=<value>``
|
||||
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=<value>``
|
||||
Set radius for tunable filters, must be a float number between 0.5 and
|
||||
|
@ -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},
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -35,6 +35,7 @@ struct scaler_fun {
|
||||
char *name;
|
||||
float params[2];
|
||||
float blur;
|
||||
float taper;
|
||||
};
|
||||
|
||||
struct scaler_config {
|
||||
|
Loading…
Reference in New Issue
Block a user