├── LICENSE ├── README.md └── insaneAA.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Vladimir Kontserenko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # InsaneAA Anti-Aliasing Script (VS port) 2 | 3 | Original idea by tonik & tophf, edited and ported by DJATOM. 4 | Use this script to fix ugly upscaled anime BDs. 5 | 6 | # Processing chain: 7 | 1) extract luma from clip; 8 | 2) apply Descale to it; 9 | 3) resize luma with Spline36 for smooth edges; 10 | 4) merge "smooth" clip with Descale clip according to descale_strength; 11 | 5) re-upscale it back to 1080p (or clip's original resolution) using eedi3+nnedi3 method; 12 | 6) merge rescaled clip with source clip using lines mask. This should prevent noise and textures distortion; 13 | 7) combine merged clip with color planes. 14 | 15 | # Prerequisites: 16 | - [eedi3/eedi3cl](https://github.com/HomeOfVapourSynthEvolution/VapourSynth-EEDI3). 17 | - [nnedi3](https://github.com/dubhater/vapoursynth-nnedi3). 18 | - [znedi3](https://github.com/sekrit-twc/znedi3). 19 | - [nnedi3cl](https://github.com/HomeOfVapourSynthEvolution/VapourSynth-NNEDI3CL). 20 | - [descale](https://github.com/Irrational-Encoding-Wizardry/vapoursynth-descale). 21 | - [finedehalo](https://gist.github.com/SX91/bcd427ec0fa8fdf7c45433917521bac4). 22 | 23 | # Basic usage: 24 | ```python3 25 | import insaneAA 26 | insaneAA.insaneAA(clip, external_aa=None, external_mask=None, faster_aa=False, eedi3_mode=insaneAA.EEDI3Mode.CPU, eedi3_device=-1, eedi3_opt=0, nnedi3_mode=insaneAA.NNEDI3Mode.NNEDI3, nnedi3_device=-1, nnedi3_opt=0, descale_strength=0.3, kernel='bilinear', bicubic_b=1/3, bicubic_c=1/3, lanczos_taps=3, descale_width=None, descale_height=720, pscrn=1, alpha=0.2, beta=0.25, gamma=1000.0, nrad=2, mdis=20, nsize=0, nns=4, output_mode=insaneAA.ClipMode.FULL, input_mode=insaneAA.ClipMode.FULL) 27 | ``` 28 | - external_aa: if clip is passed, will use it instead of making rescale. 29 | - external_mask: pass external lines mask. Non-clip input will be ignored. 30 | - faster_aa: slightly different upscaling routine, proposed by ZASTIN. Sometimes might produce worse results. But indeed it's faster, yeah. 31 | - eedi3_mode: string with mode or tuple with two strings representing modes for first and second calls of eedi3. 32 | - eedi3_device: integer or tuple with two integers representing device IDs for first and second calls of eedi3. 33 | - eedi3_opt: Controls eedi3 opt related options. You can pass single value or tuple with two values for separated opt on the instances. Passed value should be int type. 34 | - nnedi3_mode: string with mode or tuple with two strings representing modes for first and second calls of nnedi3. 35 | - nnedi3_device: integer or tuple with two integers representing device IDs for first and second calls of nnedi3. 36 | - nnedi3_opt: Controls nnedi3 opt related options. You can pass single value or tuple with two values for separated opt on the instances. znedi3 expects string, classic nnedi3 - int, nnedi3cl ignores passed value. 37 | - descale_strength: strength of mixing between descaled clip and Spline36 clip (for AA purposes). More strengh means more haloes, keep that in mind. 38 | - kernel: descaling kernel. Use getnative.py for determining native resolution and try various kernels to find the best suitable. 39 | - descale_height/descale_width: once you know native resolution, set descale_height. descale_width is almost useless, script will guess descaling width automatically. But you can set it, lol. 40 | - lanczos_taps: lanczos options for Descale. 41 | - pscrn: nnedi3's prescreener for faster operation. 42 | - alpha: eedi3's alpha. 43 | - beta: eedi3's beta. 44 | - gamma: eedi3's gamma. 45 | - nrad: eedi3's nrad. 46 | - mdis: eedi3's mdis. 47 | - output_mode: 1 - only rescale (GRAY), 2 - linemasked rescale (GRAY), 0 - linemasked rescale + untouched colors. This option useful for, say, processing all clip into lossless file and masking high resolution details later or for importing filtered luma into avisynth. 48 | - input_mode: 1 - expect output_mode=1 like GRAY csp upscale. Anything else will skip applying lines mask. 49 | #### Please do something with FullHD captions, credits on OP/ED, etc! At least mask it or somehow exclude from processing. This script makes lines only worse there. 50 | -------------------------------------------------------------------------------- /insaneAA.py: -------------------------------------------------------------------------------- 1 | from vapoursynth import core, VideoNode, GRAY, YUV # pylint: disable=no-name-in-module 2 | import descale 3 | 4 | from functools import partial 5 | from enum import Enum 6 | from typing import Any, Type, Union, Tuple 7 | from finedehalo import fine_dehalo 8 | 9 | __version__ = 0.91 10 | 11 | """ 12 | InsaneAA Anti-Aliasing Script (VS port) v0.91 (21.04.2020) 13 | 14 | Original idea by tonik & tophf, edited and ported by DJATOM. 15 | Use this script to fix ugly upscaled anime BDs. 16 | 17 | Processing chain: 18 | 1) extract luma from clip; 19 | 2) apply Descale to it; 20 | 3) resize luma with Spline36 for smooth edges; 21 | 4) merge "smooth" clip with Descale clip according to descale_strength; 22 | 5) re-upscale it back to 1080p (or clip's original resolution) using eedi3+nnedi3 method; 23 | 6) merge rescaled clip with source clip using lines mask. This should prevent noise and textures distortion; 24 | 7) combine merged clip with color planes. 25 | 26 | Prerequisites: 27 | eedi3/eedi3cl: https://github.com/HomeOfVapourSynthEvolution/VapourSynth-EEDI3 28 | znedi3: https://github.com/sekrit-twc/znedi3/releases 29 | nnedi3: https://github.com/dubhater/vapoursynth-nnedi3 30 | nnedi3cl: https://github.com/HomeOfVapourSynthEvolution/VapourSynth-NNEDI3CL 31 | descale: https://github.com/Irrational-Encoding-Wizardry/vapoursynth-descale 32 | 33 | Basic usage: 34 | import insaneAA 35 | insaneAA.insaneAA(clip, external_aa=None, external_mask=None, faster_aa=False, eedi3_mode=insaneAA.EEDI3Mode.CPU, eedi3_device=-1, eedi3_opt=0, nnedi3_mode=insaneAA.NNEDI3Mode.NNEDI3, nnedi3_device=-1, nnedi3_opt=0, descale_strength=0.3, kernel='bilinear', bicubic_b=1/3, bicubic_c=1/3, lanczos_taps=3, descale_width=None, descale_height=720, pscrn=1, alpha=0.2, beta=0.25, gamma=1000.0, nrad=2, mdis=20, nsize=0, nns=4, output_mode=insaneAA.ClipMode.FULL, input_mode=insaneAA.ClipMode.FULL) 36 | external_aa: if clip is passed, will use it intead of making rescale, otherwise do all processing. 37 | external_mask: pass external lines mask. Must be clip or ignored. 38 | faster_aa: slightly different upscaling routine, proposed by ZASTIN. Notably faster at higher native resolutions, but might produce worse results. 39 | eedi3_mode: enum/int with mode or tuple with two enums/ints representing modes for the first and second calls of eedi3. 40 | eedi3_device: integer or tuple with two integers representing device IDs for the first and second calls of eedi3. 41 | eedi3_opt: Controls eedi3 opt related options. You can pass single value or tuple with two values for separated opt on the instances. Passed value should be int type. 42 | nnedi3_mode: enum/int with mode or tuple with two enums/ints representing modes for the first and second calls of nnedi3. 43 | nnedi3_device: integer or tuple with two integers representing device IDs for the first and second calls of nnedi3. 44 | nnedi3_opt: Controls nnedi3 opt related options. You can pass single value or tuple with two values for separated opt on the instances. znedi3 expects string, classic nnedi3 - int (0 - use opt, 1 - disable, use C functions), means nothing for NNEDI3CL. 45 | descale_strength: strength of mixing between descaled clip and Spline36 clip (AA purposes). More strengh means more haloes with sharp kernel (mostly bilinear), keep that in mind. 46 | kernel: descaling kernel. Use getnative.py for determining native resolution and try various kernels to find the best suitable. 47 | bicubic_b/bicubic_c: bicubic options for Descale. 48 | lanczos_taps: lanczos options for Descale. 49 | descale_height/descale_width: once you know native resolution, set descale_height. 50 | pscrn: nnedi3's prescreener for faster operation. 51 | alpha: eedi3's alpha. 52 | beta: eedi3's beta. 53 | gamma: eedi3's gamma. 54 | nrad: eedi3's nrad. 55 | mdis: eedi3's mdis. 56 | nsize: nnedi3's nsize. 57 | nns: nnedi3's nns. 58 | output_mode: UNMASKED - just rescale (GRAY), MASKED - linemasked rescale (GRAY), FULL - linemasked rescale + source colors. 59 | input_mode: UNMASKED - expects output_mode=1 at external_aa clip. Anything else will skip applying lines mask. 60 | Please do something with FullHD details! At least mask them or somehow exclude from processing. 61 | 62 | Changelog: 63 | version 0.91 64 | Misc: expand version. I don't want to introduce 1.0 yet. 65 | Change: descale mechanism modification. 66 | version 0.9c 67 | Fix: pass lanczos_taps to actual Descale call. 68 | version 0.9b 69 | New: add lanczos_taps option. 70 | version 0.9a 71 | Fix: allow ints on eedi3/nnedi3/clip modes. 72 | version 0.9 73 | Change: huge changes in parameter values. Usage updated. 74 | version 0.8 75 | New: expose nsize/nns options from nnedi3. Sometimes different nsize preset can produce better results and nsize is simply for setting tradeoff between speed and quality (max quality by default). 76 | version 0.7 77 | New: tunable bicubic_b and bicubic_c options for bicubic descaling. 78 | Change: descale_str -> descale_strength. 79 | version 0.6b 80 | Fix: nnedi3 mode used slow C routines by default (behaviour differs from classic avisynth option). 81 | version 0.6 82 | New: parameters eedi3Opt/nnedi3Opt. Controls eedi3/nnedi3 opt related options. 83 | New: expose nrad/mdis parameters from eedi3. It's possible to improve speed with nearly the same results (say, use mdis=12 for 720p rescales). 84 | New: parameter 'externalMask'. Overrides built-in mask clip. 85 | Change: eedi3/nnedi3 mode related configuration. Now you can pass single 'cpu' or 'opencl' for eedi3Mode and single 'nnedi3', 'znedi3' or 'opencl' for nnedi3Mode. 86 | If you need to use non-default device, set eedi3Device and nnedi3Device with proper values. 87 | If you have 2 GPUs or wanna run 1st instance on GPU and second on CPU (or vice versa), just pass tuple with 2 values. 88 | Change: parameter 'ref' is now 'externalAA'. 89 | Change: parameter 'fasterAA' is now False by default. 90 | version 0.5 91 | New: inputMode. If set to 1, line masking on ref will be performed. 92 | version 0.4 93 | New: ref paramenter. You can supply processed clip there, it will be used instead of calculating new rescale. 94 | Speed-ups for AA processing by ZASTIN. 95 | version 0.3 96 | Major change in eedi3/nnedi3 options: use dict(first=dict(mode='cpu', device=-1), second=dict(mode='cpu', device=-1)) for eedi3Mode/nnedi3Mode. More in usage. 97 | Now you can pick znedi3 for sclip. The fastest nnedi3 option on my observation, but in complex scripts it might be better to use opencl nnedi3 for saving cpu cycles for other stuff. 98 | version 0.2 99 | Turn off OpenCL plugins by default. 100 | Split eedi3Cl for every eedi3 call, may improve performance on cheap GPUs. 101 | version 0.1 102 | Initial release. 103 | """ 104 | 105 | """ various modes stuff """ 106 | 107 | 108 | class ClipMode(Enum): 109 | FULL = 0 110 | UNMASKED = 1 111 | MASKED = 2 112 | 113 | 114 | class EEDI3Mode(Enum): 115 | CPU = 0 116 | OPENCL = 1 117 | 118 | 119 | class NNEDI3Mode(Enum): 120 | NNEDI3 = 0 121 | ZNEDI3 = 1 122 | NNEDI3CL = 2 123 | 124 | 125 | def insaneAA(clip: VideoNode, external_aa: VideoNode = None, external_mask: VideoNode = None, faster_aa: bool = False, 126 | eedi3_mode: Union[EEDI3Mode, Tuple[EEDI3Mode, EEDI3Mode]] = EEDI3Mode.CPU, eedi3_device: Union[int, Tuple[int, int]] = -1, eedi3_opt: Union[int, Tuple[int, int]] = 0, 127 | nnedi3_mode: Union[NNEDI3Mode, Tuple[NNEDI3Mode, NNEDI3Mode]] = NNEDI3Mode.NNEDI3, nnedi3_device: Union[int, Tuple[int, int]] = -1, nnedi3_opt: Union[int, str, Tuple[Union[int, str], Union[int, str]]] = 0, 128 | descale_strength: float = 0.3, kernel: str = 'bilinear', bicubic_b: float = 1/3, bicubic_c: float = 1/3, lanczos_taps: int = 3, descale_width: int = None, descale_height: int = 720, pscrn: int = 1, 129 | alpha: float = 0.2, beta: float = 0.25, gamma: float = 1000.0, nrad: int = 2, mdis: float = 20, nsize: int = 0, nns: int = 4, dehalo: bool = False, 130 | output_mode: ClipMode = ClipMode.FULL, input_mode: ClipMode = ClipMode.FULL) -> VideoNode: 131 | if not isinstance(clip, VideoNode): 132 | raise TypeError('insaneAA: this is not a clip.') 133 | width = clip.width 134 | height = clip.height 135 | gray_clip = core.std.ShufflePlanes(clip, 0, GRAY) 136 | if not isinstance(external_aa, VideoNode): 137 | descale_clip = revert_upscale(gray_clip, descale_strength, kernel, 138 | descale_width, descale_height, bicubic_b, bicubic_c, lanczos_taps, dehalo) 139 | upscale = rescale(descale_clip, faster_aa, eedi3_mode, eedi3_device, eedi3_opt, nnedi3_mode, 140 | nnedi3_device, nnedi3_opt, width, height, pscrn, alpha, beta, gamma, nrad, mdis, nsize, nns) 141 | else: 142 | upscale = external_aa 143 | if output_mode in [ClipMode.UNMASKED, 1, "unmasked"]: 144 | return upscale 145 | if not isinstance(external_aa, VideoNode) or input_mode in [ClipMode.UNMASKED, 1, "unmasked"]: 146 | if not isinstance(external_mask, VideoNode): 147 | linemask = core.std.Sobel( 148 | gray_clip).std.Expr("x 2 *").std.Maximum() 149 | else: 150 | linemask = external_mask 151 | aa_clip = core.std.MaskedMerge(gray_clip, upscale, linemask) 152 | else: 153 | aa_clip = external_aa 154 | if output_mode in [ClipMode.MASKED, 2, "masked"] or clip.format.num_planes == 1: 155 | return aa_clip 156 | else: 157 | return core.std.ShufflePlanes([aa_clip, clip], planes=[0, 1, 2], colorfamily=YUV) 158 | 159 | 160 | def revert_upscale(clip: VideoNode, descale_strength: float = 0.3, kernel: str = 'bilinear', descale_width: int = None, descale_height: int = 720, bicubic_b: float = 1/3, bicubic_c: float = 1/3, lanczos_taps: int = 3, dehalo: bool = False) -> VideoNode: 161 | width = clip.width 162 | height = clip.height 163 | descale_width = m4((width * descale_height) / 164 | height) if descale_width == None else descale_width 165 | descale_natural = descale.Descale( 166 | clip, descale_width, descale_height, kernel=kernel, b=bicubic_b, c=bicubic_c, taps=lanczos_taps) 167 | descale_smooth = core.resize.Spline36(clip, descale_width, descale_height) 168 | descale_mix = core.std.Expr([descale_natural, descale_smooth], 169 | 'x {strength} * y 1 {strength} - * +'.format(strength=descale_strength)) 170 | if dehalo: 171 | descale_mix = fine_dehalo(descale_mix, darkstr=0, brightstr=1.5, thmi=45, 172 | thma=128, thlimi=60, thlima=120, showmask=0, useMtEdge=True) 173 | return descale_mix 174 | 175 | 176 | def rescale(clip: VideoNode, faster_aa: bool = False, eedi3_mode: Union[EEDI3Mode, Tuple[EEDI3Mode, EEDI3Mode]] = EEDI3Mode.CPU, eedi3_device: Union[int, Tuple[int, int]] = -1, eedi3_opt: Union[int, Tuple[int, int]] = 0, nnedi3_mode: Union[NNEDI3Mode, Tuple[NNEDI3Mode, NNEDI3Mode]] = NNEDI3Mode.NNEDI3, nnedi3_device: Union[int, Tuple[int, int]] = -1, nnedi3_opt: Union[int, str, Tuple[Union[int, str], Union[int, str]]] = 0, dx: int = None, dy: int = None, pscrn: int = 1, alpha: float = 0.2, beta: float = 0.25, gamma: float = 1000.0, nrad: int = 2, mdis: float = 20, nsize: int = 0, nns: int = 4): 177 | ux = clip.width * 2 178 | uy = clip.height * 2 179 | if dx is None: 180 | raise ValueError('insaneAA: rescale lacks "dx" parameter.') 181 | if dy is None: 182 | raise ValueError('insaneAA: rescale lacks "dy" parameter.') 183 | eedi3_mode_a, eedi3_mode_b = validateInput( 184 | eedi3_mode, (EEDI3Mode, int), 'insaneAA: eedi3_mode should be enum with valid mode or tuple with 2 values providing valid modes.') 185 | nnedi3_mode_a, nnedi3_mode_b = validateInput( 186 | nnedi3_mode, (NNEDI3Mode, int), 'insaneAA: nnedi3_mode should be enum with valid mode or tuple with 2 values providing valid modes.') 187 | eedi3_device_a, eedi3_device_b = validateInput( 188 | eedi3_device, int, 'insaneAA: eedi3_device should be integer with valid device ID or tuple with 2 values providing valid device IDs.') 189 | nnedi3_device_a, nnedi3_device_b = validateInput( 190 | nnedi3_device, int, 'insaneAA: nnedi3_device should be integer with valid device ID or tuple with 2 values providing valid device IDs.') 191 | eedi3_opt_a, eedi3_opt_b = validateInput( 192 | eedi3_opt, int, 'insaneAA: eedi3_opt should be integer with valid eedi3/eedi3cl opt value or tuple with 2 values providing valid values.') 193 | nnedi3_opt_a, nnedi3_opt_b = validateInput(nnedi3_opt, ( 194 | int, str), 'insaneAA: nnedi3_opt should be integer or string with valid eedi3/eedi3cl opt value or tuple with 2 values providing valid values.') 195 | if faster_aa: 196 | clip = core.std.Transpose(clip) 197 | clip = eedi3_instance(clip, eedi3_mode_a, eedi3_device_a, eedi3_opt_a, nnedi3_mode_a, 198 | nnedi3_device_a, nnedi3_opt_a, pscrn, alpha, beta, gamma, nrad, mdis, nsize, nns) 199 | clip = core.resize.Spline36( 200 | clip, height=dx, src_top=-0.5, src_height=ux) 201 | clip = core.std.Transpose(clip) 202 | clip = eedi3_instance(clip, eedi3_mode_b, eedi3_device_b, eedi3_opt_b, nnedi3_mode_b, 203 | nnedi3_device_b, nnedi3_opt_b, pscrn, alpha, beta, gamma, nrad, mdis, nsize, nns) 204 | return core.resize.Spline36(clip, height=dy, src_top=-0.5, src_height=uy) 205 | else: 206 | clip = eedi3_instance(clip, eedi3_mode_a, eedi3_device_a, eedi3_opt_a, nnedi3_mode_a, 207 | nnedi3_device_a, nnedi3_opt_a, pscrn, alpha, beta, gamma, nrad, mdis, nsize, nns) 208 | clip = core.std.Transpose(clip) 209 | clip = eedi3_instance(clip, eedi3_mode_b, eedi3_device_b, eedi3_opt_b, nnedi3_mode_b, 210 | nnedi3_device_b, nnedi3_opt_b, pscrn, alpha, beta, gamma, nrad, mdis, nsize, nns) 211 | clip = core.std.Transpose(clip) 212 | return core.resize.Spline36(clip, dx, dy, src_left=-0.5, src_top=-0.5, src_width=ux, src_height=uy) 213 | 214 | 215 | def eedi3_instance(clip: VideoNode, eedi3_mode: EEDI3Mode = EEDI3Mode.CPU, eedi3_device: int = -1, eedi3_opt: int = 0, nnedi3_mode: NNEDI3Mode = NNEDI3Mode.NNEDI3, nnedi3_device: int = -1, nnedi3_opt: Union[int, str] = 0, pscrn: int = 1, alpha: float = 0.2, beta: float = 0.25, gamma: float = 1000.0, nrad: int = 2, mdis: float = 20, nsize: int = 0, nns: int = 4) -> VideoNode: 216 | if eedi3_mode in [EEDI3Mode.OPENCL, 1, "opencl"]: 217 | return core.eedi3m.EEDI3CL(clip, field=1, dh=True, alpha=alpha, beta=beta, gamma=gamma, vcheck=3, nrad=nrad, mdis=mdis, sclip=nnedi3_superclip(clip, nnedi3_mode, nnedi3_device, nnedi3_opt, pscrn, nsize, nns), device=eedi3_device, opt=eedi3_opt) 218 | elif eedi3_mode in [EEDI3Mode.CPU, 0, "cpu"]: 219 | return core.eedi3m.EEDI3(clip, field=1, dh=True, alpha=alpha, beta=beta, gamma=gamma, vcheck=3, nrad=nrad, mdis=mdis, sclip=nnedi3_superclip(clip, nnedi3_mode, nnedi3_device, nnedi3_opt, pscrn, nsize, nns), opt=eedi3_opt) 220 | else: 221 | raise ValueError(f'insaneAA: invalid eedi3 mode - {eedi3_mode}.') 222 | 223 | 224 | def nnedi3_superclip(clip: VideoNode, nnedi3_mode: NNEDI3Mode = NNEDI3Mode.NNEDI3, nnedi3_device: int = -1, opt: Union[int, str] = 0, pscrn: int = 1, nsize: int = 0, nns: int = 4) -> VideoNode: 225 | if nnedi3_mode in [NNEDI3Mode.NNEDI3CL, 2, "nnedi3cl"]: 226 | return core.nnedi3cl.NNEDI3CL(clip, field=1, dh=True, nsize=nsize, nns=nns, pscrn=pscrn, device=nnedi3_device) 227 | elif nnedi3_mode in [NNEDI3Mode.ZNEDI3, 1, "znedi3"]: 228 | if opt == 0: 229 | _opt = True 230 | _x_cpu = "" 231 | elif opt == 1: 232 | _opt = False 233 | _x_cpu = "" 234 | else: 235 | _opt = True 236 | _x_cpu = str(opt) 237 | return core.znedi3.nnedi3(clip, field=1, dh=True, nsize=nsize, nns=nns, pscrn=pscrn, opt=_opt, x_cpu=_x_cpu) 238 | elif nnedi3_mode in [NNEDI3Mode.NNEDI3, 0, "nnedi3"]: 239 | # swap 0 and 1 for nnedi3 to behave like a classic avisynth: 0 - use best available functions and 1 - use C functions 240 | return core.nnedi3.nnedi3(clip, field=1, dh=True, nsize=nsize, nns=nns, pscrn=pscrn, opt={0: 1, 1: 0}.get(int(opt), 1)) 241 | else: 242 | raise ValueError(f'insaneAA: invalid nnedi3 mode - {nnedi3_mode}.') 243 | 244 | 245 | def validateInput(var: Union[EEDI3Mode, NNEDI3Mode, int, str, tuple], varType: Union[Type[EEDI3Mode], Type[NNEDI3Mode], Type[int], Type[str], Type[tuple], tuple], errorString: str) -> Any: 246 | if isinstance(var, varType): 247 | return var, var 248 | elif isinstance(var, tuple): 249 | if len(var) == 2 and isinstance(var[0], varType) and isinstance(var[1], varType): 250 | return var 251 | else: 252 | raise ValueError(errorString) 253 | else: 254 | raise ValueError(errorString) 255 | 256 | 257 | def m4(x: int) -> int: 258 | return 16 if x < 16 else int(x // 4 + 0.5) * 4 259 | --------------------------------------------------------------------------------