Skip to content

Commit 2d8936f

Browse files
committed
Add custom kernels
Functionality is about the same as in Akarin's fork, but it's implemented in a different way internally. Also added force/force_h/force_v paramenters to force sampling even if the resolution stays the same. Only supported in the VapourSynth plugin.
1 parent c4bb61d commit 2d8936f

7 files changed

Lines changed: 202 additions & 47 deletions

File tree

README.md

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,37 @@ The VapourSynth plugin itself supports every constant input format. If the forma
1010
The included python wrapper, contrary to using the plugin directly, doesn't descale the chroma planes but scales them normally with `Spline36`.
1111

1212
```
13-
descale.Debilinear(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, int opt=0)
13+
descale.Debilinear(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, bool force, bool force_h, bool force_v, int opt=0)
1414
15-
descale.Debicubic(clip src, int width, int height, float b=0.0, float c=0.5, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, int opt=0)
15+
descale.Debicubic(clip src, int width, int height, float b=0.0, float c=0.5, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, bool force, bool force_h, bool force_v, int opt=0)
1616
17-
descale.Delanczos(clip src, int width, int height, int taps=3, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, int opt=0)
17+
descale.Delanczos(clip src, int width, int height, int taps=3, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, bool force, bool force_h, bool force_v, int opt=0)
1818
19-
descale.Despline16(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, int opt=0)
19+
descale.Despline16(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, bool force, bool force_h, bool force_v, int opt=0)
2020
21-
descale.Despline36(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, int opt=0)
21+
descale.Despline36(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, bool force, bool force_h, bool force_v, int opt=0)
2222
23-
descale.Despline64(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, int opt=0)
23+
descale.Despline64(clip src, int width, int height, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, bool force, bool force_h, bool force_v, int opt=0)
2424
25-
descale.Descale(clip src, int width, int height, str kernel, int taps=3, float b=0.0, float c=0.0, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, int opt=0)
25+
descale.Descale(clip src, int width, int height, str kernel, func custom_kernel, int taps=3, float b=0.0, float c=0.0, float src_left=0.0, float src_top=0.0, float src_width=width, float src_height=height, bool force, bool force_h, bool force_v, int opt=0)
2626
```
2727

2828
The AviSynth+ plugin is used similarly, but without the `descale` namespace.
29+
Custom kernels are only supported in the VapourSynth plugin.
30+
31+
### Custom kernels
32+
33+
```python
34+
# Debilinear
35+
core.descale.Descale(src, w, h, custom_kernel=lambda x: 1.0 - x, taps=1)
36+
37+
# Delanczos
38+
import math
39+
def sinc(x):
40+
return 1.0 if x == 0 else math.sin(x * math.pi) / (x * math.pi)
41+
taps = 3
42+
core.descale.Descale(src, w, h, custom_kernel=lambda x: sinc(x) * sinc(x / taps), taps=taps)
43+
```
2944

3045
## How does this work?
3146

descale.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def Despline64(src, width, height, yuv444=False, gray=False, chromaloc=None):
2222
return Descale(src, width, height, kernel='spline64', taps=None, b=None, c=None, yuv444=yuv444, gray=gray, chromaloc=chromaloc)
2323

2424

25-
def Descale(src, width, height, kernel='bilinear', taps=3, b=0.0, c=0.5, yuv444=False, gray=False, chromaloc=None):
25+
def Descale(src, width, height, kernel=None, custom_kernel=None, taps=None, b=None, c=None, yuv444=False, gray=False, chromaloc=None):
2626
src_f = src.format
2727
src_cf = src_f.color_family
2828
src_st = src_f.sample_type
@@ -31,10 +31,10 @@ def Descale(src, width, height, kernel='bilinear', taps=3, b=0.0, c=0.5, yuv444=
3131
src_sh = src_f.subsampling_h
3232

3333
if src_cf == RGB and not gray:
34-
rgb = to_rgbs(src).descale.Descale(width, height, kernel, taps, b, c)
34+
rgb = to_rgbs(src).descale.Descale(width, height, kernel, custom_kernel, taps, b, c)
3535
return rgb.resize.Point(format=src_f.id)
3636

37-
y = to_grays(src).descale.Descale(width, height, kernel, taps, b, c)
37+
y = to_grays(src).descale.Descale(width, height, kernel, custom_kernel, taps, b, c)
3838
y_f = core.register_format(GRAY, src_st, src_bits, 0, 0)
3939
y = y.resize.Point(format=y_f.id)
4040

include/descale.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ typedef enum DescaleMode
3232
DESCALE_MODE_LANCZOS = 3,
3333
DESCALE_MODE_SPLINE16 = 4,
3434
DESCALE_MODE_SPLINE36 = 5,
35-
DESCALE_MODE_SPLINE64 = 6
35+
DESCALE_MODE_SPLINE64 = 6,
36+
DESCALE_MODE_CUSTOM = 7
3637
} DescaleMode;
3738

3839

@@ -51,15 +52,23 @@ typedef enum DescaleOpt
5152
} DescaleOpt;
5253

5354

55+
typedef struct DescaleCustomKernel
56+
{
57+
double (*f)(double x, void *user_data);
58+
void *user_data;
59+
} DescaleCustomKernel;
60+
61+
5462
// Optional struct members should be initialized to 0 if not used
5563
typedef struct DescaleParams
5664
{
5765
enum DescaleMode mode;
58-
int taps; // required if mode is LANCZOS
66+
int taps; // required if mode is LANCZOS or CUSTOM
5967
double param1; // required if mode is BICUBIC
6068
double param2; // required if mode is BICUBIC
6169
double shift; // optional
6270
double active_dim; // always required; usually equal to dst_dim
71+
DescaleCustomKernel custom_kernel; // required if mode is CUSTOM
6372
} DescaleParams;
6473

6574

meson.build

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
project('Descale', 'c',
22
default_options: ['buildtype=release', 'b_ndebug=if-release', 'c_std=c99'],
33
meson_version: '>=0.51.0',
4-
version: '7'
4+
version: '8'
55
)
66

77
add_global_arguments(['-D_XOPEN_SOURCE=700'], language: 'c')
@@ -47,12 +47,19 @@ if host_machine.system() == 'windows'
4747
else
4848
m_dep = cc.find_library('m', required: false)
4949
p_dep = cc.find_library('pthread')
50-
vs = dependency('vapoursynth').partial_dependency(compile_args: true, includes: true)
51-
deps += [m_dep, p_dep, vs]
50+
deps += [m_dep, p_dep]
51+
if libtype in ['vapoursynth', 'both']
52+
vs = dependency('vapoursynth').partial_dependency(compile_args: true, includes: true)
53+
deps += [vs]
54+
endif
55+
if libtype in ['avisynth', 'both']
56+
avs = dependency('avisynth')
57+
deps += [avs]
58+
endif
5259
if libtype in ['vapoursynth', 'both']
5360
installdir = join_paths(vs.get_pkgconfig_variable('libdir'), 'vapoursynth')
5461
else
55-
installdir = join_paths(vs.get_pkgconfig_variable('libdir'), 'avisynth')
62+
installdir = join_paths(avs.get_pkgconfig_variable('libdir'), 'avisynth')
5663
endif
5764
endif
5865

src/avsplugin.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,7 @@ static AVS_Value AVSC_CC avs_descale_create(AVS_ScriptEnvironment *env, AVS_Valu
245245
b = avs_defined(v) ? avs_as_float(v) : 0.0;
246246
v = avs_array_elt(args, idx++);
247247
c = avs_defined(v) ? avs_as_float(v) : 0.5;
248-
}
249-
else {
248+
} else {
250249
b = 0.0;
251250
c = 0.5;
252251
}
@@ -263,6 +262,11 @@ static AVS_Value AVSC_CC avs_descale_create(AVS_ScriptEnvironment *env, AVS_Valu
263262
bool process_h = dst_width != src_width || shift_h != 0.0 || active_width != (double)dst_width;
264263
bool process_v = dst_height != src_height || shift_v != 0.0 || active_height != (double)dst_height;
265264

265+
if (!process_h && !process_v) {
266+
v = avs_new_value_clip(clip);
267+
goto done;
268+
}
269+
266270
v = avs_array_elt(args, idx++);
267271
int opt = avs_defined(v) ? avs_as_int(v) : 0;
268272
enum DescaleOpt opt_enum;
@@ -277,7 +281,7 @@ static AVS_Value AVSC_CC avs_descale_create(AVS_ScriptEnvironment *env, AVS_Valu
277281
if (bytes_per_pixel != 4) {
278282
AVS_Value c2, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11;
279283
c1 = avs_new_value_clip(clip);
280-
//avs_release_clip(clip); // This is apparently wrong, leaving it uncommented crashes sometimes
284+
avs_release_clip(clip);
281285
a1 = avs_new_value_int(32);
282286
AVS_Value convert_args[] = {c1, a1};
283287
c2 = avs_invoke(env, "ConvertBits", avs_new_value_array(convert_args, 2), NULL);

src/descale.c

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ static inline double cube(double x)
163163
}
164164

165165

166-
static double calculate_weight(enum DescaleMode mode, int support, double distance, double b, double c)
166+
static double calculate_weight(enum DescaleMode mode, int support, double distance, double b, double c, struct DescaleCustomKernel *ck)
167167
{
168168
distance = fabs(distance);
169169

@@ -221,6 +221,8 @@ static double calculate_weight(enum DescaleMode mode, int support, double distan
221221
} else {
222222
return 0.0;
223223
}
224+
} else if (mode == DESCALE_MODE_CUSTOM) {
225+
return ck->f(distance, ck->user_data);
224226
}
225227

226228
return 0.0;
@@ -242,7 +244,7 @@ static double round_halfup(double x)
242244

243245
// Most of this is taken from zimg
244246
// https://github.com/sekrit-twc/zimg/blob/ce27c27f2147fbb28e417fbf19a95d3cf5d68f4f/src/zimg/resize/filter.cpp#L227
245-
static void scaling_weights(enum DescaleMode mode, int support, int src_dim, int dst_dim, double param1, double param2, double shift, double active_dim, double **weights)
247+
static void scaling_weights(enum DescaleMode mode, int support, int src_dim, int dst_dim, double param1, double param2, double shift, double active_dim, struct DescaleCustomKernel *ck, double **weights)
246248
{
247249
*weights = calloc(src_dim * dst_dim, sizeof (double));
248250
double ratio = (double)dst_dim / active_dim;
@@ -254,7 +256,7 @@ static void scaling_weights(enum DescaleMode mode, int support, int src_dim, int
254256
double begin_pos = round_halfup(pos - support) + 0.5;
255257
for (int j = 0; j < 2 * support; j++) {
256258
double xpos = begin_pos + j;
257-
total += calculate_weight(mode, support, xpos - pos, param1, param2);
259+
total += calculate_weight(mode, support, xpos - pos, param1, param2, ck);
258260
}
259261
for (int j = 0; j < 2 * support; j++) {
260262
double xpos = begin_pos + j;
@@ -269,7 +271,7 @@ static void scaling_weights(enum DescaleMode mode, int support, int src_dim, int
269271
real_pos = xpos;
270272

271273
int idx = (int)floor(real_pos);
272-
(*weights)[i * src_dim + idx] += calculate_weight(mode, support, xpos - pos, param1, param2) / total;
274+
(*weights)[i * src_dim + idx] += calculate_weight(mode, support, xpos - pos, param1, param2, ck) / total;
273275
}
274276
}
275277
}
@@ -561,18 +563,21 @@ static struct DescaleCore *create_core(int src_dim, int dst_dim, struct DescaleP
561563
support = 2;
562564
} else if (params->mode == DESCALE_MODE_LANCZOS) {
563565
support = params->taps;
564-
if (support == 0)
565-
return NULL;
566566
} else if (params->mode == DESCALE_MODE_SPLINE16) {
567567
support = 2;
568568
} else if (params->mode == DESCALE_MODE_SPLINE36) {
569569
support = 3;
570570
} else if (params->mode == DESCALE_MODE_SPLINE64) {
571571
support = 4;
572+
} else if (params->mode == DESCALE_MODE_CUSTOM) {
573+
support = params->taps;
572574
} else {
573575
return NULL;
574576
}
575577

578+
if (support == 0)
579+
return NULL;
580+
576581
core.src_dim = src_dim;
577582
core.dst_dim = dst_dim;
578583
core.bandwidth = support * 4 - 1;
@@ -582,7 +587,7 @@ static struct DescaleCore *create_core(int src_dim, int dst_dim, struct DescaleP
582587
double *multiplied_weights;
583588
double *lower;
584589

585-
scaling_weights(params->mode, support, dst_dim, src_dim, params->param1, params->param2, params->shift, params->active_dim, &weights);
590+
scaling_weights(params->mode, support, dst_dim, src_dim, params->param1, params->param2, params->shift, params->active_dim, &params->custom_kernel, &weights);
586591
transpose_matrix(src_dim, dst_dim, weights, &transposed_weights);
587592

588593
core.weights_left_idx = calloc(ceil_n(dst_dim, 8), sizeof (int));

0 commit comments

Comments
 (0)