Skip to content

Commit

Permalink
Merge remote-tracking branch 'master' into mod
Browse files Browse the repository at this point in the history
  • Loading branch information
sinsanction committed Mar 24, 2024
2 parents 0f2583c + 8c53f5d commit 0e72331
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 51 deletions.
26 changes: 15 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@

Video/Image filter to undo upscaling.

Includes a VapourSynth and Avisynth+ plugin
Includes a VapourSynth and AviSynth+ plugin

## Usage

The VapourSynth plugin itself supports every constant input format. If the format is subsampled, left-aligned chroma planes are always assumed.
The included python wrapper, contrary to using the plugin directly, doesn't descale the chroma planes but scales them normally with `Spline36`.

```
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)
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 border_handling=0, bool force=false, bool force_h=false, bool force_v=false, int opt=0)
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)
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 border_handling=0, bool force=false, bool force_h=false, bool force_v=false, int opt=0)
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)
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 border_handling=0, bool force=false, bool force_h=false, bool force_v=false, int opt=0)
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)
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 border_handling=0, bool force=false, bool force_h=false, bool force_v=false, int opt=0)
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)
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 border_handling=0, bool force=false, bool force_h=false, bool force_v=false, int opt=0)
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)
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 border_handling=0, bool force=false, bool force_h=false, bool force_v=false, int opt=0)
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)
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, int border_handling=0, bool force=false, bool force_h=false, bool force_v=false, int opt=0)
```

The AviSynth+ plugin is used similarly, but without the `descale` namespace.
Expand All @@ -37,9 +37,13 @@ core.descale.Descale(src, w, h, custom_kernel=lambda x: 1.0 - x, taps=1)
# Delanczos
import math
def sinc(x):
return 1.0 if x == 0 else math.sin(x * math.pi) / (x * math.pi)
return 1.0 if x == 0 else math.sin(x * math.pi) / (x * math.pi)
taps = 3
core.descale.Descale(src, w, h, custom_kernel=lambda x: sinc(x) * sinc(x / taps), taps=taps)

# You can also use the python wrapper instead of calling the plugin functions directly
import descale
descale.Decustom(src, w, h, lambda x: 1.0 - x, taps=1)
```

## How does this work?
Expand All @@ -61,8 +65,8 @@ We now have the original vector `x`.

## Compilation

By default only the VapourSynth plugin is compiled
To build the Avisynth+ plugin, add `-Dlibtype=avisynth` or `-Dlibtype=both` to the meson command below.
By default only the VapourSynth plugin is compiled.
To build the AviSynth+ plugin, add `-Dlibtype=avisynth` or `-Dlibtype=both` to the meson command below.

### Linux

Expand Down
50 changes: 32 additions & 18 deletions descale.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,42 @@
from enum import IntEnum
from vapoursynth import core, GRAYS, RGBS, GRAY, YUV, RGB


# If yuv444 is True chroma will be upscaled instead of downscaled
# If gray is True the output will be grayscale
def Debilinear(src, width, height, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='bilinear', taps=None, b=None, c=None, yuv444=yuv444, gray=gray, chromaloc=chromaloc)
class BorderHandling(IntEnum):
MIRROR = 0
ZERO = 1
REPEAT = 2

def Debicubic(src, width, height, b=0.0, c=0.5, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='bicubic', taps=None, b=b, c=c, yuv444=yuv444, gray=gray, chromaloc=chromaloc)
class Opt(IntEnum):
AUTO = 0
NONE = 1
AVX2 = 2

def Delanczos(src, width, height, taps=3, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='lanczos', taps=taps, b=None, c=None, yuv444=yuv444, gray=gray, chromaloc=chromaloc)
# If yuv444 is True chroma will be upscaled instead of downscaled (i.e. output is in 4:4:4 format)
# If gray is True the output will be grayscale (i.e. just the luma plane is processed and returned)
def Debilinear(src, width, height, border_handling=None, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='bilinear', border_handling=border_handling, yuv444=yuv444, gray=gray, chromaloc=chromaloc)

def Despline16(src, width, height, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='spline16', taps=None, b=None, c=None, yuv444=yuv444, gray=gray, chromaloc=chromaloc)
def Debicubic(src, width, height, b=0.0, c=0.5, border_handling=None, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='bicubic', b=b, c=c, border_handling=border_handling, yuv444=yuv444, gray=gray, chromaloc=chromaloc)

def Despline36(src, width, height, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='spline36', taps=None, b=None, c=None, yuv444=yuv444, gray=gray, chromaloc=chromaloc)
def Delanczos(src, width, height, taps=3, border_handling=None, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='lanczos', taps=taps, border_handling=border_handling, yuv444=yuv444, gray=gray, chromaloc=chromaloc)

def Despline64(src, width, height, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='spline64', taps=None, b=None, c=None, yuv444=yuv444, gray=gray, chromaloc=chromaloc)
def Despline16(src, width, height, border_handling=None, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='spline16', border_handling=border_handling, yuv444=yuv444, gray=gray, chromaloc=chromaloc)

def Despline36(src, width, height, border_handling=None, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='spline36', border_handling=border_handling, yuv444=yuv444, gray=gray, chromaloc=chromaloc)

def Descale(src, width, height, kernel=None, custom_kernel=None, taps=None, b=None, c=None, yuv444=False, gray=False, chromaloc=None):
def Despline64(src, width, height, border_handling=None, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, kernel='spline64', border_handling=border_handling, yuv444=yuv444, gray=gray, chromaloc=chromaloc)

def Decustom(src, width, height, custom_kernel, taps, border_handling=None, yuv444=False, gray=False, chromaloc=None):
return Descale(src, width, height, custom_kernel=custom_kernel, taps=taps, border_handling=border_handling, yuv444=yuv444, gray=gray, chromaloc=chromaloc)


def Descale(src, width, height, kernel=None, custom_kernel=None, taps=None, b=None, c=None, border_handling=None, yuv444=False, gray=False, chromaloc=None):
src_f = src.format
src_cf = src_f.color_family
src_st = src_f.sample_type
Expand All @@ -31,10 +45,10 @@ def Descale(src, width, height, kernel=None, custom_kernel=None, taps=None, b=No
src_sh = src_f.subsampling_h

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

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

Expand All @@ -44,7 +58,7 @@ def Descale(src, width, height, kernel=None, custom_kernel=None, taps=None, b=No
if not yuv444 and ((width % 2 and src_sw) or (height % 2 and src_sh)):
raise ValueError('Descale: The output dimension and the subsampling are incompatible.')

uv_f = core.register_format(src_cf, src_st, src_bits, 0 if yuv444 else src_sw, 0 if yuv444 else src_sh)
uv_f = core.query_video_format(src_cf, src_st, src_bits, 0 if yuv444 else src_sw, 0 if yuv444 else src_sh)
uv = src.resize.Spline36(width, height, format=uv_f.id, chromaloc_s=chromaloc)

return core.std.ShufflePlanes([y,uv], [0,1,2], YUV)
Expand Down
13 changes: 11 additions & 2 deletions include/descale.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ typedef enum DescaleDir
} DescaleDir;


typedef enum DescaleBorder
{
DESCALE_BORDER_MIRROR = 0,
DESCALE_BORDER_ZERO = 1,
DESCALE_BORDER_REPEAT = 2
} DescaleBorder;


typedef enum DescaleOpt
{
DESCALE_OPT_AUTO = 0,
Expand All @@ -63,7 +71,7 @@ typedef struct DescaleCustomKernel
} DescaleCustomKernel;


// Optional struct members should be initialized to 0 if not used
// Optional struct members must be initialized to 0 if not used
typedef struct DescaleParams
{
enum DescaleMode mode;
Expand All @@ -72,7 +80,8 @@ typedef struct DescaleParams
double param2; // required if mode is BICUBIC
double shift; // optional
double active_dim; // always required; usually equal to dst_dim
DescaleCustomKernel custom_kernel; // required if mode is CUSTOM
enum DescaleBorder border_handling; // optional
struct DescaleCustomKernel custom_kernel; // required if mode is CUSTOM
} DescaleParams;


Expand Down
28 changes: 23 additions & 5 deletions src/avsplugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,16 @@ static AVS_Value AVSC_CC avs_descale_create(AVS_ScriptEnvironment *env, AVS_Valu
goto done;
}

v = avs_array_elt(args, idx++);
int border_handling = avs_defined(v) ? avs_as_int(v) : 0;
enum DescaleBorder border_handling_enum;
if (border_handling == 1)
border_handling_enum = DESCALE_BORDER_ZERO;
if (border_handling == 2)
border_handling_enum = DESCALE_BORDER_REPEAT;
else
border_handling_enum = DESCALE_BORDER_MIRROR;

v = avs_array_elt(args, idx++);
int opt = avs_defined(v) ? avs_as_int(v) : 0;
enum DescaleOpt opt_enum;
Expand All @@ -279,7 +289,7 @@ static AVS_Value AVSC_CC avs_descale_create(AVS_ScriptEnvironment *env, AVS_Valu

int bits_per_pixel = avs_bits_per_component(vi);
if (bits_per_pixel != 32) {
AVS_Value c2, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11;
AVS_Value c2, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12;
c1 = avs_new_value_clip(clip);
avs_release_clip(clip);
a1 = avs_new_value_int(32);
Expand All @@ -296,9 +306,10 @@ static AVS_Value AVSC_CC avs_descale_create(AVS_ScriptEnvironment *env, AVS_Valu
a8 = avs_new_value_float(shift_v);
a9 = avs_new_value_float(active_width);
a10 = avs_new_value_float(active_height);
a11 = avs_new_value_int(opt);
AVS_Value descale_args[] = {c2, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11};
c1 = avs_invoke(env, "Descale", avs_new_value_array(descale_args, 12), NULL);
a11 = avs_new_value_int(border_handling);
a12 = avs_new_value_int(opt);
AVS_Value descale_args[] = {c2, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12};
c1 = avs_invoke(env, "Descale", avs_new_value_array(descale_args, 13), NULL);
avs_release_value(c2);
a1 = avs_new_value_int(bits_per_pixel);
AVS_Value convert_args2[] = {c1, a1};
Expand All @@ -307,7 +318,7 @@ static AVS_Value AVSC_CC avs_descale_create(AVS_ScriptEnvironment *env, AVS_Valu
return c2;
}

struct DescaleParams params = {mode, taps, b, c, 0, 0};
struct DescaleParams params = {mode, taps, b, c, 0, 0, border_handling_enum};
struct DescaleData dd = {
src_width, src_height,
dst_width, dst_height,
Expand Down Expand Up @@ -359,6 +370,7 @@ const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment *env)
"[src_top]f"
"[src_width]f"
"[src_height]f"
"[border_handling]i"
"[opt]i",
avs_descale_create,
(void *)(DESCALE_MODE_BILINEAR)
Expand All @@ -376,6 +388,7 @@ const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment *env)
"[src_top]f"
"[src_width]f"
"[src_height]f"
"[border_handling]i"
"[opt]i",
avs_descale_create,
(void *)(DESCALE_MODE_BICUBIC)
Expand All @@ -392,6 +405,7 @@ const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment *env)
"[src_top]f"
"[src_width]f"
"[src_height]f"
"[border_handling]i"
"[opt]i",
avs_descale_create,
(void *)(DESCALE_MODE_LANCZOS)
Expand All @@ -407,6 +421,7 @@ const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment *env)
"[src_top]f"
"[src_width]f"
"[src_height]f"
"[border_handling]i"
"[opt]i",
avs_descale_create,
(void *)(DESCALE_MODE_SPLINE16)
Expand All @@ -422,6 +437,7 @@ const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment *env)
"[src_top]f"
"[src_width]f"
"[src_height]f"
"[border_handling]i"
"[opt]i",
avs_descale_create,
(void *)(DESCALE_MODE_SPLINE36)
Expand All @@ -437,6 +453,7 @@ const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment *env)
"[src_top]f"
"[src_width]f"
"[src_height]f"
"[border_handling]i"
"[opt]i",
avs_descale_create,
(void *)(DESCALE_MODE_SPLINE64)
Expand All @@ -456,6 +473,7 @@ const char * AVSC_CC avisynth_c_plugin_init(AVS_ScriptEnvironment *env)
"[src_top]f"
"[src_width]f"
"[src_height]f"
"[border_handling]i"
"[opt]i",
avs_descale_create,
NULL
Expand Down
37 changes: 22 additions & 15 deletions src/descale.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ static double round_halfup(double x)

// Most of this is taken from zimg
// https://github.com/sekrit-twc/zimg/blob/ce27c27f2147fbb28e417fbf19a95d3cf5d68f4f/src/zimg/resize/filter.cpp#L227
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)
static void scaling_weights(enum DescaleMode mode, int support, int src_dim, int dst_dim, double param1, double param2, double shift, double active_dim, enum DescaleBorder border_handling, struct DescaleCustomKernel *ck, double **weights)
{
*weights = calloc(src_dim * dst_dim, sizeof (double));
double ratio = (double)dst_dim / active_dim;
Expand All @@ -260,15 +260,23 @@ static void scaling_weights(enum DescaleMode mode, int support, int src_dim, int
}
for (int j = 0; j < 2 * support; j++) {
double xpos = begin_pos + j;
double real_pos;

// Mirror the position if it goes beyond image bounds.
if (xpos < 0.0)
real_pos = -xpos;
else if (xpos >= src_dim)
real_pos = DSMIN(2.0 * src_dim - xpos, src_dim - 0.5);
else
real_pos = xpos;
double real_pos = xpos;

if (xpos < 0.0 || xpos > src_dim) {
if (border_handling == DESCALE_BORDER_ZERO) {
continue;
} else if (border_handling == DESCALE_BORDER_REPEAT) {
if (xpos < 0.0)
real_pos = 0.0;
else if (xpos >= src_dim)
real_pos = src_dim - 0.5;
} else { // Mirror
if (xpos < 0.0)
real_pos = -xpos;
else if (xpos >= src_dim)
real_pos = DSMIN(2.0 * src_dim - xpos, src_dim - 0.5);
}
}

int idx = (int)floor(real_pos);
(*weights)[i * src_dim + idx] += calculate_weight(mode, support, xpos - pos, param1, param2, ck) / total;
Expand Down Expand Up @@ -345,11 +353,10 @@ static void process_plane_h_b7_c(int width, int current_width, int current_heigh
sum += upper[0][j] * dstp[j + 1];
sum += upper[1][j] * dstp[j + 2];
sum += upper[2][j] * dstp[j + 3];
}
else if (j < width - 2) {
} else if (j < width - 2) {
sum += upper[0][j] * dstp[j + 1];
sum += upper[1][j] * dstp[j + 2];}
else if (j < width - 1) {
sum += upper[1][j] * dstp[j + 2];
} else if (j < width - 1) {
sum += upper[0][j] * dstp[j + 1];
}

Expand Down Expand Up @@ -587,7 +594,7 @@ static struct DescaleCore *create_core(int src_dim, int dst_dim, struct DescaleP
double *multiplied_weights;
double *lower;

scaling_weights(params->mode, support, dst_dim, src_dim, params->param1, params->param2, params->shift, params->active_dim, &params->custom_kernel, &weights);
scaling_weights(params->mode, support, dst_dim, src_dim, params->param1, params->param2, params->shift, params->active_dim, params->border_handling, &params->custom_kernel, &weights);
transpose_matrix(src_dim, dst_dim, weights, &transposed_weights);

core.weights_left_idx = calloc(ceil_n(dst_dim, 8), sizeof (int));
Expand Down
Loading

0 comments on commit 0e72331

Please sign in to comment.