Skip to content

alpha

This module implements functions based on the famous dehalo_alpha.

Classes:

  • AlphaBlur

    A Gaussian blur approximation inspired by Dehalo_Alpha.

Functions:

  • dehalo_alpha

    Reduce halo artifacts by aggressively processing the edges and their surroundings.

IterArr module-attribute

IterArr: TypeAlias = T | list[T] | tuple[T | list[T], ...]

VSFunctionPlanesArgs module-attribute

VSFunctionPlanesArgs: TypeAlias = VSFunctionPlanesArgs[VideoNode, VideoNode]

AlphaBlur

AlphaBlur(
    rx: float | Sequence[float] = 2.0,
    ry: float | Sequence[float] | None = None,
    func: FuncExceptT | None = None,
    **kwargs: Any
)

A Gaussian blur approximation inspired by Dehalo_Alpha.

The blur radius roughly corresponds to a Gaussian sigma as follows
  • Radius 1.5 ≈ sigma 1.0
  • Radius 2.0 ≈ sigma 1.4
  • Radius 3.0 ≈ sigma 2.0
  • Radius 4.0 ≈ sigma 2.75

Initializes an AlphaBlur instance.

Parameters:

  • rx

    (float | Sequence[float], default: 2.0 ) –

    Horizontal radius for halo removal.

  • ry

    (float | Sequence[float] | None, default: None ) –

    Vertical radius for halo removal. Defaults to rx if not set.

  • func

    (FuncExceptT | None, default: None ) –

    An optional function to use for error handling.

  • **kwargs

    (Any, default: {} ) –

    Optional keyword arguments:

    • downscaler: Custom downscaler Scaler object.
    • upscaler: Custom upscaler Scaler object.

Methods:

  • __call__

    Applies the Gaussian blur approximation to the input clip.

Attributes:

Source code in vsdehalo/alpha.py
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def __init__(
    self,
    rx: float | Sequence[float] = 2.0,
    ry: float | Sequence[float] | None = None,
    func: FuncExceptT | None = None,
    **kwargs: Any,
) -> None:
    """
    Initializes an AlphaBlur instance.

    Args:
        rx: Horizontal radius for halo removal.
        ry: Vertical radius for halo removal. Defaults to `rx` if not set.
        func: An optional function to use for error handling.
        **kwargs: Optional keyword arguments:

               - downscaler: Custom downscaler Scaler object.
               - upscaler: Custom upscaler Scaler object.
    """
    self.rx = rx
    self.ry = self.rx if ry is None else ry
    self.func = func or self
    self.downscaler = Scaler.ensure_obj(kwargs.get("downscaler", Mitchell()), self.func)
    self.upscaler = Scaler.ensure_obj(kwargs.get("upscaler", BSpline()), self.func)

downscaler instance-attribute

downscaler = ensure_obj(get('downscaler', Mitchell()), func)

func instance-attribute

func = func or self

rx instance-attribute

rx = rx

ry instance-attribute

ry = rx if ry is None else ry

upscaler instance-attribute

upscaler = ensure_obj(get('upscaler', BSpline()), func)

__call__

__call__(clip: VideoNode, planes: PlanesT = None, **kwargs: Any) -> Any

Applies the Gaussian blur approximation to the input clip.

Parameters:

  • clip

    (VideoNode) –

    Source clip.

  • planes

    (PlanesT, default: None ) –

    Which planes to process. Default to all.

Raises:

  • CustomIndexError

    If any of the radius values (rx or ry) are less than 1.0.

Returns:

  • Any

    Blurred clip.

Source code in vsdehalo/alpha.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
def __call__(self, clip: vs.VideoNode, planes: PlanesT = None, **kwargs: Any) -> Any:
    """
    Applies the Gaussian blur approximation to the input clip.

    Args:
        clip: Source clip.
        planes: Which planes to process. Default to all.

    Raises:
        CustomIndexError: If any of the radius values (`rx` or `ry`) are less than 1.0.

    Returns:
        Blurred clip.
    """
    assert check_variable_format(clip, self.func)

    planes = normalize_planes(clip, planes)

    work_clip, *chroma = split(clip) if planes == [0] else (clip,)

    rxs = normalize_seq(self.rx, work_clip.format.num_planes)
    rys = normalize_seq(self.ry, work_clip.format.num_planes)

    if any(x < 1 for x in (*rxs, *rys)):
        raise CustomIndexError("rx, and ry must all be greater than 1.0!", self.func)

    if (len(set(rxs)) == len(set(rys)) == 1) or planes == [0] or work_clip.format.num_planes == 1:
        processed = self._function(clip, rxs[0], rys[0])

        if not chroma:
            return processed

        return join([processed, *chroma], clip.format.color_family)

    return join([self._function(*values) for values in zip(split(work_clip), rxs, rys)])

dehalo_alpha

dehalo_alpha(
    clip: VideoNode,
    blur: (
        IterArr[float]
        | VSFunctionPlanesArgs
        | tuple[float | list[float] | VSFunctionPlanesArgs, ...]
    ) = GAUSS(sigma=1.4),
    lowsens: IterArr[float] = 50.0,
    highsens: IterArr[float] = 50.0,
    ss: float | tuple[float, ...] = 1.5,
    darkstr: IterArr[float] = 0.0,
    brightstr: IterArr[float] = 1.0,
    planes: PlanesT = 0,
    attach_masks: bool = False,
    func: FuncExceptT | None = None,
    **kwargs: Any
) -> VideoNode

Reduce halo artifacts by aggressively processing the edges and their surroundings.

The parameter ss can be configured per iteration while blur, lowsens, highsens, darkstr and brightstr can be configured per plane and per iteration. You can specify:

- A single value: applies to all iterations and all planes.
- A tuple of values: interpreted as iteration-wise.
- A list inside the tuple: interpreted as per-plane for a specific iteration.
For example

blur=(1.4, [1.4, 1.65], [1.5, 1.4, 1.45]) implies 3 iterations: - 1st: 1.4 for all planes - 2nd: 1.4 for luma, 1.65 for both chroma planes - 3rd: 1.5 for luma, 1.4 for U, 1.45 for V

Parameters:

  • clip

    (VideoNode) –

    Source clip.

  • blur

    (IterArr[float] | VSFunctionPlanesArgs | tuple[float | list[float] | VSFunctionPlanesArgs, ...], default: GAUSS(sigma=1.4) ) –

    Standard deviation of the Gaussian kernel if float or custom blurring function to use in place of the default implementation.

  • lowsens

    (IterArr[float], default: 50.0 ) –

    Lower sensitivity threshold — dehalo is fully applied below this value. Setting both lowsens and highsens to -1 disables mask-based processing entirely.

  • highsens

    (IterArr[float], default: 50.0 ) –

    Upper sensitivity threshold — dehalo is completely skipped above this value. Setting both lowsens and highsens to -1 disables mask-based processing entirely.

  • ss

    (float | tuple[float, ...], default: 1.5 ) –

    Supersampling factor to reduce aliasing artifacts.

  • darkstr

    (IterArr[float], default: 0.0 ) –

    Strength factor for suppressing dark halos.

  • brightstr

    (IterArr[float], default: 1.0 ) –

    Strength factor for suppressing bright halos.

  • planes

    (PlanesT, default: 0 ) –

    Planes to process. Default to 0.

  • attach_masks

    (bool, default: False ) –

    Stores generated masks as frame properties in the output clip. The prop name is DehaloAlphaMask_{i}, where i is the iteration index.

  • func

    (FuncExceptT | None, default: None ) –

    An optional function to use for error handling.

  • **kwargs

    (Any, default: {} ) –

    Additionnal advanced parameters.

Raises:

  • CustomIndexError

    If lowsens or highsens are not beween 0 and 100 (inclusive).

Returns:

  • VideoNode

    Dehaloed clip.

Source code in vsdehalo/alpha.py
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
def dehalo_alpha(
    clip: vs.VideoNode,
    # Blur param
    blur: IterArr[float]
    | VSFunctionPlanesArgs
    | tuple[float | list[float] | VSFunctionPlanesArgs, ...] = Prefilter.GAUSS(sigma=1.4),
    # Mask params
    lowsens: IterArr[float] = 50.0,
    highsens: IterArr[float] = 50.0,
    # Supersampling clamp params
    ss: float | tuple[float, ...] = 1.5,
    # Limiting params
    darkstr: IterArr[float] = 0.0,
    brightstr: IterArr[float] = 1.0,
    # Misc params
    planes: PlanesT = 0,
    attach_masks: bool = False,
    func: FuncExceptT | None = None,
    **kwargs: Any,
) -> vs.VideoNode:
    """
    Reduce halo artifacts by aggressively processing the edges and their surroundings.

    The parameter `ss` can be configured per iteration while `blur`, `lowsens`, `highsens`, `darkstr` and `brightstr`
    can be configured per plane and per iteration. You can specify:

        - A single value: applies to all iterations and all planes.
        - A tuple of values: interpreted as iteration-wise.
        - A list inside the tuple: interpreted as per-plane for a specific iteration.

    For example:
        `blur=(1.4, [1.4, 1.65], [1.5, 1.4, 1.45])` implies 3 iterations:
            - 1st: 1.4 for all planes
            - 2nd: 1.4 for luma, 1.65 for both chroma planes
            - 3rd: 1.5 for luma, 1.4 for U, 1.45 for V

    Args:
        clip: Source clip.
        blur: Standard deviation of the Gaussian kernel if float or custom blurring function
            to use in place of the default implementation.
        lowsens: Lower sensitivity threshold — dehalo is fully applied below this value.
            Setting both `lowsens` and `highsens` to `-1` disables mask-based processing entirely.
        highsens: Upper sensitivity threshold — dehalo is completely skipped above this value.
            Setting both `lowsens` and `highsens` to `-1` disables mask-based processing entirely.
        ss: Supersampling factor to reduce aliasing artifacts.
        darkstr: Strength factor for suppressing dark halos.
        brightstr: Strength factor for suppressing bright halos.
        planes: Planes to process. Default to 0.
        attach_masks: Stores generated masks as frame properties in the output clip.
            The prop name is `DehaloAlphaMask_{i}`, where `i` is the iteration index.
        func: An optional function to use for error handling.
        **kwargs: Additionnal advanced parameters.

    Raises:
        CustomIndexError: If `lowsens` or `highsens` are not beween 0 and 100 (inclusive).

    Returns:
        Dehaloed clip.
    """
    util = FunctionUtil(clip, func or dehalo_alpha, planes)

    assert check_progressive(clip, util.func)

    values = _normalize_iter_arr_t(
        blur,
        lowsens,
        highsens,
        ss,
        darkstr,
        brightstr,
        kwargs.get("supersampler", Lanczos()),
        kwargs.get("supersampler_ref", Mitchell()),
    )
    masks_to_prop = list[ConstantFormatVideoNode]()

    work_clip = util.work_clip

    for (
        blur_i,
        lowsens_i,
        highsens_i,
        (ss_i, *_),
        darkstr_i,
        brightstr_i,
        (sser_i, *_),
        (sser_ref_i, *_),
    ) in values:
        # Applying the blur function
        dehalo = (
            blur_i[0](work_clip, planes=planes)
            if _is_callable(blur_i[0])
            else gauss_blur(work_clip, blur_i, planes=planes)
        )

        # Building the mask
        if all(0 <= x <= 100 for x in (*lowsens_i, *highsens_i)):
            mask = norm_expr(
                [Morpho.gradient(work_clip, planes=planes), Morpho.gradient(dehalo, planes=planes)],
                "x 0 = x y - dup x / ? range_max * {lowsens} - x range_size + range_size 2 * / {highsens} + *",
                planes,
                func=util.func,
                lowsens=(scale_delta(x, 8, clip) for x in lowsens_i),
                highsens=(x / 100 for x in highsens_i),
            )

            if attach_masks:
                masks_to_prop.append(core.std.SetFrameProps(mask, lowsens=lowsens_i, highsens=highsens_i))

            dehalo = core.std.MaskedMerge(dehalo, work_clip, limiter(mask, planes=planes, func=util.func), planes)

        elif lowsens_i.count(-1) == len(lowsens_i) and highsens_i.count(-1) == len(highsens_i):
            pass
        else:
            raise CustomIndexError("lowsens and highsens must be between 0 and 100!", func)

        # Clamping with supersampling clips to reduce aliasing
        if ss_i == 1:
            dehalo = repair.Mode.MINMAX_SQUARE1(work_clip, dehalo, planes)
        else:
            ss_width = mod_x(work_clip.width * ss_i, 2**work_clip.format.subsampling_w)
            ss_height = mod_x(work_clip.height * ss_i, 2**work_clip.format.subsampling_h)

            sser_i = Scaler.ensure_obj(sser_i)
            sser_ref_i = Scaler.ensure_obj(sser_ref_i)

            clip_ss = sser_i.scale(work_clip, ss_width, ss_height)
            inpand = sser_ref_i.scale(Morpho.minimum(dehalo, planes=planes), ss_width, ss_height)
            expand = sser_ref_i.scale(Morpho.maximum(dehalo, planes=planes), ss_width, ss_height)
            dehalo = sser_i.scale(
                MeanMode.MEDIAN(clip_ss, inpand, expand, planes=planes), work_clip.width, work_clip.height
            )

        # Limiting the dehalo clip to control the bright and dark halos
        work_clip = dehalo = norm_expr(
            [work_clip, dehalo],
            "x x y - dup {brightstr} * dup1 {darkstr} * ? -",
            planes,
            func=util.func,
            darkstr=darkstr_i,
            brightstr=brightstr_i,
        )

    out = util.return_clip(dehalo)

    for i, mask in enumerate(masks_to_prop):
        out = out.std.ClipToProp(mask, f"DehaloAlphaMask_{i}")

    return out